Files
hq/04-ISSUES/121-builders-real-package-registry-grant-deadlocks-genesis/01-diagnosis.md
T
jochen 9766a3afce Issue 121 diagnosed: the seats work renamed the requirement, not the order
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.
2026-09-26 14:47:45 +02:00

3.6 KiB
Raw Blame History

121 — Diagnosis

2026-09-26 — the seats work renamed the requirement; it did not reorder the bootstrap

The seats change landed the same day this was filed (ADRs 0109–0111 accepted, and the code on both main branches), and it touches exactly the two manifests this report names. That made it worth asking first whether the deadlock had been fixed in passing. It has not.

Step 1 — what the seats work changed

builder's requirement was renamed from package-registry to npm-package-registry, which is now a seat the forge holds; the forge claims npm-package-registry and git at mesh scope and serves both. The provision is the same relationship under a name the mesh defines rather than one a manifest invented.

Step 2 — the seat's holder cannot answer when there is no holder yet

The seat is consulted in exactly one branch of resolution: where several nodes provide the wanted provision and no pin says which. That is the ambiguity case, and it is not the bootstrap case. With nothing providing it at all, resolution takes the branch for no providers and refuses, naming the remedy:

nothing in this mesh provides "npm-package-registry", wanted by builder — assign gitea to a node

A refusal, not a deferral. So a seat delivering a provision does not make a requirement optional before the seat is filled, and nothing about the seats work moves the order.

Step 3 — ruled out: genesis has no escape hatch

Resolution has an Unchecked mode that "takes brokered requirements on trust instead of refusing when nothing answers them", which would be exactly the exemption a bootstrap needs. It is not one: it exists for the first of two passes — working out what each node offers — and its own comment states that a declaration is never built from an unchecked resolution. The second pass, the one that produces the declaration, refuses.

Step 4 — the other arm of the cycle is intact

The forge's manifest still carries a build section, its runtime image compiled from the toolchain's build and runtime bases like every other built module. builder is what runs that build. So both arms stand: the builder cannot be assigned before the forge provides the registry, and the forge cannot exist as a container before the builder has built its image.

What did change, and why it is the part worth recording

The three tests that pinned the carried-fallback design — the builder's own package-binding resource, its port taken from the node, its identity kept — were deleted by the controller's seats PR and replaced by seat-based tests that pass.

The report deliberately left those three failing so the gap would stay visible. It is no longer visible: the suite is green, and the only remaining trace is this issue. That is the same way the deadlock was invisible in the first place — a running mesh resolves the grant instantly, because the forge is already built and assigned. Twice now, the evidence has been removed by something correct.

What would actually answer it

  • A provision that resolves to a carried fallback when nothing provides it yet. Nothing in the model expresses "this, or a carried default until something can answer", for any provision. This would be the first, which is why it belongs in a record rather than in a manifest.
  • Genesis sequencing instead — build the forge before granting anything, with the builder never resolving the registry during that window.
  • The forge's runtime image not being the mesh's to build — an external image breaks the cycle by removing the dependency rather than by expressing it.

None is chosen here. The report's open questions stand as written.