Rebased on main 056414b (after #144). Migration 0082 follows #144's 0081 (which only creates artifact_mirrored); no other open branch holds a migration above 0081. 0082 has never run on the live store, so the review's schema changes are made in it, not in a new number.
Why
On 2026-10-08 assign g14 sensors answered "no module of that name" a minute after the merge that asked for the module's build (issue 300); minutes later it worked. The controller kept no record of a build it asked for until the outcome came back.
What changes
Build requests (build_request): kept once the ask is made, never before, with the real asker (merge, plan, build, assign, rebuild, replay); a request not handed over or not waited for is marked not-asked, unless its outcome was heard. Kept 30 days.
assign of a module the catalogue does not hold says its case: in flight (no outcome within 2 h, or built within 5 min and being registered) keeps a pending assignment; known, not built refuses and offers build or build: "true"; unknown refuses, claiming only what it checked (the catalogue and the kept requests), with the closest names.
build: "true" asks from the last request's source; a pending assignment already waiting is re-pointed to the new build, and no build is asked that nothing waits on.
Making it (ADR 0261): under the machine's hold, the row is claimed (waiting to applying), the ordinary assign act runs, then the final state is set. unassign of a claimed row is refused, never overridden. A claim left by a stopped controller is settled by the tick from what the mesh holds.
Settling is a controller tick every minute (settlingPending, started in serve) plus each build's outcome. status, the board, the summary and the probe only read.
An assignment not made (expired or refused) raises the condition assignment-not-made (warning, operator), with plain words; it clears when the module is assigned there later, assigned again, or taken back with unassign. status is not well only while a pending assignment is open; the condition carries the rest.
Housekeeping: ended rows deleted after 30 days; Pending(zero) is state = 'waiting'; pending_assignment.node is a foreign key to node with on delete cascade.
Making a pending assignment sends nothing, as a person's assign sends nothing: its note says push <node> sends it. No claim about the upgrade announcement remains.
Tests
pending_test.go: in flight (answer, asker merge, status + JSON, build applying it); a status read settles nothing and the tick does; failed build expiring, its condition raised, read as known-not-built, cleared by unassign; cleared by a later assignment; build: "true" (asker assign); re-pointing a waiting row with one ask; a failed ask and a request not waited for never in flight, an outcome heard after still the last word; a plan's request kept as plan; a build being registered in flight, then expired past the grace; unknown wording; several-module act refused whole; withdraw then build makes nothing; a claim refused to unassign, Pending(zero) excluding it, a stale claim released or applied; silent build expiring; ended rows deleted after 30 days; seat build; plain words. internal/inventory/pending_test.go: a row goes with its machine. TestReplay325.
sh merge-check.sh: exit 0 locally.
Do not merge without the operator's approval.
hq issue 325 and ADR 0261 (novox/hq#214). Replay R325: novox/mesh-lab#68.
Rebased on main 056414b (after #144). Migration **0082** follows #144's 0081 (which only creates `artifact_mirrored`); no other open branch holds a migration above 0081. 0082 has never run on the live store, so the review's schema changes are made in it, not in a new number.
## Why
On 2026-10-08 `assign g14 sensors` answered "no module of that name" a minute after the merge that asked for the module's build (issue 300); minutes later it worked. The controller kept no record of a build it asked for until the outcome came back.
## What changes
- **Build requests** (`build_request`): kept once the ask is made, never before, with the real asker (`merge`, `plan`, `build`, `assign`, `rebuild`, `replay`); a request not handed over or not waited for is marked not-asked, unless its outcome was heard. Kept 30 days.
- **`assign` of a module the catalogue does not hold** says its case: *in flight* (no outcome within 2 h, or built within 5 min and being registered) keeps a **pending assignment**; *known, not built* refuses and offers `build` or `build: "true"`; *unknown* refuses, claiming only what it checked (the catalogue and the kept requests), with the closest names.
- **`build: "true"`** asks from the last request's source; a pending assignment already waiting is re-pointed to the new build, and no build is asked that nothing waits on.
- **Making it** (ADR 0261): under the machine's hold, the row is claimed (`waiting` to `applying`), the ordinary `assign` act runs, then the final state is set. `unassign` of a claimed row is refused, never overridden. A claim left by a stopped controller is settled by the tick from what the mesh holds.
- **Settling** is a controller tick every minute (`settlingPending`, started in `serve`) plus each build's outcome. `status`, the board, the summary and the probe only read.
- **An assignment not made** (expired or refused) raises the condition `assignment-not-made` (warning, operator), with plain words; it clears when the module is assigned there later, assigned again, or taken back with `unassign`. `status` is not well only while a pending assignment is open; the condition carries the rest.
- Housekeeping: ended rows deleted after 30 days; `Pending(zero)` is `state = 'waiting'`; `pending_assignment.node` is a foreign key to `node` with `on delete cascade`.
- Making a pending assignment sends nothing, as a person's `assign` sends nothing: its note says `push <node>` sends it. No claim about the upgrade announcement remains.
## Tests
`pending_test.go`: in flight (answer, asker `merge`, status + JSON, build applying it); a status read settles nothing and the tick does; failed build expiring, its condition raised, read as known-not-built, cleared by `unassign`; cleared by a later assignment; `build: "true"` (asker `assign`); re-pointing a waiting row with one ask; a failed ask and a request not waited for never in flight, an outcome heard after still the last word; a plan's request kept as `plan`; a build being registered in flight, then expired past the grace; unknown wording; several-module act refused whole; withdraw then build makes nothing; a claim refused to unassign, `Pending(zero)` excluding it, a stale claim released or applied; silent build expiring; ended rows deleted after 30 days; seat `build`; plain words. `internal/inventory/pending_test.go`: a row goes with its machine. `TestReplay325`.
`sh merge-check.sh`: exit 0 locally.
Do not merge without the operator's approval.
composed together: pass — every machine composes with the change as it did without (4 of 4 compose)
Machines
mesh-controller on novox: passed — healthy 3 times over 3m39s
mesh-controller on the rest running it: sent
build-agent on ace: passed — healthy 3 times over 3m33s
Transitions
2026-10-08 14:45 (new) → proposed (announced): novox/mesh-controller#150's head announced
2026-10-08 14:51 proposed → checked (checked): the verdict names this commit
2026-10-08 14:51 checked → ready (accepted): the gate passed or warned, and the repository's own check did not fail
2026-10-08 14:58 ready → published (merged): on the trunk its modules follow: merged there, and its walk opened — or nothing for a walk to move
2026-10-08 14:58 published → delivering (go): its walk started: let go by this owner in its group's order, by a person, or on the controller's own path
2026-10-08 15:09 delivering → superseded (superseded): a newer delivery to the same trunk took over its walk
The commit's note under refs/notes/mesh-plan keeps every transition: git log --notes=mesh-plan.
<!-- mesh-delivery:view -->
**Delivery** `novox/mesh-controller@8adb7f1a0519` — **superseded** since 2026-10-08T15:09:41Z
**Delivery plan** — builds build-agent, mesh-controller, route-proxy → ace, g14, novox, shanks; no bus step
- tier 0: mesh-controller
- tier 1: build-agent
- tier 2: route-proxy
- ace: receives build-agent, route-proxy
- g14: receives build-agent
- novox: receives mesh-controller, build-agent, route-proxy
- shanks: receives build-agent
- build-agent keeps data (ADR 0233): what it holds is backed up before it moves
- route-proxy provides route: its consumers are sent again after it
- route-proxy keeps data (ADR 0233): what it holds is backed up before it moves
**Group** `fix/assign-says-why-a-module-is-not-there`, in order: novox/mesh-controller@8adb7f1a0519 → novox/mesh-lab@33e63ee5b47a
- composed together: pass — every machine composes with the change as it did without (4 of 4 compose)
**Machines**
- mesh-controller on novox: passed — healthy 3 times over 3m39s
- mesh-controller on the rest running it: sent
- build-agent on ace: passed — healthy 3 times over 3m33s
**Transitions**
- 2026-10-08 14:45 (new) → proposed (announced): novox/mesh-controller#150's head announced
- 2026-10-08 14:51 proposed → checked (checked): the verdict names this commit
- 2026-10-08 14:51 checked → ready (accepted): the gate passed or warned, and the repository's own check did not fail
- 2026-10-08 14:58 ready → published (merged): on the trunk its modules follow: merged there, and its walk opened — or nothing for a walk to move
- 2026-10-08 14:58 published → delivering (go): its walk started: let go by this owner in its group's order, by a person, or on the controller's own path
- 2026-10-08 15:09 delivering → superseded (superseded): a newer delivery to the same trunk took over its walk
The commit's note under `refs/notes/mesh-plan` keeps every transition: `git log --notes=mesh-plan`.
Correction to the description's migration note: the store refuses a migration numbered below one already applied (store.Pending). So whichever of #144 (0081) and this PR (0082) merges second must be the higher number: if this merges first, #144 renumbers its 0081 to 0083 before it merges; if #144 merges first, nothing changes here.
Correction to the description's migration note: the store refuses a migration numbered below one already applied (`store.Pending`). So whichever of #144 (0081) and this PR (0082) merges second must be the higher number: if this merges first, #144 renumbers its 0081 to 0083 before it merges; if #144 merges first, nothing changes here.
build-agent keeps data (ADR 0233): what it holds is backed up before it moves
route-proxy provides route: its consumers are sent again after it
route-proxy keeps data (ADR 0233): what it holds is backed up before it moves
Run by the build seat on g14 as build-1791467365220841250 (builds --log build-1791467365220841250).
--- the gate: build-agent, mesh-controller, route-proxy
mesh-controller: ok, 50 tool(s); WARNING: controller stay(s) up and say(s) not how it is ready — judged by liveness alone, refused from 2026-11-18 (ADR 0240 rule 8)
long-running resources without health: 1
1 manifest(s) checked. Judged against the seats this binary carries; a claim on one of the mesh's own seats is judged fully at registration, and a seat declared by a module not given here reads as unknown. Identities judged on a 6-character machine name, against the bounds of the providers given here
judging 0 module(s) changed against 4 machine(s), as the snapshot of 2026-10-08T13:48:06Z says they run
composing every machine as the mesh is
the bus's user list leaves out 5 user(s) the mesh has minted no credential for: controller, node.f35, node.idt, node.ygcyr, node.yvbpsm. Each is a user that cannot connect until one is issued
the bus's user list leaves out 5 user(s) the mesh has minted no credential for: controller, node.f35, node.idt, node.ygcyr, node.yvbpsm. Each is a user that cannot connect until one is issued
composing every machine with the change
the bus's user list leaves out 5 user(s) the mesh has minted no credential for: controller, node.f35, node.idt, node.ygcyr, node.yvbpsm. Each is a user that cannot connect until one is issued
the bus's user list leaves out 5 user(s) the mesh has minted no credential for: controller, node.f35, node.idt, node.ygcyr, node.yvbpsm. Each is a user that cannot connect until one is issued
merge gate: PASS — every machine composes with the change as it did without (4 of 4 compose)
judged by controller development build against the facts of 2026-10-08T13:48:06Z
machines:
- a machine (f35): composes
- holds mesh-dns-resolver (idt): composes
- holds git, holds mesh-artifact-store, holds mesh-broker, holds mesh-catalog, holds mesh-delivery, holds mesh-dns-resolver, holds mesh-store, holds mesh-vault, holds npm-package-registry, the control node, the hub (ygcyr): composes
- a machine (yvbpsm): composes
a merge would rebuild 3 module(s) in 3 tier(s): build-agent, mesh-controller, route-proxy
--- its merge-check.sh (go toolchain)
ok github.com/novox/mesh-controller/cmd/mesh-builder 1.035s
ok github.com/novox/mesh-controller/cmd/mesh-controller 169.187s
ok github.com/novox/mesh-controller/examples/objectstore-provisioner 1.014s
ok github.com/novox/mesh-controller/examples/postgres-provisioner 1.019s
ok github.com/novox/mesh-controller/examples/redis-provisioner 1.014s
ok github.com/novox/mesh-controller/examples/route-proxy 1.239s
ok github.com/novox/mesh-controller/internal/artifacts 1.050s
ok github.com/novox/mesh-controller/internal/broker 2.755s
ok github.com/novox/mesh-controller/internal/builder 2.601s
ok github.com/novox/mesh-controller/internal/catalogue 4.709s
ok github.com/novox/mesh-controller/internal/conditions 2.245s
ok github.com/novox/mesh-controller/internal/envfile 1.024s
ok github.com/novox/mesh-controller/internal/facts 1.032s
ok github.com/novox/mesh-controller/internal/identity 5.347s
ok github.com/novox/mesh-controller/internal/inventory 74.800s
ok github.com/novox/mesh-controller/internal/lease 17.667s
ok github.com/novox/mesh-controller/internal/licences 4.947s
ok github.com/novox/mesh-controller/internal/licences/adapters 1.010s
ok github.com/novox/mesh-controller/internal/link 44.575s
ok github.com/novox/mesh-controller/internal/lint 2.205s
ok github.com/novox/mesh-controller/internal/outward 1.046s
ok github.com/novox/mesh-controller/internal/overlay 1.036s
ok github.com/novox/mesh-controller/internal/secrets 1.605s
ok github.com/novox/mesh-controller/internal/store 3.459s
ok github.com/novox/mesh-controller/internal/testbus 1.195s
ok github.com/novox/mesh-controller/internal/token 1.014s
**Merge check** at `b93dea8c`
- `mesh/merge-gate`: **PASS** — every machine composes with the change as it did without (4 of 4 compose) (modules: build-agent, mesh-controller, route-proxy)
- `mesh/repo-check`: **PASS** — its merge-check.sh passed
**Delivery plan** — builds build-agent, mesh-controller, route-proxy → ace, g14, novox, shanks; no bus step
- tier 0: mesh-controller
- tier 1: build-agent
- tier 2: route-proxy
- ace: receives build-agent, route-proxy
- g14: receives build-agent
- novox: receives mesh-controller, build-agent, route-proxy
- shanks: receives build-agent
- build-agent keeps data (ADR 0233): what it holds is backed up before it moves
- route-proxy provides route: its consumers are sent again after it
- route-proxy keeps data (ADR 0233): what it holds is backed up before it moves
Run by the build seat on g14 as `build-1791467365220841250` (`builds --log build-1791467365220841250`).
```
--- the gate: build-agent, mesh-controller, route-proxy
mesh-controller: ok, 50 tool(s); WARNING: controller stay(s) up and say(s) not how it is ready — judged by liveness alone, refused from 2026-11-18 (ADR 0240 rule 8)
long-running resources without health: 1
1 manifest(s) checked. Judged against the seats this binary carries; a claim on one of the mesh's own seats is judged fully at registration, and a seat declared by a module not given here reads as unknown. Identities judged on a 6-character machine name, against the bounds of the providers given here
judging 0 module(s) changed against 4 machine(s), as the snapshot of 2026-10-08T13:48:06Z says they run
composing every machine as the mesh is
the bus's user list leaves out 5 user(s) the mesh has minted no credential for: controller, node.f35, node.idt, node.ygcyr, node.yvbpsm. Each is a user that cannot connect until one is issued
the bus's user list leaves out 5 user(s) the mesh has minted no credential for: controller, node.f35, node.idt, node.ygcyr, node.yvbpsm. Each is a user that cannot connect until one is issued
composing every machine with the change
the bus's user list leaves out 5 user(s) the mesh has minted no credential for: controller, node.f35, node.idt, node.ygcyr, node.yvbpsm. Each is a user that cannot connect until one is issued
the bus's user list leaves out 5 user(s) the mesh has minted no credential for: controller, node.f35, node.idt, node.ygcyr, node.yvbpsm. Each is a user that cannot connect until one is issued
merge gate: PASS — every machine composes with the change as it did without (4 of 4 compose)
judged by controller development build against the facts of 2026-10-08T13:48:06Z
machines:
- a machine (f35): composes
- holds mesh-dns-resolver (idt): composes
- holds git, holds mesh-artifact-store, holds mesh-broker, holds mesh-catalog, holds mesh-delivery, holds mesh-dns-resolver, holds mesh-store, holds mesh-vault, holds npm-package-registry, the control node, the hub (ygcyr): composes
- a machine (yvbpsm): composes
a merge would rebuild 3 module(s) in 3 tier(s): build-agent, mesh-controller, route-proxy
--- its merge-check.sh (go toolchain)
ok github.com/novox/mesh-controller/cmd/mesh-builder 1.035s
ok github.com/novox/mesh-controller/cmd/mesh-controller 169.187s
ok github.com/novox/mesh-controller/examples/objectstore-provisioner 1.014s
ok github.com/novox/mesh-controller/examples/postgres-provisioner 1.019s
ok github.com/novox/mesh-controller/examples/redis-provisioner 1.014s
ok github.com/novox/mesh-controller/examples/route-proxy 1.239s
ok github.com/novox/mesh-controller/internal/artifacts 1.050s
ok github.com/novox/mesh-controller/internal/broker 2.755s
ok github.com/novox/mesh-controller/internal/builder 2.601s
ok github.com/novox/mesh-controller/internal/catalogue 4.709s
ok github.com/novox/mesh-controller/internal/conditions 2.245s
ok github.com/novox/mesh-controller/internal/envfile 1.024s
ok github.com/novox/mesh-controller/internal/facts 1.032s
ok github.com/novox/mesh-controller/internal/identity 5.347s
ok github.com/novox/mesh-controller/internal/inventory 74.800s
ok github.com/novox/mesh-controller/internal/lease 17.667s
ok github.com/novox/mesh-controller/internal/licences 4.947s
ok github.com/novox/mesh-controller/internal/licences/adapters 1.010s
ok github.com/novox/mesh-controller/internal/link 44.575s
ok github.com/novox/mesh-controller/internal/lint 2.205s
ok github.com/novox/mesh-controller/internal/outward 1.046s
ok github.com/novox/mesh-controller/internal/overlay 1.036s
ok github.com/novox/mesh-controller/internal/secrets 1.605s
ok github.com/novox/mesh-controller/internal/store 3.459s
ok github.com/novox/mesh-controller/internal/testbus 1.195s
ok github.com/novox/mesh-controller/internal/token 1.014s
```
build-agent keeps data (ADR 0233): what it holds is backed up before it moves
route-proxy provides route: its consumers are sent again after it
route-proxy keeps data (ADR 0233): what it holds is backed up before it moves
Run by the build seat on ace as build-1791469340723530258 (builds --log build-1791469340723530258).
--- the gate: build-agent, mesh-controller, route-proxy
mesh-controller: ok, 51 tool(s); WARNING: controller stay(s) up and say(s) not how it is ready — judged by liveness alone, refused from 2026-11-18 (ADR 0240 rule 8)
long-running resources without health: 1
1 manifest(s) checked. Judged against the seats this binary carries; a claim on one of the mesh's own seats is judged fully at registration, and a seat declared by a module not given here reads as unknown. Identities judged on a 6-character machine name, against the bounds of the providers given here
judging 0 module(s) changed against 4 machine(s), as the snapshot of 2026-10-08T14:20:42Z says they run
composing every machine as the mesh is
the bus's user list leaves out 5 user(s) the mesh has minted no credential for: controller, node.f35, node.idt, node.ygcyr, node.yvbpsm. Each is a user that cannot connect until one is issued
the bus's user list leaves out 5 user(s) the mesh has minted no credential for: controller, node.f35, node.idt, node.ygcyr, node.yvbpsm. Each is a user that cannot connect until one is issued
composing every machine with the change
the bus's user list leaves out 5 user(s) the mesh has minted no credential for: controller, node.f35, node.idt, node.ygcyr, node.yvbpsm. Each is a user that cannot connect until one is issued
the bus's user list leaves out 5 user(s) the mesh has minted no credential for: controller, node.f35, node.idt, node.ygcyr, node.yvbpsm. Each is a user that cannot connect until one is issued
merge gate: PASS — every machine composes with the change as it did without (4 of 4 compose)
judged by controller development build against the facts of 2026-10-08T14:20:42Z
machines:
- a machine (f35): composes
- holds mesh-dns-resolver (idt): composes
- holds git, holds mesh-artifact-store, holds mesh-broker, holds mesh-catalog, holds mesh-delivery, holds mesh-dns-resolver, holds mesh-store, holds mesh-vault, holds npm-package-registry, the control node, the hub (ygcyr): composes
- a machine (yvbpsm): composes
a merge would rebuild 3 module(s) in 3 tier(s): build-agent, mesh-controller, route-proxy
--- its merge-check.sh (go toolchain)
ok github.com/novox/mesh-controller/cmd/mesh-builder 1.041s
ok github.com/novox/mesh-controller/cmd/mesh-controller 158.532s
ok github.com/novox/mesh-controller/examples/objectstore-provisioner 1.013s
ok github.com/novox/mesh-controller/examples/postgres-provisioner 1.016s
ok github.com/novox/mesh-controller/examples/redis-provisioner 1.013s
ok github.com/novox/mesh-controller/examples/route-proxy 1.155s
ok github.com/novox/mesh-controller/internal/artifacts 1.033s
ok github.com/novox/mesh-controller/internal/broker 2.443s
ok github.com/novox/mesh-controller/internal/builder 1.784s
ok github.com/novox/mesh-controller/internal/catalogue 4.039s
ok github.com/novox/mesh-controller/internal/conditions 2.169s
ok github.com/novox/mesh-controller/internal/envfile 1.056s
ok github.com/novox/mesh-controller/internal/facts 1.085s
ok github.com/novox/mesh-controller/internal/identity 4.128s
ok github.com/novox/mesh-controller/internal/inventory 70.501s
ok github.com/novox/mesh-controller/internal/lease 17.722s
ok github.com/novox/mesh-controller/internal/licences 3.748s
ok github.com/novox/mesh-controller/internal/licences/adapters 1.083s
ok github.com/novox/mesh-controller/internal/link 43.838s
ok github.com/novox/mesh-controller/internal/lint 1.940s
ok github.com/novox/mesh-controller/internal/outward 1.077s
ok github.com/novox/mesh-controller/internal/overlay 1.067s
ok github.com/novox/mesh-controller/internal/secrets 1.442s
ok github.com/novox/mesh-controller/internal/store 3.256s
ok github.com/novox/mesh-controller/internal/testbus 1.248s
ok github.com/novox/mesh-controller/internal/token 1.018s
**Merge check** at `dd78357b`
- `mesh/merge-gate`: **PASS** — every machine composes with the change as it did without (4 of 4 compose) (modules: build-agent, mesh-controller, route-proxy)
- `mesh/repo-check`: **PASS** — its merge-check.sh passed
**Delivery plan** — builds build-agent, mesh-controller, route-proxy → ace, g14, novox, shanks; no bus step
- tier 0: mesh-controller
- tier 1: build-agent
- tier 2: route-proxy
- ace: receives build-agent, route-proxy
- g14: receives build-agent
- novox: receives mesh-controller, build-agent, route-proxy
- shanks: receives build-agent
- build-agent keeps data (ADR 0233): what it holds is backed up before it moves
- route-proxy provides route: its consumers are sent again after it
- route-proxy keeps data (ADR 0233): what it holds is backed up before it moves
Run by the build seat on ace as `build-1791469340723530258` (`builds --log build-1791469340723530258`).
```
--- the gate: build-agent, mesh-controller, route-proxy
mesh-controller: ok, 51 tool(s); WARNING: controller stay(s) up and say(s) not how it is ready — judged by liveness alone, refused from 2026-11-18 (ADR 0240 rule 8)
long-running resources without health: 1
1 manifest(s) checked. Judged against the seats this binary carries; a claim on one of the mesh's own seats is judged fully at registration, and a seat declared by a module not given here reads as unknown. Identities judged on a 6-character machine name, against the bounds of the providers given here
judging 0 module(s) changed against 4 machine(s), as the snapshot of 2026-10-08T14:20:42Z says they run
composing every machine as the mesh is
the bus's user list leaves out 5 user(s) the mesh has minted no credential for: controller, node.f35, node.idt, node.ygcyr, node.yvbpsm. Each is a user that cannot connect until one is issued
the bus's user list leaves out 5 user(s) the mesh has minted no credential for: controller, node.f35, node.idt, node.ygcyr, node.yvbpsm. Each is a user that cannot connect until one is issued
composing every machine with the change
the bus's user list leaves out 5 user(s) the mesh has minted no credential for: controller, node.f35, node.idt, node.ygcyr, node.yvbpsm. Each is a user that cannot connect until one is issued
the bus's user list leaves out 5 user(s) the mesh has minted no credential for: controller, node.f35, node.idt, node.ygcyr, node.yvbpsm. Each is a user that cannot connect until one is issued
merge gate: PASS — every machine composes with the change as it did without (4 of 4 compose)
judged by controller development build against the facts of 2026-10-08T14:20:42Z
machines:
- a machine (f35): composes
- holds mesh-dns-resolver (idt): composes
- holds git, holds mesh-artifact-store, holds mesh-broker, holds mesh-catalog, holds mesh-delivery, holds mesh-dns-resolver, holds mesh-store, holds mesh-vault, holds npm-package-registry, the control node, the hub (ygcyr): composes
- a machine (yvbpsm): composes
a merge would rebuild 3 module(s) in 3 tier(s): build-agent, mesh-controller, route-proxy
--- its merge-check.sh (go toolchain)
ok github.com/novox/mesh-controller/cmd/mesh-builder 1.041s
ok github.com/novox/mesh-controller/cmd/mesh-controller 158.532s
ok github.com/novox/mesh-controller/examples/objectstore-provisioner 1.013s
ok github.com/novox/mesh-controller/examples/postgres-provisioner 1.016s
ok github.com/novox/mesh-controller/examples/redis-provisioner 1.013s
ok github.com/novox/mesh-controller/examples/route-proxy 1.155s
ok github.com/novox/mesh-controller/internal/artifacts 1.033s
ok github.com/novox/mesh-controller/internal/broker 2.443s
ok github.com/novox/mesh-controller/internal/builder 1.784s
ok github.com/novox/mesh-controller/internal/catalogue 4.039s
ok github.com/novox/mesh-controller/internal/conditions 2.169s
ok github.com/novox/mesh-controller/internal/envfile 1.056s
ok github.com/novox/mesh-controller/internal/facts 1.085s
ok github.com/novox/mesh-controller/internal/identity 4.128s
ok github.com/novox/mesh-controller/internal/inventory 70.501s
ok github.com/novox/mesh-controller/internal/lease 17.722s
ok github.com/novox/mesh-controller/internal/licences 3.748s
ok github.com/novox/mesh-controller/internal/licences/adapters 1.083s
ok github.com/novox/mesh-controller/internal/link 43.838s
ok github.com/novox/mesh-controller/internal/lint 1.940s
ok github.com/novox/mesh-controller/internal/outward 1.077s
ok github.com/novox/mesh-controller/internal/overlay 1.067s
ok github.com/novox/mesh-controller/internal/secrets 1.442s
ok github.com/novox/mesh-controller/internal/store 3.256s
ok github.com/novox/mesh-controller/internal/testbus 1.248s
ok github.com/novox/mesh-controller/internal/token 1.018s
```
A merge asks for a new module's build, and assign answered "no module of that
name" until the build registered it, which read as a module nobody registered
(hq issue 325). Keep every build request, tell a build in flight, a module
known and not built, and an unknown name apart, and make an assignment made
while the build runs when the build registers the module.
Review of #150: a withdrawal could land between the look and the act, a
failed ask read as a build in flight, a request kept the wrong asker, a
build being registered read as not built, build "true" could ask a build
nothing waited on, and a status read changed state. Claim a row under the
machine's hold before making it, keep a request only once asked, settle on
the controller's own tick, raise an assignment not made as a condition
until it is answered, and tie each row to its machine.
mesh/merge-gate pass: builds build-agent, mesh-controller, route-proxy → ace, g14, novox, shanks; no bus step; every machine composes with the change as it…
Second review of #150: one key per machine and module let a newer failure
be cleared in the tick that raised it, pruning could orphan an open
condition, and a build no longer waited for read as never asked although it
may still run.
Second review addressed at 8adb7f1, rebased on main fe00fec (#151); 0082 still directly follows 0081.
A: the condition id is <node>.<module>.<row id>, one condition per pending assignment; a newer row answers an older one only while it is open or once it was made. unassign takes back every unanswered row of that module on that machine. TestASecondAssignmentNotMadeKeepsItsCondition follows the review's sequence; it fails on the old key and rule with "the second assignment's condition was cleared in the tick that raised it".
B:ForgetEndedPending keeps a row raised_at is null or cleared_at is not null only. A removed machine takes its rows (foreign key) and the tick clears any of their conditions still open (clearOrphaned). No verb removes a machine yet, so the test removes it as the store would. TestARaisedRowIsKeptUntilItsConditionClears.
Stuck recovery:TestAStaleClaimIsSettledFromWhatTheMeshHolds, both branches, and a fresh claim left alone.
Waited build that times out:link.ErrNotHandedOver now marks a Submit that failed before the hand-over; only that is marked not asked. Any later failure is outcome_unknown (asked, outcome unknown), read as in flight until its outcome or 2 h. TestABuildNoLongerWaitedForIsStillInFlight.
sh merge-check.sh: exit 0 locally. R325 re-pointed in mesh-lab#68 and proved again.
Second review addressed at 8adb7f1, rebased on main fe00fec (#151); 0082 still directly follows 0081.
- **A:** the condition id is `<node>.<module>.<row id>`, one condition per pending assignment; a newer row answers an older one only while it is open or once it was made. `unassign` takes back every unanswered row of that module on that machine. `TestASecondAssignmentNotMadeKeepsItsCondition` follows the review's sequence; it fails on the old key and rule with "the second assignment's condition was cleared in the tick that raised it".
- **B:** `ForgetEndedPending` keeps a row `raised_at is null or cleared_at is not null` only. A removed machine takes its rows (foreign key) and the tick clears any of their conditions still open (`clearOrphaned`). No verb removes a machine yet, so the test removes it as the store would. `TestARaisedRowIsKeptUntilItsConditionClears`.
- **Stuck recovery:** `TestAStaleClaimIsSettledFromWhatTheMeshHolds`, both branches, and a fresh claim left alone.
- **Waited build that times out:** `link.ErrNotHandedOver` now marks a Submit that failed before the hand-over; only that is marked not asked. Any later failure is `outcome_unknown` (asked, outcome unknown), read as in flight until its outcome or 2 h. `TestABuildNoLongerWaitedForIsStillInFlight`.
`sh merge-check.sh`: exit 0 locally. R325 re-pointed in mesh-lab#68 and proved again.
build-agent keeps data (ADR 0233): what it holds is backed up before it moves
route-proxy provides route: its consumers are sent again after it
route-proxy keeps data (ADR 0233): what it holds is backed up before it moves
Run by the build seat on shanks as build-1791470759146112587 (builds --log build-1791470759146112587).
--- the gate: build-agent, mesh-controller, route-proxy
mesh-controller: ok, 51 tool(s); WARNING: controller stay(s) up and say(s) not how it is ready — judged by liveness alone, refused from 2026-11-18 (ADR 0240 rule 8)
long-running resources without health: 1
1 manifest(s) checked. Judged against the seats this binary carries; a claim on one of the mesh's own seats is judged fully at registration, and a seat declared by a module not given here reads as unknown. Identities judged on a 6-character machine name, against the bounds of the providers given here
judging 0 module(s) changed against 4 machine(s), as the snapshot of 2026-10-08T14:40:42Z says they run
composing every machine as the mesh is
the bus's user list leaves out 5 user(s) the mesh has minted no credential for: controller, node.f35, node.idt, node.ygcyr, node.yvbpsm. Each is a user that cannot connect until one is issued
the bus's user list leaves out 5 user(s) the mesh has minted no credential for: controller, node.f35, node.idt, node.ygcyr, node.yvbpsm. Each is a user that cannot connect until one is issued
composing every machine with the change
the bus's user list leaves out 5 user(s) the mesh has minted no credential for: controller, node.f35, node.idt, node.ygcyr, node.yvbpsm. Each is a user that cannot connect until one is issued
the bus's user list leaves out 5 user(s) the mesh has minted no credential for: controller, node.f35, node.idt, node.ygcyr, node.yvbpsm. Each is a user that cannot connect until one is issued
merge gate: PASS — every machine composes with the change as it did without (4 of 4 compose)
judged by controller development build against the facts of 2026-10-08T14:40:42Z
machines:
- a machine (f35): composes
- holds mesh-dns-resolver (idt): composes
- holds git, holds mesh-artifact-store, holds mesh-broker, holds mesh-catalog, holds mesh-delivery, holds mesh-dns-resolver, holds mesh-store, holds mesh-vault, holds npm-package-registry, the control node, the hub (ygcyr): composes
- a machine (yvbpsm): composes
a merge would rebuild 3 module(s) in 3 tier(s): build-agent, mesh-controller, route-proxy
--- its merge-check.sh (go toolchain)
ok github.com/novox/mesh-controller/cmd/mesh-builder 1.031s
ok github.com/novox/mesh-controller/cmd/mesh-controller 184.593s
ok github.com/novox/mesh-controller/examples/objectstore-provisioner 1.021s
ok github.com/novox/mesh-controller/examples/postgres-provisioner 1.031s
ok github.com/novox/mesh-controller/examples/redis-provisioner 1.012s
ok github.com/novox/mesh-controller/examples/route-proxy 1.182s
ok github.com/novox/mesh-controller/internal/artifacts 1.076s
ok github.com/novox/mesh-controller/internal/broker 2.628s
ok github.com/novox/mesh-controller/internal/builder 1.941s
ok github.com/novox/mesh-controller/internal/catalogue 4.761s
ok github.com/novox/mesh-controller/internal/conditions 2.218s
ok github.com/novox/mesh-controller/internal/envfile 1.053s
ok github.com/novox/mesh-controller/internal/facts 1.060s
ok github.com/novox/mesh-controller/internal/identity 4.799s
ok github.com/novox/mesh-controller/internal/inventory 84.445s
ok github.com/novox/mesh-controller/internal/lease 17.710s
ok github.com/novox/mesh-controller/internal/licences 4.283s
ok github.com/novox/mesh-controller/internal/licences/adapters 1.052s
ok github.com/novox/mesh-controller/internal/link 46.299s
ok github.com/novox/mesh-controller/internal/lint 2.148s
ok github.com/novox/mesh-controller/internal/outward 1.078s
ok github.com/novox/mesh-controller/internal/overlay 1.078s
ok github.com/novox/mesh-controller/internal/secrets 1.522s
ok github.com/novox/mesh-controller/internal/store 3.463s
ok github.com/novox/mesh-controller/internal/testbus 1.237s
ok github.com/novox/mesh-controller/internal/token 1.015s
**Merge check** at `8adb7f1a`
- `mesh/merge-gate`: **PASS** — every machine composes with the change as it did without (4 of 4 compose) (modules: build-agent, mesh-controller, route-proxy)
- `mesh/repo-check`: **PASS** — its merge-check.sh passed
**Delivery plan** — builds build-agent, mesh-controller, route-proxy → ace, g14, novox, shanks; no bus step
- tier 0: mesh-controller
- tier 1: build-agent
- tier 2: route-proxy
- ace: receives build-agent, route-proxy
- g14: receives build-agent
- novox: receives mesh-controller, build-agent, route-proxy
- shanks: receives build-agent
- build-agent keeps data (ADR 0233): what it holds is backed up before it moves
- route-proxy provides route: its consumers are sent again after it
- route-proxy keeps data (ADR 0233): what it holds is backed up before it moves
Run by the build seat on shanks as `build-1791470759146112587` (`builds --log build-1791470759146112587`).
```
--- the gate: build-agent, mesh-controller, route-proxy
mesh-controller: ok, 51 tool(s); WARNING: controller stay(s) up and say(s) not how it is ready — judged by liveness alone, refused from 2026-11-18 (ADR 0240 rule 8)
long-running resources without health: 1
1 manifest(s) checked. Judged against the seats this binary carries; a claim on one of the mesh's own seats is judged fully at registration, and a seat declared by a module not given here reads as unknown. Identities judged on a 6-character machine name, against the bounds of the providers given here
judging 0 module(s) changed against 4 machine(s), as the snapshot of 2026-10-08T14:40:42Z says they run
composing every machine as the mesh is
the bus's user list leaves out 5 user(s) the mesh has minted no credential for: controller, node.f35, node.idt, node.ygcyr, node.yvbpsm. Each is a user that cannot connect until one is issued
the bus's user list leaves out 5 user(s) the mesh has minted no credential for: controller, node.f35, node.idt, node.ygcyr, node.yvbpsm. Each is a user that cannot connect until one is issued
composing every machine with the change
the bus's user list leaves out 5 user(s) the mesh has minted no credential for: controller, node.f35, node.idt, node.ygcyr, node.yvbpsm. Each is a user that cannot connect until one is issued
the bus's user list leaves out 5 user(s) the mesh has minted no credential for: controller, node.f35, node.idt, node.ygcyr, node.yvbpsm. Each is a user that cannot connect until one is issued
merge gate: PASS — every machine composes with the change as it did without (4 of 4 compose)
judged by controller development build against the facts of 2026-10-08T14:40:42Z
machines:
- a machine (f35): composes
- holds mesh-dns-resolver (idt): composes
- holds git, holds mesh-artifact-store, holds mesh-broker, holds mesh-catalog, holds mesh-delivery, holds mesh-dns-resolver, holds mesh-store, holds mesh-vault, holds npm-package-registry, the control node, the hub (ygcyr): composes
- a machine (yvbpsm): composes
a merge would rebuild 3 module(s) in 3 tier(s): build-agent, mesh-controller, route-proxy
--- its merge-check.sh (go toolchain)
ok github.com/novox/mesh-controller/cmd/mesh-builder 1.031s
ok github.com/novox/mesh-controller/cmd/mesh-controller 184.593s
ok github.com/novox/mesh-controller/examples/objectstore-provisioner 1.021s
ok github.com/novox/mesh-controller/examples/postgres-provisioner 1.031s
ok github.com/novox/mesh-controller/examples/redis-provisioner 1.012s
ok github.com/novox/mesh-controller/examples/route-proxy 1.182s
ok github.com/novox/mesh-controller/internal/artifacts 1.076s
ok github.com/novox/mesh-controller/internal/broker 2.628s
ok github.com/novox/mesh-controller/internal/builder 1.941s
ok github.com/novox/mesh-controller/internal/catalogue 4.761s
ok github.com/novox/mesh-controller/internal/conditions 2.218s
ok github.com/novox/mesh-controller/internal/envfile 1.053s
ok github.com/novox/mesh-controller/internal/facts 1.060s
ok github.com/novox/mesh-controller/internal/identity 4.799s
ok github.com/novox/mesh-controller/internal/inventory 84.445s
ok github.com/novox/mesh-controller/internal/lease 17.710s
ok github.com/novox/mesh-controller/internal/licences 4.283s
ok github.com/novox/mesh-controller/internal/licences/adapters 1.052s
ok github.com/novox/mesh-controller/internal/link 46.299s
ok github.com/novox/mesh-controller/internal/lint 2.148s
ok github.com/novox/mesh-controller/internal/outward 1.078s
ok github.com/novox/mesh-controller/internal/overlay 1.078s
ok github.com/novox/mesh-controller/internal/secrets 1.522s
ok github.com/novox/mesh-controller/internal/store 3.463s
ok github.com/novox/mesh-controller/internal/testbus 1.237s
ok github.com/novox/mesh-controller/internal/token 1.015s
```
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
hq issue 325 and ADR 0261 (novox/hq#214). Replay R325: novox/mesh-lab#68.
Rebased on main
056414b(after #144). Migration 0082 follows #144's 0081 (which only createsartifact_mirrored); no other open branch holds a migration above 0081. 0082 has never run on the live store, so the review's schema changes are made in it, not in a new number.Why
On 2026-10-08
assign g14 sensorsanswered "no module of that name" a minute after the merge that asked for the module's build (issue 300); minutes later it worked. The controller kept no record of a build it asked for until the outcome came back.What changes
build_request): kept once the ask is made, never before, with the real asker (merge,plan,build,assign,rebuild,replay); a request not handed over or not waited for is marked not-asked, unless its outcome was heard. Kept 30 days.assignof a module the catalogue does not hold says its case: in flight (no outcome within 2 h, or built within 5 min and being registered) keeps a pending assignment; known, not built refuses and offersbuildorbuild: "true"; unknown refuses, claiming only what it checked (the catalogue and the kept requests), with the closest names.build: "true"asks from the last request's source; a pending assignment already waiting is re-pointed to the new build, and no build is asked that nothing waits on.waitingtoapplying), the ordinaryassignact runs, then the final state is set.unassignof a claimed row is refused, never overridden. A claim left by a stopped controller is settled by the tick from what the mesh holds.settlingPending, started inserve) plus each build's outcome.status, the board, the summary and the probe only read.assignment-not-made(warning, operator), with plain words; it clears when the module is assigned there later, assigned again, or taken back withunassign.statusis not well only while a pending assignment is open; the condition carries the rest.Pending(zero)isstate = 'waiting';pending_assignment.nodeis a foreign key tonodewithon delete cascade.assignsends nothing: its note sayspush <node>sends it. No claim about the upgrade announcement remains.Tests
pending_test.go: in flight (answer, askermerge, status + JSON, build applying it); a status read settles nothing and the tick does; failed build expiring, its condition raised, read as known-not-built, cleared byunassign; cleared by a later assignment;build: "true"(askerassign); re-pointing a waiting row with one ask; a failed ask and a request not waited for never in flight, an outcome heard after still the last word; a plan's request kept asplan; a build being registered in flight, then expired past the grace; unknown wording; several-module act refused whole; withdraw then build makes nothing; a claim refused to unassign,Pending(zero)excluding it, a stale claim released or applied; silent build expiring; ended rows deleted after 30 days; seatbuild; plain words.internal/inventory/pending_test.go: a row goes with its machine.TestReplay325.sh merge-check.sh: exit 0 locally.Do not merge without the operator's approval.
Delivery
novox/mesh-controller@8adb7f1a0519— superseded since 2026-10-08T15:09:41ZDelivery plan — builds build-agent, mesh-controller, route-proxy → ace, g14, novox, shanks; no bus step
Group
fix/assign-says-why-a-module-is-not-there, in order: novox/mesh-controller@8adb7f1a05 → novox/mesh-lab@33e63ee5b4Machines
Transitions
The commit's note under
refs/notes/mesh-plankeeps every transition:git log --notes=mesh-plan.Correction to the description's migration note: the store refuses a migration numbered below one already applied (
store.Pending). So whichever of #144 (0081) and this PR (0082) merges second must be the higher number: if this merges first, #144 renumbers its 0081 to 0083 before it merges; if #144 merges first, nothing changes here.Merge check at
b93dea8cmesh/merge-gate: PASS — every machine composes with the change as it did without (4 of 4 compose) (modules: build-agent, mesh-controller, route-proxy)mesh/repo-check: PASS — its merge-check.sh passedDelivery plan — builds build-agent, mesh-controller, route-proxy → ace, g14, novox, shanks; no bus step
Run by the build seat on g14 as
build-1791467365220841250(builds --log build-1791467365220841250).b93dea8c18todd78357b9bMerge check at
dd78357bmesh/merge-gate: PASS — every machine composes with the change as it did without (4 of 4 compose) (modules: build-agent, mesh-controller, route-proxy)mesh/repo-check: PASS — its merge-check.sh passedDelivery plan — builds build-agent, mesh-controller, route-proxy → ace, g14, novox, shanks; no bus step
Run by the build seat on ace as
build-1791469340723530258(builds --log build-1791469340723530258).dd78357b9bto8adb7f1a05Second review addressed at
8adb7f1, rebased on mainfe00fec(#151); 0082 still directly follows 0081.<node>.<module>.<row id>, one condition per pending assignment; a newer row answers an older one only while it is open or once it was made.unassigntakes back every unanswered row of that module on that machine.TestASecondAssignmentNotMadeKeepsItsConditionfollows the review's sequence; it fails on the old key and rule with "the second assignment's condition was cleared in the tick that raised it".ForgetEndedPendingkeeps a rowraised_at is null or cleared_at is not nullonly. A removed machine takes its rows (foreign key) and the tick clears any of their conditions still open (clearOrphaned). No verb removes a machine yet, so the test removes it as the store would.TestARaisedRowIsKeptUntilItsConditionClears.TestAStaleClaimIsSettledFromWhatTheMeshHolds, both branches, and a fresh claim left alone.link.ErrNotHandedOvernow marks a Submit that failed before the hand-over; only that is marked not asked. Any later failure isoutcome_unknown(asked, outcome unknown), read as in flight until its outcome or 2 h.TestABuildNoLongerWaitedForIsStillInFlight.sh merge-check.sh: exit 0 locally. R325 re-pointed in mesh-lab#68 and proved again.Merge check at
8adb7f1amesh/merge-gate: PASS — every machine composes with the change as it did without (4 of 4 compose) (modules: build-agent, mesh-controller, route-proxy)mesh/repo-check: PASS — its merge-check.sh passedDelivery plan — builds build-agent, mesh-controller, route-proxy → ace, g14, novox, shanks; no bus step
Run by the build seat on shanks as
build-1791470759146112587(builds --log build-1791470759146112587).