--- status: located opened: 2026-09-25 located-in: [mesh-catalog modules/builder, mesh-catalog modules/gitea, mesh-controller internal/catalogue/resolve.go] fixed-by: amended-design: --- # 121 — `builder` requiring a real `package-registry` grant deadlocks a genesis bootstrap ## What was observed On the control-node, 2026-09-25, fixing `builder`'s hand-faked `package-registry` binding (a hardcoded JSON fragment standing in for a real grant, found live-blocking nothing but carrying no correctness at all — see the migration log's account of the same night). The fix declared `requires: ["artifact-store", "package-registry"]` on `builder` and let the mesh mint the grant properly, mirroring how `artifact-store` was already required. Running `mesh-controller`'s own test suite against the catalogue as changed surfaced three tests that pinned a different, deliberate design: ``` --- FAIL: TestTheBuildersCarriedPackageBindingTakesThePortFromTheNode --- FAIL: TestTheBuildersCarriedBindingStartsWhereTheForgeServes --- FAIL: TestTheBuildersPackageBindingKeepsItsIdentity ``` Read together with their own comment in `foundation_manifests_test.go`: > The builder carries a binding because at genesis nothing provides `package-registry` to resolve > one from; the day the forge is a module, the same consumer is told what the forge serves. The tests expect `builder` to carry its own `package-binding` resource — a `merge: json` file with `protected: [provision, from, at, as]`, settable only on `port` via the ordinary settings layers — matching defaults (`port: 3000`, `scheme: http`, `npm-path: /api/packages//npm/`, `as: mesh-builder`, `from: gitea`) that agree with what `gitea`'s own `serves.package-registry` declares. Tonight's fix removed that resource entirely. **The dependency this protects against is real, not hypothetical.** `gitea`'s own `module.json` carries a `build` section — its runtime image is compiled from a Dockerfile via `mesh-tools`'s build/runtime bases, the same as every other built module. `builder` is what runs that build. So: - `builder` now `requires: package-registry`, satisfiable only by `gitea`. - `gitea` cannot run — cannot exist as a container at all — until `builder` has built its image. On the control-node tonight this is invisible: the mesh is already running, `gitea` is already built and assigned, and the grant resolves immediately. A genesis bootstrap — a new mesh raised from nothing, or that node fully re-raised for disaster recovery — hits the order the tests describe: `builder` is needed to build `gitea`'s image before `gitea` can be assigned, so `builder` cannot yet hold a real `package-registry` grant, so (as tonight's fix has it) `builder` cannot start. ## Why it matters beyond this instance This is the same shape ADR 0075 already named for the mesh as a whole ("the bootstrap still has no package registry... that is the same pivot as everything else and it is not solved here") — but it now has a concrete second collision: the one module capable of building a package-registry provider's own image would refuse to run before that provider exists, precisely because it was made to depend on it correctly. It is also a near miss worth writing down for its own reason: the fix that broke this was reviewed against `go build`, the full test suite (which flagged it immediately — three tests, not zero), and matched an explicitly-stated target design in `22-the-work-ahead.md` written a week earlier. Nothing about the fix was careless; the deadlock was invisible because the node it ran against had already crossed the point where it would bite. ## What is not decided here - Whether `builder` should carry both — its own default/fallback binding for when no real grant can be resolved yet, and a real `requires: package-registry` for when one can — and if so, what decides which one is live at a given moment. Nothing in this mesh's `requires`/`provides` resolution currently expresses "this, or a carried fallback if it cannot resolve" for any provision; this would be the first. - Whether the fallback belongs on `builder` specifically (matching what the removed tests already encoded) or is a shape the mesh should offer any module bootstrapping a provider it also depends on — the "builder builds the thing it needs to ask a favour of" pattern is not unique to `package-registry` and could recur. - Whether genesis itself should sequence around this instead (build gitea before granting anything, with `builder` never resolving `package-registry` during that specific window) rather than the manifest carrying two shapes of one credential. Tonight's `builder` fix (`mesh-catalog modules/builder/module.json`) is left in place — correct for the steady state, live and working on the control-node — with the three tests above left failing rather than reverted or hacked to pass, so the gap stays visible rather than quietly patched over.