diff --git a/04-ISSUES/206-a-seats-worker-changing-type-strands-the-holder-and-the-build-that-would-fix-it/00-report.md b/04-ISSUES/206-a-seats-worker-changing-type-strands-the-holder-and-the-build-that-would-fix-it/00-report.md new file mode 100644 index 0000000..8afec45 --- /dev/null +++ b/04-ISSUES/206-a-seats-worker-changing-type-strands-the-holder-and-the-build-that-would-fix-it/00-report.md @@ -0,0 +1,57 @@ +--- +status: open +opened: 2026-10-03 +located-in: + - mesh-controller +fixed-by: +amended-design: +--- + +# 206 — A seat's worker changing type strands its holder, and the build that would fix it + +## What was observed + +2026-10-03, the switch to shared build work ([ADR 0190](../../02-DECISIONS/0190-a-seats-work-is-shared-by-its-holders-and-building-is-the-first-such-role.md)). +The controller change made a seat's worker a pull consumer and the build machine pull from it. The +merge's plan put the **build machine** in tier 0 and the controller in tier 1, so the new build machine +rolled first, onto a bus where the running controller had defined the worker as push: + +``` +mesh-builder: this machine cannot take work from mesh-build-machine: nats: cannot pull subscribe to +push based consumer. The mesh creates that queue and this machine's worker on it, and a build +machine may not create one +``` + +It restarted every few seconds. The old controller kept asking for tier 1 — the new controller image — +on that worker, and nothing took it. The only thing that would redefine the worker is the controller +that could not be built; there is no verb to run a module's previous build. The way out was a +person running the previous build machine image by hand on the control node until the new controller +had rolled, then removing it. + +Two smaller faults surfaced on the way and were each a step of the same handover: the build machine's +credential, issued on 2026-09-28, carried no `claims`, so the new binary's "serve the seat your +credential claims" fell back to the new seat it had no grant for (re-issuing the credential fixed it); +and the build machine's container restarts on its environment file, not on its credential, so the +re-issued credential reached it only because it was already restarting. + +## Why it matters beyond this instance + +A consumer's type is part of the contract between the controller that defines a worker and the holder +that binds it, and the two are built and rolled by different plans in an order the dependency graph +decides, not the contract. Any future change to a worker's shape — ack wait, filters, type — can strand +every holder the same way, and when the holder is the build machine, the mesh cannot build its way out. +[ADR 0162](../../02-DECISIONS/0162-a-merge-produces-a-tiered-plan-the-mesh-keeps.md) orders tiers by +artifacts; it says nothing about what must be *running* before what, and +[issue 201](../201-a-push-recreated-the-controller-behind-the-row-its-successor-wrote/00-report.md) +is the same gap seen from the controller's side. + +## Questions HQ must answer + +- Does the controller own the worker's shape fully — redefining an existing consumer to the shape it + derives on every start — or does a holder bind whatever shape it finds? Either answer must hold + across a handover where the two are at different versions. +- Should a plan that changes the build machine always roll the controller first, or should the mesh + keep a way to run a module's previous build without a person on the machine? +- A credential issued before claims existed names none: should the mesh re-issue credentials whose + shape is older than what the binary reads, or should every holder treat an unclaimed credential as + the seat its manifest claims?