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.
95 lines
5.6 KiB
Markdown
95 lines
5.6 KiB
Markdown
# 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.
|