Asked first whether the seats change fixed this in passing, since it landed the same day and touches both manifests the report names. It did not: the seat's holder is consulted only where several nodes provide the thing, and with none providing it resolution refuses outright. Genesis has no exemption — the unchecked first pass exists to learn what each node offers, and a declaration is never built from it. Records the part that did change: the three tests left failing on purpose were deleted by the controller's seats PR and replaced with passing seat-based ones, so the gap is invisible again. Adds the resolver to located-in, since that is where the refusal is.
4.9 KiB
status, opened, located-in, fixed-by, amended-design
| status | opened | located-in | fixed-by | amended-design | |||
|---|---|---|---|---|---|---|---|
| located | 2026-09-25 |
|
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-registryto 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/<owner>/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:
buildernowrequires: package-registry, satisfiable only bygitea.giteacannot run — cannot exist as a container at all — untilbuilderhas 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
buildershould carry both — its own default/fallback binding for when no real grant can be resolved yet, and a realrequires: package-registryfor when one can — and if so, what decides which one is live at a given moment. Nothing in this mesh'srequires/providesresolution currently expresses "this, or a carried fallback if it cannot resolve" for any provision; this would be the first. - Whether the fallback belongs on
builderspecifically (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 topackage-registryand could recur. - Whether genesis itself should sequence around this instead (build gitea before granting anything,
with
buildernever resolvingpackage-registryduring 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.