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
This commit is contained in:
@@ -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.
|
||||
+71
@@ -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.
|
||||
@@ -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 `<node>/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
|
||||
`<prefix>.<name>`. 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. `<node>/<module>.<tool>` 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.
|
||||
Reference in New Issue
Block a user