GIT13: the forge module keeps no trusted callers; who may call a verb is the seat definition's

The config value callers, the package seats' list of callers with its watch, and the seat git's caller judgement are gone:
the grants part grants by each definition's callers and the bus refuses the rest.

Builds: design 47 G §68, §127, §313, §321

Claude-Session: https://claude.ai/code/session_01JG73KEq4zoDfM9WtYpcQgZ
This commit is contained in:
2026-11-11 11:43:18 +01:00
parent 5daf3a333a
commit 312d824e4f
9 changed files with 17 additions and 228 deletions
+1 -1
View File
@@ -197,7 +197,7 @@ the module's own object store `incoming`, its transfers in the state `transfers`
side), `versions` and `remove` (a tagged version, by the builder). Before anything is uploaded the holder asks the
builder whether it numbered the version with that digest; a package goes up as npm's publish sends it, a Go module's
zip to the Go upload. Every outcome is said on the seat's events (`published`, `refused`, `removed`), a Go package's
with its module path. Who may call each verb is the trusted config value `callers` (empty: design 51's list).
with its module path. Who may call each verb is the seat definition's `callers`, which the grants part grants by; the holder keeps no list.
## The package registry's retention (novox/hq ADR 0251 §4, to-be 51)
@@ -488,7 +488,7 @@ func TestToolsAreTheOnesTheReadmeNamesAndSayWhatTheyDo(t *testing.T) {
t.Errorf("transfers has options (a lifetime): %s", req)
}
// The retention asks the builder's versions; the forge's other bundles ask the catalog half and record a re-read.
if strings.Join(m.Invokes, ",") != "seat:mesh-catalog.revision,seat:mesh-hand-acts.hand-act,mesh-delivery-builder.builder_versions" {
if strings.Join(m.Invokes, ",") != "seat:mesh-catalog.revision,seat:mesh-catalog.registrations,seat:mesh-hand-acts.hand-act,mesh-delivery-builder.builder_versions" {
t.Errorf("the manifest invokes %v", m.Invokes)
}
if len(m.Tools) != 0 {
+1 -5
View File
@@ -276,11 +276,7 @@ var boundCap = parts.BusCap(packageseats.IncomingObjects)
// busHolder is the holder over the runtime's bus state, the builder's numbering and the forge.
func busHolder(forge func() (*Forge, error)) (*packageseats.Holder, error) {
callers, err := packageseats.ConfiguredCallers()
if err != nil {
return nil, err
}
return &packageseats.Holder{Callers: callers, Objects: parts.BusObjects(packageseats.IncomingObjects),
return &packageseats.Holder{Objects: parts.BusObjects(packageseats.IncomingObjects),
Transfers: parts.BusTransfers[packageseats.Opening](packageseats.TransfersState),
Builder: builds.BuilderVersions{Ask: stdio.Ask}, Store: seatStore{forge}, Say: say, Finding: finding,
Cap: boundCap, Record: recordAct, Idle: parts.BusArm(packageseats.IdleState)}, nil
@@ -6,7 +6,7 @@ package main
// the built package-registry bundle, played over its real stdin and stdout as the node's tool runtime plays it, its
// `incoming` an object store and its `transfers` and `findings` key-value buckets of a nats-server of this test alone
// (so the publish in parts runs through mesh-sdk's parts.BusObjects and BusTransfers, ruling 4 of the lead's 14:0x), the
// builder's builder_versions answered on mesh/ask, the config value `callers` watched on mesh/provision.watch, and the forge an
// builder's builder_versions answered on mesh/ask, and the forge an
// httptest server that takes reads only with the tools' token and writes only with the packages account's.
//
// A Go package the builder numbered goes up in two chunks, is uploaded whole as the packages account and says
@@ -113,7 +113,6 @@ func (p *player) handle(m wire) {
Data []byte `json:"data"`
Subject string `json:"subject"`
Body json.RawMessage `json:"body"`
Kind string `json:"kind"`
TTL int `json:"ttl_seconds"`
}
_ = json.Unmarshal(m.Params, &a)
@@ -123,12 +122,6 @@ func (p *player) handle(m wire) {
_ = json.Unmarshal(a.Body, &q)
d, ok := p.numbered[q.Name+"@"+q.Version]
p.answer(m.ID, map[string]any{"recorded": ok && d == q.Digest, "why": "not numbered with that digest"}, nil)
case m.Method == "mesh/provision.watch" && a.Kind == "config":
// The watch of `callers`, as the runtime answers it: the value now as one change (mesh/provision,
// named by the watch's id) handed over before the watch is answered, here unset, so design 51's list holds.
p.send(map[string]any{"id": "change-" + watchID(m.ID), "method": "mesh/provision", "params": map[string]any{
"watch": watchID(m.ID), "change": map[string]any{"kind": "config", "key": a.Key}}})
p.answer(m.ID, map[string]any{}, nil)
case m.Method == "mesh/seat.publish":
p.answer(m.ID, map[string]any{}, p.nc.Publish(a.Subject, a.Body))
case m.Method == "mesh/publish":
@@ -142,15 +135,6 @@ func (p *player) handle(m wire) {
}
}
// watchID is the watch's id as a string, the form the change names it by.
func watchID(raw json.RawMessage) string {
var id string
if json.Unmarshal(raw, &id) != nil {
return string(raw)
}
return id
}
func (p *player) objects(m wire, local, name string, data []byte) {
os, err := p.js.ObjectStore(p.ctx, "gitea_"+local)
if err != nil {
+2 -115
View File
@@ -2,21 +2,18 @@
// `go-package-registry` (novox/hq design 51 *The four registry seats*, ADR 0605, ADR 0611). A build module publishes
// a package through `publish` and `publish-part`: the package's one file lands in the forge module's own object store
// `incoming`, is hashed, checked against the builder's numbering and then uploaded into the forge's package registry
// with the module's own write side. The receiving side is mesh-sdk's seat-neutral parts.Receiver and the caller list
// mesh-sdk's VerbCallers (ADR 0627 §2): this package binds both to the two seats and to the forge.
// with the module's own write side. The receiving side is mesh-sdk's seat-neutral parts.Receiver: this package binds it to the two seats and to the
// forge. Who may call a verb is the seat definition's `callers`, which the grants part grants by (design 47 §68, §321).
package packageseats
import (
"encoding/json"
"errors"
"fmt"
"strings"
"sync"
"time"
"git.novox.be/novox/mesh-contracts/health-and-repair/go/clients/meshhandactsclient"
"git.novox.be/novox/mesh-delivery/sdk/parts"
stdio "git.novox.be/novox/mesh-sdk/go"
)
// The seats the forge module holds.
@@ -67,103 +64,6 @@ const (
ReasonRetention = "retention"
)
// Callers is the trusted config value `callers`, read as mesh-sdk's list per verb.
type Callers = stdio.VerbCallers
// DefaultCallers is design 51's list for the package seats: publish and publish-part the two build modules that
// publish packages; remove the builder (a tagged version whose branch ended); versions anyone on the mesh, as the
// registry's reads are anonymous.
func DefaultCallers() Callers {
builds := []string{"mesh-build-go", "mesh-build-node"}
return Callers{VerbPublish: builds, VerbPublishPart: builds, VerbRemove: {"mesh-delivery-builder"},
VerbVersions: {stdio.AnyCaller}}
}
// ConfiguredCallers is the config value `callers` as mesh-config resolved it, its lines of the package seats' verbs; unset,
// or with none of them, is DefaultCallers. It is held from a watch of the value and replaced on each change, so a call
// never asks the runtime (issue 1966). The same value carries the lines of the other seat the module holds (git, served
// by its TypeScript side), so each holder takes its own verbs' lines and neither refuses the other's.
func ConfiguredCallers() (Callers, error) { return configured() }
var (
watchMu sync.Mutex
heldMu sync.Mutex
heldList Callers
heldBad error
)
// configured reads the held list; the first call starts the watch, and a change that cannot be read keeps the list last
// read, said and never widened.
func configured() (Callers, error) {
if l, err := read(); l != nil || err != nil {
return l, err
}
watchMu.Lock()
defer watchMu.Unlock()
if l, err := read(); l != nil || err != nil {
return l, err
}
if err := stdio.WatchConfig("callers", func(c stdio.ProvisionChange) error {
heldMu.Lock()
defer heldMu.Unlock()
if c.Refused != "" {
heldBad = fmt.Errorf("the config value callers is not answered: %s", c.Refused)
return nil
}
l, err := OwnCallers(c.Value)
if err != nil {
heldBad = fmt.Errorf("the config value callers cannot be read: %w", err)
return nil
}
heldList, heldBad = l, nil
return nil
}); err != nil {
return nil, fmt.Errorf("the config value callers cannot be read: %w", err)
}
l, err := read()
if l == nil && err == nil {
err = errors.New("the config value callers cannot be read: the runtime handed over no value")
}
return l, err
}
func read() (Callers, error) {
heldMu.Lock()
defer heldMu.Unlock()
if heldList != nil {
return heldList, nil
}
return nil, heldBad
}
// OwnCallers reads the package seats' lines of the config value as the runtime answered it: a JSON object of verb to
// callers, or that object written as a string. A value that cannot be read is an error, never the defaults.
func OwnCallers(raw json.RawMessage) (Callers, error) {
text := strings.TrimSpace(string(raw))
var quoted string
if json.Unmarshal(raw, &quoted) == nil {
text = strings.TrimSpace(quoted)
}
if text == "" || text == "null" {
return DefaultCallers(), nil
}
var all map[string]json.RawMessage
if err := json.Unmarshal([]byte(text), &all); err != nil {
return nil, fmt.Errorf("callers is not a JSON object of verb to callers: %w", err)
}
own := map[string]json.RawMessage{}
for _, verb := range Verbs {
if line, ok := all[verb]; ok {
own[verb] = line
}
}
if len(own) == 0 {
return DefaultCallers(), nil
}
b, _ := json.Marshal(own)
return stdio.ParseVerbCallers(string(b), Verbs, DefaultCallers())
}
// Publish is a publish as the seats' contract names it (mesh-contracts npmpackageregistry and gopackageregistry share
// one shape): what the tools bundle reads from either seat's PublishInput.
type Publish struct {
@@ -227,7 +127,6 @@ type Store interface {
// Holder answers the verbs of both seats.
type Holder struct {
Callers Callers
Objects parts.Objects
Transfers parts.Transfers[Opening]
Builder Builder
@@ -316,9 +215,6 @@ func (h *Holder) answer(transfer string, r parts.Received) Answer {
// Publish is `publish` on seat for the proved caller: a package is one file, its digest the file's sha256.
func (h *Holder) Publish(seat, caller string, p Publish) (Answer, error) {
if err := h.Callers.Allowed(VerbPublish, caller); err != nil {
return Answer{}, err
}
if err := checkPublish(seat, p); err != nil {
return Answer{}, err
}
@@ -349,9 +245,6 @@ func (h *Holder) Publish(seat, caller string, p Publish) (Answer, error) {
// PublishPart is `publish-part`: one chunk, from the caller that opened the transfer alone.
func (h *Holder) PublishPart(seat, caller, transfer, part string, sequence int, last bool, data []byte) (Answer, error) {
if err := h.Callers.Allowed(VerbPublishPart, caller); err != nil {
return Answer{}, err
}
got, err := h.receiver().Take(key(seat, transfer), caller, part, sequence, last, data, func(t Transfer) error {
if t.Shape.Seat != seat {
return fmt.Errorf("transfer %s is %s's, not %s's", transfer, t.Shape.Seat, seat)
@@ -366,9 +259,6 @@ func (h *Holder) PublishPart(seat, caller, transfer, part string, sequence int,
// Versions is `versions`: every version the seat holds of name, newest first.
func (h *Holder) Versions(seat, caller, name string) ([]Version, error) {
if err := h.Callers.Allowed(VerbVersions, caller); err != nil {
return nil, err
}
if _, ok := Seats[seat]; !ok {
return nil, fmt.Errorf("versions: the forge holds no seat %s", seat)
}
@@ -385,9 +275,6 @@ func (h *Holder) Versions(seat, caller, name string) ([]Version, error) {
// Remove is `remove`: the builder removes a tagged version (its branch ended); an untagged version goes only by the
// forge's own retention, never by a call. A version already gone is said removed all the same, so a retry ends.
func (h *Holder) Remove(seat, caller, name, version, reason string) (Outcome, error) {
if err := h.Callers.Allowed(VerbRemove, caller); err != nil {
return Outcome{}, err
}
if _, ok := Seats[seat]; !ok {
return Outcome{}, fmt.Errorf("remove: the forge holds no seat %s", seat)
}
@@ -10,7 +10,6 @@ import (
"git.novox.be/novox/mesh-contracts/health-and-repair/go/clients/meshhandactsclient"
"git.novox.be/novox/mesh-delivery/sdk/parts"
stdio "git.novox.be/novox/mesh-sdk/go"
)
type memObjects map[string][]byte
@@ -102,7 +101,7 @@ type bed struct {
func newBed(numbered bool) *bed {
b := &bed{forge: &forge{files: map[string]map[string][]byte{}}, findings: map[string]bool{}}
b.h = &Holder{Callers: DefaultCallers(), Objects: memObjects{}, Transfers: memTransfers{}, Builder: builder{ok: numbered},
b.h = &Holder{Objects: memObjects{}, Transfers: memTransfers{}, Builder: builder{ok: numbered},
Store: b.forge, Now: func() time.Time { return time.Unix(1e9, 0) },
Say: func(seat, event string, o Outcome) error { b.said = append(b.said, said{seat, event, o}); return nil },
Finding: func(kind, subject, _ string, raised bool) { b.findings[kind+" "+subject] = raised }}
@@ -212,14 +211,6 @@ func TestTheForgeHoldsBothPackageSeats(t *testing.T) {
do func(b *bed) error
want string
}{
{"a caller not on publish's list", func(b *bed) error {
_, err := b.h.Publish(NpmSeat, "ace.claude-code", pub("x", "0.1.0", file))
return err
}, "not on the holder's caller list"},
{"an unproven caller", func(b *bed) error {
_, err := b.h.Publish(NpmSeat, stdio.Unproven, pub("x", "0.1.0", file))
return err
}, "no caller the tool runner proved"},
{"a seat the forge does not hold", func(b *bed) error {
_, err := b.h.Publish("cargo-package-registry", goBuild, pub("x", "0.1.0", file))
return err
@@ -244,7 +235,6 @@ func TestTheForgeHoldsBothPackageSeats(t *testing.T) {
_, err := b.h.PublishPart(GoSeat, nodeBuild, "t-x", digest(file), 0, true, file)
return err
}, "not open"},
{"remove by a build module", func(b *bed) error { _, err := b.h.Remove(GoSeat, goBuild, "x", "0.1.1-feat.1", "tag-gone"); return err }, "not on the holder's caller list"},
{"remove of a version with no tag", func(b *bed) error {
_, err := b.h.Remove(GoSeat, "novox.mesh-delivery-builder", "x", "0.1.1", "tag-gone")
return err
@@ -304,29 +294,4 @@ func TestTheForgeHoldsBothPackageSeats(t *testing.T) {
}
})
t.Run("the callers value: its own verbs' lines, the other seat's left to it, unset or none of its own the defaults", func(t *testing.T) {
manifest := `"{\"publish\":[\"mesh-build-go\"],\"remove\":[\"mesh-delivery-builder\"],\"pull\":[\"mesh-delivery-gate\"]}"`
for _, row := range []struct {
raw string
publish []string
fails bool
}{
{raw: manifest, publish: []string{"mesh-build-go"}},
{raw: `{"pull":["mesh-delivery-gate"]}`, publish: DefaultCallers()[VerbPublish]},
{raw: `""`, publish: DefaultCallers()[VerbPublish]},
{raw: `{"publish":"mesh-build-go"}`, fails: true},
{raw: `"not json"`, fails: true},
} {
got, err := OwnCallers([]byte(row.raw))
if row.fails != (err != nil) {
t.Fatalf("%s: err %v", row.raw, err)
}
if !row.fails && strings.Join(got[VerbPublish], ",") != strings.Join(row.publish, ",") {
t.Errorf("%s: publish %v, want %v", row.raw, got[VerbPublish], row.publish)
}
if _, theirs := got["pull"]; theirs {
t.Errorf("%s: the seat git's line is not the package seats'", row.raw)
}
}
})
}
-7
View File
@@ -248,13 +248,6 @@
"default": [
"jschoubben"
]
},
"callers": {
"type": "string",
"trusted": true,
"runtime": true,
"default": "{\"publish\":[\"mesh-build-go\",\"mesh-build-node\"],\"publish-part\":[\"mesh-build-go\",\"mesh-build-node\"],\"remove\":[\"mesh-delivery-builder\"],\"versions\":[\"*\"],\"pull\":[\"mesh-delivery-forge-bridge\",\"mesh-delivery-gate\"],\"branch\":[\"mesh-delivery-forge-bridge\",\"mesh-delivery-indexer\"],\"open-pulls\":[\"mesh-delivery-forge-bridge\",\"mesh-delivery-indexer\",\"mesh-delivery-gate\"],\"tags\":[\"mesh-delivery-forge-bridge\"],\"trees\":[\"mesh-delivery-forge-bridge\",\"mesh-delivery-checker\",\"mesh-delivery-indexer\"],\"file\":[\"mesh-delivery-forge-bridge\",\"mesh-delivery-checker\",\"mesh-delivery-indexer\"],\"contains\":[\"mesh-delivery-indexer\",\"mesh-delivery-gate\"],\"statuses\":[\"mesh-delivery-gate\"],\"status\":[\"mesh-delivery-forge-bridge\"],\"comment\":[\"mesh-delivery-forge-bridge\"],\"view\":[\"mesh-delivery-forge-bridge\"],\"note\":[\"mesh-delivery-forge-bridge\"],\"merge-pull\":[\"mesh-delivery-forge-bridge\"],\"tag\":[\"mesh-delivery-builder\"]}",
"description": "per verb of the seats the module holds, the callers that may call it (JSON object of verb to callers; * every proved caller, operator the operator's session); each holder reads the lines of its own verbs. The package seats' lines are design 51's (empty is that list for them); the seat git's are design 47's: its reads the parts that declare them in invokes, its acts (status, comment, view, note, merge-pull) the forge bridge alone, and a verb of git with no line is called by no one"
}
},
"facts": {
+4 -32
View File
@@ -2,10 +2,8 @@
// delivery part makes of the forge is a verb of the seat, served here as a tool `git.<verb>` against the seat's generated
// contract, and no part names this module's own tools.
//
// **Who may call which verb** is the holder's trusted config value `callers` (ADR 0627 §2), the one value the module's
// package seats read too: each holder takes the lines of the verbs it serves, so neither refuses the other's. Unset, no
// one may call a verb of `git`: the list is the manifest's, never a list in this code. The caller is the one the tool
// runner proved in the call's `_meta` (mesh-sdk `callers.callerOf`), never an argument.
// **Who may call which verb** is the seat definition's `callers`, which the grants part grants by: the bus refuses a caller a
// definition does not name (design 47 §68, §313, §321), so this holder judges no caller and keeps no list of its own.
//
// **A failed read answers an error, never an empty answer**: the forge's 404 is the kind `not-found`, its refusal (another
// 4xx, or a refusal of this holder) the kind `refused`, a forge that failed or did not answer the kind `failed`; an empty list or an absent file is answered
@@ -16,8 +14,6 @@
// (design 47 §314): only into a base whose protection refuses a push, and only that head.
import type { ToolDefinition } from "@novox/mesh-sdk/tools";
import { callerOf, refusal, parseVerbCallers, type VerbCallers } from "@novox/mesh-sdk/callers";
import { config } from "@novox/mesh-sdk/runtime";
import {
seat as GIT, verbs as GIT_VERBS,
type BranchAnswer, type ContainsAnswer, type CommentAnswer, type ErrorKind, type FileAnswer, type MergePullAnswer, type Merging, type NoteAnswer,
@@ -33,9 +29,6 @@ import { appendNote, viewBody, viewComment, type Runner } from "./delivery.js";
/** The verbs of the seat, in its definition's order. */
export const VERBS: readonly string[] = Object.values(GIT_VERBS);
/** The config value whose lines per verb say who may call it. */
export const CALLERS_KEY = "callers";
/** How many tags a page of `tags` holds. */
export const TAGS_PAGE = 50;
@@ -50,31 +43,13 @@ export interface HeadStatuses {
}
/** What the holder reaches beside the forge's API: git in the forge's container (the note), the head's one writer of its
* statuses, and the callers' list. */
* statuses, and whether the first registrations are written. */
export interface SeatDeps {
run: Runner;
container: string;
statuses: HeadStatuses;
/** Whether the first registrations were written (registrations.ts): until then merge-pull refuses every merge (design 47 §95). */
registered: () => boolean;
/** The config value `callers` as the runtime answers it; the runtime's own config read by default. */
callers?: () => Promise<unknown>;
}
/** The lines of `raw` (the config value, an object or that object as a string) for the seat's verbs; unset is none. */
export function gitCallers(raw: unknown): VerbCallers {
if (raw === undefined || raw === null || raw === "") return {};
let all: unknown = raw;
if (typeof raw === "string") {
try {
all = JSON.parse(raw);
} catch (err) {
throw new Error(`callers is not a JSON object of verb to callers: ${err instanceof Error ? err.message : String(err)}`);
}
}
if (!all || typeof all !== "object" || Array.isArray(all)) throw new Error("callers is not a JSON object of verb to callers");
const mine = Object.fromEntries(Object.entries(all as Record<string, unknown>).filter(([verb]) => VERBS.includes(verb)));
return parseVerbCallers(JSON.stringify(mine), VERBS, {});
}
/** A failure as the contract says it: the forge's or this holder's refusal, or a forge that failed. */
@@ -146,17 +121,14 @@ async function mergingOf(gitea: GiteaClient, owner: string, repo: string, p: Git
return { mergeable: p.mergeable, protected: !!rule, required, writes };
}
/** The seat's verbs as tools `git.<verb>`, each judging its caller first and answering in the seat's contract. */
/** The seat's verbs as tools `git.<verb>`, each answering in the seat's contract. */
export function seatTools(gitea: GiteaClient, deps: SeatDeps): ToolDefinition[] {
const listOf = deps.callers ?? (() => config(CALLERS_KEY));
const verb = (name: string, description: string, act: (args: Record<string, unknown>) => Promise<object>): ToolDefinition => ({
name: `${GIT}.${name}`,
description,
input: {},
run: async (args: Record<string, unknown>) => {
try {
const why = refusal(gitCallers(await listOf()), name, callerOf(args));
if (why) return failure(new Refused(why.message));
return await act(args);
} catch (err) {
return failure(err);
+6 -14
View File
@@ -431,19 +431,14 @@ test("the two new tools are named for what mesh_search is asked, and gitea_api s
assert.ok("refused" in judgeApiCall("PATCH", "/repos/a/b/pulls/7", { base: "x" }));
});
// **The seat git's verbs judge their caller, and merge-pull judges the Approve's head and no check** (design 47 *The seat
// git*, ADR 0661 §3): a caller the trusted list does not name is refused before the forge is asked; merge-pull merges the
// **merge-pull judges the head it is asked at and no check** (design 47 *The seat git*, §314): merge-pull merges the
// head it is named into a base that takes no push, whatever its statuses say, and an id said again after the merge is
// answered performed.
test("the seat git's verbs judge their caller, and merge-pull merges the Approve's head and judges no check", async () => {
const { seatTools, gitCallers } = await import("../dist/seat.js");
test("merge-pull merges the head the caller names and judges no check", async () => {
const { seatTools } = await import("../dist/seat.js");
const { runAs } = await import("@novox/mesh-sdk/callers");
const bridge = "ace.mesh-delivery-forge-bridge";
let registered = true;
const list = JSON.stringify({ publish: ["mesh-build-go"], "merge-pull": ["mesh-delivery-forge-bridge"],
pull: ["mesh-delivery-forge-bridge", "mesh-delivery-gate"], status: ["mesh-delivery-forge-bridge"] });
assert.deepEqual(Object.keys(gitCallers(list)).sort(), ["merge-pull", "pull", "status"], "only the seat git's lines are its");
assert.deepEqual(gitCallers(undefined), {}, "unset, no one may call");
// The head's one writer of its statuses (gate.ts), over a forge whose first write is still in flight when the next is asked.
const { GateWriter } = await import("../dist/gate.js");
const landed: string[] = [];
@@ -457,15 +452,13 @@ test("the seat git's verbs judge their caller, and merge-pull merges the Approve
};
const gates = new GateWriter(slow);
const seat = seatTools(Object.assign(Object.create(client), { setCommitStatus: slow.setCommitStatus }),
{ run: async () => ({ stdout: "", code: 0 }), container: "gitea", callers: async () => list, statuses: gates, registered: () => registered });
{ run: async () => ({ stdout: "", code: 0 }), container: "gitea", statuses: gates, registered: () => registered });
const call = (verb: string, args: Record<string, unknown>, caller = bridge) =>
runAs({ "mesh/caller": caller }, args, () => seat.find((t: any) => t.name === `git.${verb}`)!.run(args)) as Promise<any>;
const merge = (extra: Record<string, unknown> = {}, caller?: string) =>
call("merge-pull", { id: "act-1", repository: "novox/mesh-catalog", number: 7, head: REVIEWED, ...extra }, caller);
const rows: { name: string; set: () => void; caller?: string; extra?: Record<string, unknown>; want: RegExp | "merged" | "held"; loud?: boolean; kind?: string }[] = [
{ name: "no registration written yet", set: () => { registered = false; }, want: /no module is registered yet/, kind: "no-registrations" },
{ name: "a caller not on the list", set: () => {}, caller: "ace.mesh-delivery-checker", want: /refused for ace.mesh-delivery-checker/ },
{ name: "an unproven caller", set: () => {}, caller: "", want: /no caller the tool runner proved/ },
{ name: "a head that moved", set: () => { state.head = MOVED; }, want: /its head is 3c4d5e6f7a8b, not the abc1234def56/, kind: "head-moved" },
{ name: "a base no rule guards", set: () => { state.rules = []; }, want: /guarded by no protection rule/, kind: "unprotected" },
{ name: "a base whose protection was removed after the last branch-moved", set: () => { state.rules = [protectedMain()]; state.rules = []; }, want: /guarded by no protection rule/, kind: "unprotected" },
@@ -555,11 +548,10 @@ test("every read and act of the seat git answers its contract, and an error is a
};
const run = async (_f: string, args: string[]) => args.includes("show") || args.includes("--ref") && !args.includes("append")
? { stdout: noteShown, code: noteShown ? 0 : 1 } : (noteWritten++, { stdout: "", code: 0 });
const seat = seatTools(forge as any, { run: run as any, container: "gitea", callers: async () => JSON.stringify({}), statuses: { status: async () => {} }, registered: () => true });
const everyone = JSON.stringify(Object.fromEntries(seat.map((t: any) => [t.name.slice(4), ["*"]])));
const seat = seatTools(forge as any, { run: run as any, container: "gitea", statuses: { status: async () => {} }, registered: () => true });
const call = (verb: string, args: Record<string, unknown>, over: Record<string, unknown> = {}) => {
const t = seat.find((x: any) => x.name === `git.${verb}`)!;
const wrapped = seatTools(Object.assign(Object.create(forge), over) as any, { run: run as any, container: "gitea", callers: async () => everyone, statuses: { status: async () => {} }, registered: () => true });
const wrapped = seatTools(Object.assign(Object.create(forge), over) as any, { run: run as any, container: "gitea", statuses: { status: async () => {} }, registered: () => true });
return runAs({ "mesh/caller": "ace.x" }, args, () => wrapped.find((x: any) => x.name === t.name)!.run(args)) as Promise<any>;
};
const rows: [string, string, Record<string, unknown>, Record<string, unknown>, (a: any) => unknown, unknown][] = [