diff --git a/checks/handacts/fallbacks b/checks/handacts/fallbacks index c4fdf709..238233b7 100644 --- a/checks/handacts/fallbacks +++ b/checks/handacts/fallbacks @@ -2,7 +2,4 @@ # the fallback of a recorder (mesh-sdk handacts.Recorder) while the seat mesh-hand-acts has no holder that answers, until # the controller's lease is written to a holder that is no controller. The check of this directory (handacts) refuses any # other, and an entry whose module no longer invokes it: the list only shrinks, and it is empty before the lease write -# (novox/hq ADR 0565 §11; issue 1953, issue 774). -mesh-config hand-act fallback until the lease write: its sets, clears and held values record through mesh-hand-acts first -mesh-config warranted fallback until the lease write: its warrants record through mesh-hand-acts first -mesh-healers drill fallback until the lease write: a drill records through mesh-hand-acts first +# (novox/hq ADR 0565 §11; issue 1953, issue 774). It is empty: no manifest invokes the controller's log (issue 1953, 1955). diff --git a/modules/gitea/index.ts b/modules/gitea/index.ts index d5bc1898..9271f010 100644 --- a/modules/gitea/index.ts +++ b/modules/gitea/index.ts @@ -9,13 +9,13 @@ // // module.gitea.pull.closed — a pull request closed unmerged: the delivery it was is stopped (novox/hq ADR 0239) // -// Consumes mesh-controller.checked — a pull request's merge check, judged — and sets it as the head -// commit's statuses: `mesh/merge-gate`, the modules of the mesh's graph the change touches, and +// Once consumed mesh-controller.checked — a pull request's merge check, judged — and set it as the head +// commit's statuses (setVerdict, kept below for a revert, and bound to no event since ADR 0571 clause 13): `mesh/merge-gate`, the modules of the mesh's graph the change touches, and // `mesh/repo-check`, the repository's own merge-check.sh (novox/hq ADR 0237 as amended); with a comment // saying why when the gate is not a pass or the repository's own check failed. Of the two, only those the base // still requires are written (issue 1301): main requires `mesh/check` — the checker's — everywhere, hq included since // ADR 0527 §2, and since R4g no controller judges a merge (its merge gate went with build-agent, ADR 0524 §5), so nothing -// publishes mesh-controller.checked and these two are written for no base. +// publishes a merge check's verdict, this module consumes no event of the controller, and these two are written for no base. // // Reads mesh-delivery-checker.owed — what the checker owes the forge, mesh/check on each head it judged — by a watch, and // writes it (check.ts; ADR 0521, R4e finding R2-1). No tool writes mesh/check (finding K1), and no event: the watch hands @@ -43,7 +43,7 @@ // The polling is deliberately unhurried: an event a minute late is still an event, whereas hammering // the forge for an immediacy nobody asked for is not. -import { emit, on } from "@novox/mesh-sdk/events"; +import { emit } from "@novox/mesh-sdk/events"; import { registerModuleTools } from "@novox/mesh-sdk/tools"; import { statesAnnouncement } from "./kept.js"; import { commitStatusTool } from "./delivery.js"; @@ -649,19 +649,9 @@ if (gitea) { setTimeout(() => { catchUpAsked = false; void pass(); }, 30_000); }; void listenOk.then((ok) => (ok ? pass() : undefined)); - // Bound again until they hold, and not awaited (bind.ts, issue 1793): the runtime kills a bundle that has not answered its - // handshake in 10 s, so the import must end while a refused bind is still asked again. - bindInBackground([ - ["mesh-controller.checked", () => on("mesh-controller.checked", (event) => { - const c = (event.body ?? {}) as Checked; - // A head's own verdict takes its turn after every write of that head's gate already asked for (gate.ts). - // A verdict of a check asked before the one now pending is an older run's, and dropped (novox/hq issue 456). - if (!c.group && c.owner && c.repo && c.commit) { - return gates.verdict(c.owner, c.repo, c.commit, () => setVerdict(client, event), c.id); - } - return setVerdict(client, event); - })], - ], () => console.log("[gitea] watching for new repositories and merged pull requests")); + // No consumer is bound since ADR 0571 clause 13 took the controller's checked out of this module; a consumer added later + // is bound again until it holds, and not awaited (bind.ts, issue 1793). + bindInBackground([], () => console.log("[gitea] watching for new repositories and merged pull requests")); // The checker's mesh/check, from its owed state (check.ts; ADR 0521): a read not granted yet is said once and asked again, // apart from the binds above, so neither waits for the other. void watchOwed((handler) => state(CHECKER_OWED).watch((change) => handler(change)), checks); diff --git a/modules/gitea/module.json b/modules/gitea/module.json index 7d703aff..2fd3ae33 100644 --- a/modules/gitea/module.json +++ b/modules/gitea/module.json @@ -86,9 +86,6 @@ "forge.wiki.changed", "forge.other" ], - "consumes": [ - "mesh-controller.checked" - ], "listens": [ { "name": "web", diff --git a/modules/mesh-config/cmd/mesh-config/main.go b/modules/mesh-config/cmd/mesh-config/main.go index 1256b0fa..8b8de588 100644 --- a/modules/mesh-config/cmd/mesh-config/main.go +++ b/modules/mesh-config/cmd/mesh-config/main.go @@ -75,8 +75,9 @@ func (runtimeStore) Read(log string) ([]LogEntry, error) { func (runtimeStore) Emit(event string, body any) error { return stdio.Emit(event, body) } -// recorder records into the hand-act log of the seat mesh-hand-acts, falling back to the controller's while the seat -// has no holder that answers and the controller still holds its lease (novox/hq ADR 0565 §11). +// recorder records into the hand-act log of the seat mesh-hand-acts (novox/hq ADR 0565 §11). The manifest invokes no verb +// of the controller (issue 1953), so the SDK recorder's fall back to the controller's log, while the seat has no holder, +// is refused by the bus and said in the answer: nothing is recorded in two logs. var recorder = handacts.Recorder{Ask: stdio.Ask, LeaseAway: func() bool { e, err := stdio.State(LeaseRead).Get(buscompose.ControllerLeaseKey) var raw []byte diff --git a/modules/mesh-config/module.json b/modules/mesh-config/module.json index 9554db29..ffe59e18 100644 --- a/modules/mesh-config/module.json +++ b/modules/mesh-config/module.json @@ -28,8 +28,6 @@ "invokes": [ "seat:mesh-hand-acts.hand-act", "seat:mesh-hand-acts.warranted", - "seat:mesh-controller.hand-act", - "seat:mesh-controller.warranted", "gitea.gitea_get_pull_request" ], "reads": [ diff --git a/modules/mesh-healers/cmd/mesh-healers/healers_test.go b/modules/mesh-healers/cmd/mesh-healers/healers_test.go index 9b6f9dce..ad2de049 100644 --- a/modules/mesh-healers/cmd/mesh-healers/healers_test.go +++ b/modules/mesh-healers/cmd/mesh-healers/healers_test.go @@ -442,8 +442,8 @@ func TestTheVerbsAndReads(t *testing.T) { if a["acting"] != true || len(a["acts"].([]Attempt)) != 1 || len(a["healers"].([]map[string]any)) != 3 { t.Errorf("healers answered %v", a) } - // drill records into mesh-hand-acts as a drill; while that seat has no holder, into the controller's drill until - // the lease is handed away (ADR 0565 §11). + // drill records into mesh-hand-acts as a drill, and never into the controller's log, whoever holds the lease + // (ADR 0565 §11, issue 1955). drills := []struct { name string seatGone bool @@ -452,8 +452,9 @@ func TestTheVerbsAndReads(t *testing.T) { log, err string }{ {"into the seat", false, true, map[string]any{"what": "stopped records", "why": "does it raise"}, handacts.LogSeat, ""}, - {"no holder: the controller's", true, false, map[string]any{"what": "stopped records", "why": "x"}, handacts.LogController, ""}, - {"no holder, lease away", true, true, map[string]any{"what": "stopped records", "why": "x"}, "", "handed its lease away"}, + {"into the seat under a controller's lease", false, false, map[string]any{"what": "stopped records", "why": "x"}, handacts.LogSeat, ""}, + {"no holder: refused, the controller's not asked", true, false, map[string]any{"what": "stopped records", "why": "x"}, "", "nothing was recorded"}, + {"no holder, lease away", true, true, map[string]any{"what": "stopped records", "why": "x"}, "", "nothing was recorded"}, {"says nothing", false, true, map[string]any{}, "", "says what"}, } for _, d := range drills { @@ -489,6 +490,11 @@ func TestTheVerbsAndReads(t *testing.T) { case d.log == handacts.LogSeat && !strings.Contains(asked[0], `"verb":"hand-act drill"`): t.Errorf("drill %s asked %v", d.name, asked) } + for _, k := range asked { + if strings.Contains(k, "mesh-controller") { + t.Errorf("drill %s asked the controller: %v", d.name, asked) + } + } } if _, err := w.h.read(WouldState, ""); err != nil { t.Error(err) diff --git a/modules/mesh-healers/cmd/mesh-healers/manifest_test.go b/modules/mesh-healers/cmd/mesh-healers/manifest_test.go index af3745b9..bbbe8d49 100644 --- a/modules/mesh-healers/cmd/mesh-healers/manifest_test.go +++ b/modules/mesh-healers/cmd/mesh-healers/manifest_test.go @@ -49,7 +49,7 @@ func TestTheManifestSaysWhatTheCodeDoes(t *testing.T) { }{ {"emits (healer-acted is the seat's, said on its subject)", m.Emits, nil}, {"consumes", m.Consumes, []string{RaisedEvent, ChangedEvent, RecordedEvent}}, - {"invokes", m.Invokes, []string{AssertTool, "seat:mesh-hand-acts.hand-act", "seat:mesh-controller.drill"}}, + {"invokes", m.Invokes, []string{AssertTool, "seat:mesh-hand-acts.hand-act"}}, {"reads", m.Reads, []string{LeaseRead, ConditionsRead, HandActsRead}}, } { if !slices.Equal(c.got, c.want) { diff --git a/modules/mesh-healers/cmd/mesh-healers/tools.go b/modules/mesh-healers/cmd/mesh-healers/tools.go index d3452b95..c26a30f9 100644 --- a/modules/mesh-healers/cmd/mesh-healers/tools.go +++ b/modules/mesh-healers/cmd/mesh-healers/tools.go @@ -57,8 +57,7 @@ func Tools(h *Healers) []stdio.Tool { }}, {Name: DrillTool, Description: "Record a drill: something broken on purpose to see the mesh raise and clear it, with what it " + - "tests, in the hand-act log of the seat mesh-hand-acts (the controller's drill while that seat has no " + - "holder and the controller holds its lease). A drill is never a repair: S15 never counts it.", + "tests, in the hand-act log of the seat mesh-hand-acts. A drill is never a repair: S15 never counts it.", Input: obj(map[string]any{"what": map[string]any{"type": "string", "description": "what was done on purpose"}, "why": map[string]any{"type": "string", "description": "what the drill tests"}, "condition": map[string]any{"type": "string", "description": "the condition it is meant to raise, if any"}}), @@ -85,8 +84,8 @@ func Tools(h *Healers) []stdio.Tool { } // Drill records a drill (ADR 0565 §11, which ends ADR 0564 Limits 1's refusal): through the seat mesh-hand-acts as -// the act `hand-act drill`, and while that seat has no holder that answers and the controller still holds its lease, -// through the controller's own `drill`, which records it as a drill too. It answers which log took it. +// the act `hand-act drill`. The controller's own `drill` is not asked (issue 1955): a seat with no holder that answers +// refuses the drill, said whole, and nothing is recorded in a second log. func (h *Healers) Drill(what, why, condition string) (any, error) { if what == "" || why == "" { return nil, errors.New("a drill says what was done on purpose and what it tests: nothing was recorded") @@ -95,27 +94,14 @@ func (h *Healers) Drill(what, why, condition string) (any, error) { return nil, errors.New("the hand-act log cannot be reached from here: nothing was recorded") } body := map[string]any{"what": what, "why": why, "verb": handacts.VerbDrill} - controller := map[string]any{"what": what, "why": why} if condition != "" { - body["condition"], controller["condition"] = condition, condition + body["condition"] = condition } answer, err := h.Ask("seat:"+handacts.Seat+".hand-act", body) - if err == nil { - return map[string]any{"log": handacts.LogSeat, "answer": answer}, nil + if err != nil { + return nil, fmt.Errorf("%s.hand-act refused the drill, so nothing was recorded: %w", handacts.Seat, err) } - if !handacts.NoResponder(err) { - return nil, fmt.Errorf("%s.hand-act refused the drill: %w", handacts.Seat, err) - } - if acting, _ := h.Acting(); acting { - return nil, fmt.Errorf("%s has no holder that answers, and the controller handed its lease away: nothing was "+ - "recorded (%w)", handacts.Seat, err) - } - answer, cerr := h.Ask("seat:mesh-controller.drill", controller) - if cerr != nil { - return nil, fmt.Errorf("%s has no holder that answers (%v), and the controller's drill refused too: %w", handacts.Seat, - err, cerr) - } - return map[string]any{"log": handacts.LogController, "answer": answer}, nil + return map[string]any{"log": handacts.LogSeat, "answer": answer}, nil } // Heal is the verb heal: one condition judged now by its healer. diff --git a/modules/mesh-healers/module.json b/modules/mesh-healers/module.json index 634dd388..6f2f0688 100644 --- a/modules/mesh-healers/module.json +++ b/modules/mesh-healers/module.json @@ -20,8 +20,7 @@ ], "invokes": [ "mesh-grants.grants_assert", - "seat:mesh-hand-acts.hand-act", - "seat:mesh-controller.drill" + "seat:mesh-hand-acts.hand-act" ], "reads": [ "mesh-controller.lease",