Files
hq/04-ISSUES/121-builders-real-package-registry-grant-deadlocks-genesis/01-diagnosis.md
T
jochen a1d8b478ec Issue 121: retract step 4 — the forge's service is not built
Step 4 claimed the forge cannot exist as a container before the builder has built its image. The
forge's server is an upstream public image pinned by digest; only its runtime sidecar is built. The
store and the broker have the same shape, and ADR 0078 raises both at genesis as plumbing and adopts
them in place — so the forge can be raised the same way and serve git, packages and OCI before
anything is built.

What survives: a grant is minted by the provider's runtime sidecar, which is built, so the question
is whether raise-service, grant, build-sidecar simply works. Sequencing inside the mesh, not images.

The wrong version came from taking a record's bootstrap argument at face value instead of comparing it
to how the store and broker are raised — one command away in the manifests.
2026-09-26 15:36:19 +02:00

95 lines
5.6 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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.
## 2026-09-26, later — Step 4 is retracted: the forge's service is not built
Step 4 said the forge's runtime image is compiled by the builder, so the forge cannot exist as a
container before the builder has built it. **That is not what the forge's definition says**, and it
took one command to disprove.
The forge's **server** is an upstream public image pinned by digest. What the mesh builds is the
forge's **runtime sidecar** — its own agent beside the service, a separate container. The store and
the broker have exactly the same shape: an upstream server image, a built sidecar. And that shape is
the point, because [ADR 0078](../../02-DECISIONS/0078-the-store-and-broker-are-modules.md) raises the
store and the broker at genesis as plumbing and **adopts them in place** as ordinary modules
afterwards. Nothing stops the forge being raised the same way: its service can be serving git,
packages and OCI before anything at all has been built.
So the second arm of the cycle, as this diagnosis stated it, does not exist.
**What survives is narrower and is still worth answering.** A grant — the credential the builder
would use against the package registry — is minted and delivered by the **provider's runtime
sidecar**, and that sidecar is built. So the question is whether a consumer can be granted a
provision whose provider's sidecar is not yet running, and whether the ordering
*raise the service → grant → build the sidecar* simply works. That is sequencing inside the mesh's
own machinery, not a chicken-and-egg about images, and it may not be a deadlock at all.
The report's open questions should be read against this. In particular, a carried fallback binding
may be answering a problem that adopt-in-place already solves.
**Why the error is recorded rather than edited away:** the wrong version was reached by taking a
record's bootstrap argument at face value instead of checking it against how the store and the broker
are actually raised — which is the one comparison the manifests make available in a single command.
## 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.