From 41a0e77748aec7a30b79be944836eadeae0b2ce6 Mon Sep 17 00:00:00 2001 From: jochen Date: Wed, 7 Oct 2026 01:42:57 +0200 Subject: [PATCH] Issues 282-284: the merge gate judged nothing, the seat's check disagreed with agents' machines, a seat and a module of one name were unreachable --- .../00-report.md | 79 +++++++++++++++++++ .../00-report.md | 71 +++++++++++++++++ .../00-report.md | 49 ++++++++++++ 3 files changed, 199 insertions(+) create mode 100644 04-ISSUES/282-the-merge-gate-compared-broken-to-broken-and-passed/00-report.md create mode 100644 04-ISSUES/283-a-merge-check-passed-on-an-agents-machine-and-failed-on-the-build-seat/00-report.md create mode 100644 04-ISSUES/284-a-seat-and-a-module-of-one-name-were-both-unreachable/00-report.md diff --git a/04-ISSUES/282-the-merge-gate-compared-broken-to-broken-and-passed/00-report.md b/04-ISSUES/282-the-merge-gate-compared-broken-to-broken-and-passed/00-report.md new file mode 100644 index 00000000..0c380f8e --- /dev/null +++ b/04-ISSUES/282-the-merge-gate-compared-broken-to-broken-and-passed/00-report.md @@ -0,0 +1,79 @@ +--- +status: located +opened: 2026-10-07 +located-in: [mesh-controller cmd/mesh-controller, mesh-controller internal/facts, mesh-controller internal/builder] +fixed-by: mesh-controller PR #104 +amended-design: +--- + +# 282. The merge gate compared broken to broken, and passed + +## Symptom + +On 2026-10-07 every pull request the build seat checked through the gate came back with the same +verdict: + +> gate PASS — every machine composes with the change as it did without (0 of 4 compose) + +That was four pull requests to the controller and the node-engine. **No machine composed** in the gate's +store, with the change or without, though all four compose on the mesh. The gate's rule is "what composes +in the base and not with the change fails". With nothing composing in the base, that rule can never fire. +So the gate validated nothing and said PASS. Each verdict carried, as notes only, one line per machine: +"composes on the mesh and not as the snapshot raised it … judged by what the change adds". + +## Diagnosis + +Reproduced off the mesh with the controller's own `merge-gate` against the live facts snapshot and a +throwaway store. The base was refused on every machine. Each cause was a fact the gate did not raise +from the snapshot: + +1. **No bus account.** Since issue 203, composition refuses a module whose own secret `broker` has no + minted account behind it ("has no bus credential: nothing was issued"). The snapshot lists that + secret among each machine's accepted secrets. The gate stored a stand-in value for it but minted no + account. So every machine running a module on the bus (the build agent, the route proxy, the + distribution registry) failed. That covers all four. +2. **No outward links.** With that fixed, every machine failed next on "has not reported which of its + links face outside" (ADR 0140): a filter is written around those links. The snapshot did not carry + them. +3. **Settings refused on the way in.** The gate kept each settings layer through `settings set`, which + judges a layer alone. A mesh-wide layer whose module needs a value that only a machine's layer gives + was refused, though the mesh holds both and composes them. A media server's access path was refused + too. The scrub had withheld a path that read as a key, and the bare word `withheld` is not an + absolute path. +4. **The JSON verdict was not alone on stdout.** Once machines composed, composition printed its own + lines (bus users without a credential) before the verdict. The build seat would then have read no + verdict at all. + +Ruled out: the change under check. The same four refusals appear with no change at all. + +## Fix + +- The gate mints the account with the credential for every accepted `broker` of a module that reads + one. The snapshot carries each machine's outward links, scrubbed. For a snapshot from an older + controller, the gate stands one in for a machine that composes on the mesh. Layers are kept as the + mesh holds them (`KeepSettings`), because composition still judges every layer. A withheld path keeps + a path's shape, both in the scrub and in the gate's stand-ins. +- **A baseline that does not compose is an error, never a pass.** A machine that composes on the mesh + and not in the gate is listed under "could not judge". The verdict is then `error`, the build seat + reports it in the gate's own words, and the command exits non-zero. +- In `--json` mode, only the verdict is written to stdout. + +Measured on the live snapshot of 2026-10-06 with the fixed controller: **4 of 4 compose** in the base. +The resources composed match the mesh's own declaration for each machine, apart from pseudonyms (which +the snapshot now applies to resource names too) and the machine's bus membership file. + +## How it is checked + +The controller's tests `TestIssue282AModuleOnTheBusComposesInTheGate` and +`TestIssue282AMachineTheGateCannotRaiseIsAnErrorNeverAPass`: a module on the bus composes in the gate, +and a machine the gate cannot raise turns the verdict to `error`. `TestAWithheldPathIsStoodInForByAPath` +and the facts package's `TestAWithheldPathStaysAPath` cover the path stand-ins. + +## Left open + +- A change to the controller is judged by its own controller, so its pull requests see this fix at once. + Every other pull request is judged by the controller the mesh runs, and sees the fix only after this + one is merged and rolled out. +- The snapshot does not say whether a machine holds a bus membership, so the gate composes none. It is + one resource, the same with the change and without. +- The snapshot's `sources` still name each repository by the forge's address and owner. diff --git a/04-ISSUES/283-a-merge-check-passed-on-an-agents-machine-and-failed-on-the-build-seat/00-report.md b/04-ISSUES/283-a-merge-check-passed-on-an-agents-machine-and-failed-on-the-build-seat/00-report.md new file mode 100644 index 00000000..c60a78c3 --- /dev/null +++ b/04-ISSUES/283-a-merge-check-passed-on-an-agents-machine-and-failed-on-the-build-seat/00-report.md @@ -0,0 +1,71 @@ +--- +status: located +opened: 2026-10-07 +located-in: [mesh-controller internal/builder, mesh-controller cmd/mesh-controller, mesh-host internal/bootstrap] +fixed-by: mesh-controller PR #104, mesh-host PR #46 +amended-design: +--- + +# 283. A merge check passed on an agent's machine and failed on the build seat + +## Symptom + +On 2026-10-07 the repository layer (`mesh/repo-check`) failed on the build seat for pull requests whose +`merge-check.sh` had passed on the machine of the agent who wrote them: + +- three node-engine pull requests: "its merge-check.sh failed: internal/bootstrap/publish_test.go"; +- two controller pull requests: "its merge-check.sh failed: FAIL". + +Neither summary says what failed. + +## Diagnosis + +The build seat runs the script in a container of the toolchain image the mesh holds, as the user the +seat's service runs as, against a throwaway store and bus. The repositories it reads are cloned beside +the checkout at the refs the controller asks for. The agents ran the same script in their own +environment. Three differences, each one the environment and not the change: + +1. **Another Go.** The toolchain is one release of Go and the agents' machines run the next. Their + `gofmt` disagree on how to lay out a `return` of several composite literals spanning lines. Each + one reformats the other's output, so no file using that construct satisfies both. The node-engine's + script failed at `gofmt -l` on the seat. Its last line, the file's name, became the summary. +2. **Siblings at another ref.** The controller's test that compares the installer's first bus user list + with what the controller derives reads the node-engine checkout beside it. On the seat that checkout + was at the commit the mesh runs. On the agent's machine it was at the agent's feature branch, which + carried the matching change. The test failed on the seat, correctly for that change judged on its + own, and the summary was the script's last line: "FAIL". +3. **Another user.** The seat runs its check containers as root, over checkouts root owns. Run as root + over checkouts another user cloned, `git` refuses the repository ("dubious ownership") and Go's VCS + stamping fails the judge's build. + +## Fix + +- **The seat is the reference, and a check can be run as it runs.** `mesh-controller check-here` runs + the same code the seat runs (`builder.Check`) on the checkout's HEAD. It builds the ask the controller + would make: the gate's modules and judge by the planner's answer over the facts snapshot, and the + siblings at the refs the snapshot names. It runs in the toolchain the snapshot names, as the seat's + user, against a throwaway store and bus of the mesh's versions. The snapshot now carries the + toolchains (without the store's address) and the refs cloned beside a check. Both come from the same + rule the controller asks the seat with. +- Check containers set `safe.directory` for the check's own checkouts, so whoever cloned them, git + and Go's build read them. +- A failed script is summarised by what failed: the first failing test, the first failing package, or + the files the toolchain's `gofmt` would change. +- The node-engine's test is laid out so both releases' `gofmt` agree. + +## How it is checked + +`check-here` on the node-engine's main reproduced the seat's failure: "not gofmt'd by the toolchain's +gofmt: internal/bootstrap/publish_test.go". It passed after the fix. The builder's test +`TestAFailedScriptIsSaidByWhatFailed` and the controller's `TestACheckByHandClonesWhatTheSeatClones` +cover the summary and the refs. + +## Left open + +- A change spread over two repositories as a delivery group (ADR 0239) is still checked one pull + request at a time on the repository layer, with the siblings at what the mesh runs. A test that reads + a sibling fails for each member alone. The group's composed check runs the gate over every head + together, but skips each member's own script. A member's script should run with the group's other + heads beside it. +- The toolchain's Go is older than the agents'. Raising it is a change to the toolchain module, not to + the check. diff --git a/04-ISSUES/284-a-seat-and-a-module-of-one-name-were-both-unreachable/00-report.md b/04-ISSUES/284-a-seat-and-a-module-of-one-name-were-both-unreachable/00-report.md new file mode 100644 index 00000000..0a24a017 --- /dev/null +++ b/04-ISSUES/284-a-seat-and-a-module-of-one-name-were-both-unreachable/00-report.md @@ -0,0 +1,49 @@ +--- +status: located +opened: 2026-10-07 +located-in: [mesh-tools node-tools/internal/console] +fixed-by: mesh-tools PR #19 +amended-design: +--- + +# 284. A seat and a module of one name were both unreachable + +## Symptom + +On 2026-10-07 the console's overview listed the seat `mesh-delivery` as held on the control node with +**no verbs**. Every address with the name was refused the same way, the module's own tools included: + +> the seat mesh-delivery has no verb deliveries; it has + +That covered `mesh-delivery.deliveries` and `/mesh-delivery.delivery_status`. Neither the seat's +verbs nor the module's tools could be called through the console. The controller's own record of the +seat was whole: `mesh-controller.tools` listed its ten verbs. + +## Diagnosis + +`mesh-delivery` is both the delivery's seat and the module holding it (ADR 0239). The module answers the +seat's verbs with tools of the same names, and has tools of its own as well. The console builds its +index from the runtimes' announcements, and kept one table of what it had listed, keyed +`.`. The module's tool `mesh-delivery.deliveries` and the seat's verb +`mesh-delivery.deliveries` shared a key. The module's came first, so the seat was listed with no verb. + +Resolution then looked for a seat of the address's prefix before anything else. It found the +verb-less seat and refused, so the module's tools were never reached, even through a node-qualified +address. + +Ruled out: the controller's seat records (seeded with the verbs, as `tools` shows) and the module's +announcement (the seat's holder is heard, which is why the overview names the machine). + +## Fix + +- The seat's verbs and the modules' tools are keyed apart, so both are listed. +- When a seat and a module share a name, the address goes to the module's tool in two cases: the seat + has no such verb, or a machine is named for a seat held once for the mesh. `/.` is + a module on one machine. A seat with no verbs at all says that its holder announced none, instead of + ending on an empty list. + +## How it is checked + +The console's `TestASeatAndAModuleOfOneNameAreEachReached` covers this: the seat listed with its verbs, +the module with its tools, and each address resolved to the seat or the module. The test launches no +bundle, so the repository's `merge-check.sh` runs it even where the console's other tests cannot run.