Design 51: answer the first review of the milestone amendment (builds counted from the records, the write side's window and outage, the revert's open points, holds and the old name's events, live proof)

Claude-Session: https://claude.ai/code/session_01JG73KJNrH5EgFXMKKYSrig
This commit is contained in:
2026-11-04 00:09:56 +01:00
parent 01a0a3a4d9
commit 2d48b85c0e
@@ -62,7 +62,12 @@ asks the status view's `node_haves` for every node, away ones included, and stop
whose declaration names an artifact has applied a build whose node-engine states the capability to read the new names
(ADR 0614 §4), judged by that stated capability and never by a fingerprint. Its tool `registry_old_name` lists the
nodes still holding it open, and drops a retired node from the window by hand, recorded as a hand act; a node holding
it open for seven days is a finding naming the node, cleared when the window closes. `cargo-package-registry` stays
it open for seven days is a finding naming the node, cleared when the window closes. Closing the old name emits
`old-name-closed` on both image seats' `<seat>.events`. A node away when the last `delivered` was said must close the
window when it returns and applies the new build, with no other delivery: the event that says such a node's return,
and the verb that answers each node's stated capabilities, are not yet named by any record or design (the stated
capability comes with the release that adds it, design 47); until they are, the window waits on the next `delivered`,
tracked on issue 1966. `cargo-package-registry` stays
named, without a holder, until a build module publishes cargo packages.
**One contract, a seat definition** (design 26, *A seat's definition*). Verbs: `publish`, `publish-part` (ADR 0611), `versions`, `remove` (a
@@ -131,13 +136,23 @@ the verb.
writes from any process on the mesh network, so the rule protects nothing until its write side is closed. So
`distribution`'s own program serves the store's port in front of the registry container, which listens on its
node's loopback only: reads (the OCI distribution protocol's GET and HEAD) stay open to every node's container
runtime, and a write reaches the store only from `distribution`'s own process, after a `publish` it accepted. Each
runtime, and a write reaches the store only from `distribution`'s own process, after a `publish` it accepted.
**Until every build module publishes through the seats**, the build modules' builds still running push straight to
the store, as they do today: so `distribution`'s program also accepts a write from a build module's own account
until every node's have of every build module, read through `node_haves` at its start and on each `delivered`, runs
a build that publishes through the seats; then it refuses them, naming `publish`. **While the program is down**,
the store answers no read and no write: a node pulling an image fails and retries, and what already runs is not
touched; the holder's health check, which probes the read side every minute, names it, and the program serves
again at once when it restarts, holding nothing but its transfers in `incoming`. Each
holder keeps its write side as an own secret beside a read-only tool token (the forge module: `write:package`, its
tool token `read:package`).
- **The package seats' read sides need no credential** for the mesh's own packages: the forge serves the mesh owner's
packages to anonymous reads, so the provisions `npm-package-registry` and `go-package-registry` carry no
credential (form none), and mesh-build-node, which held a pair for both writing and installing, requires none (the
operator's answer of 2026-11-03, that the mesh's packages may be read without a credential).
operator's answer of 2026-11-03, that the mesh's packages may be read without a credential). The scope is the
mesh network only: the store's GET and HEAD and the forge's package reads answer anonymously there, and through no
public route. Whether the forge has a public route that would also serve its package reads anonymously is not
known to this design, to confirm with the operator before the merge.
- **The forge module serves both package seats' verbs from its Go bundle**, the one that already serves its package
retention, renamed for both packages; its tools bundle in TypeScript is unchanged, so no holder needs generated
TypeScript types in this milestone.
@@ -161,7 +176,9 @@ the verb.
naming the merge it waits on. A fingerprint already numbered keeps its number.
- **A package is numbered per source revision**, not per fingerprint: a second trunk build of a revision already
numbered for that package (a build module's change reaching it by a second build, ADR 0603 §3, or the contracts
move's one rebuild) keeps the package's version and publishes nothing, since a package's content is its source.
move's one rebuild) keeps the package's version and publishes nothing, since a package's content is its source,
but only when `versions` holds that revision `published`; a revision numbered but never stored is published by its
next build under the same version. The build log says the version was kept, and why.
So a publish that triggers a rebuild of the package's own build module (the shared contracts, the forge module
that holds their registry and the Go build module that builds them, ADR 0614 §6) never comes round to a new
version, and the triggers end. An image, bundle or archive is numbered per fingerprint, as above.
@@ -178,9 +195,11 @@ the verb.
- **An unknown floor does not stall the build of an image, bundle or archive.** The floor of `distribution`'s own
names is `distribution`'s `versions`, which only its new build serves, and the same holds for the forge module and
the builder: waiting would never end. So a trunk build of an image, bundle or archive whose floor is not yet known
is built unnumbered and published by its digest (as a bundle or archive is from every done key, ADR 0605 §3); once
the floor answers, the builder numbers it, and the build module calls `publish` with the version and the held
digest, which stores nothing. A package's trunk build waits for its floor, since a package is published only under
is built unnumbered and published by its digest (as a bundle or archive is from every done key, ADR 0605 §3). That
publish is what wakes the floor: the builder hears the holder's `published` on its durable consumer of
`<seat>.events` and asks the floor again then, never on a timer; once all three answer it numbers the build and emits
`numbered`, which the build module consumes to call `publish` with the version and the held digest, which stores
nothing. A package's trunk build waits for its floor, since a package is published only under
its version.
- **Events of the builder**: `numbered` (name, version, tag, fingerprint, commit, head when it answers an ask), on its
own family, kept seven days; a waiter on a build's end does not wake on it. **Tools of the builder**:
@@ -210,7 +229,10 @@ the verb.
branch's end; retention may remove an old unreferenced one (below).
- **The forge bridge says a branch's end**: beside its `merged`, `refused`, `open` and `pull` kinds on its stream
`forge`, it says a branch-deleted kind, from the forge module's `forge.delete` event, which it consumes on a durable
consumer as it consumes the pull events. Its tool `bridge_tags` answers a package's git tags, for the floor.
consumer as it consumes the pull events. Only the forge module's account may publish `forge.delete`, the subject
being in its own namespace (design 32 § 2), so no other module can end a branch's tagged versions. Every existing
consumer of `forge` ignores a kind it does not know. Its tool `bridge_tags` answers a package's git tags, for the
floor.
### Retention
@@ -220,6 +242,10 @@ With the store's write side closed, the artifact sweep (seat `mesh-artifact-swee
longer deletes or holds by writing to the store: it calls `remove` on the seat that holds the thing, with the reason
retention, and `hold` to keep a kept archive from removal, both granted to its holder alone by `distribution`'s
caller list; its rules of what is kept (*The artifact store* below) stand, and it reads both reference forms.
`distribution` keeps what is held in its own state `holds` (bus state, template state), emits `held` and `released`
on the seat's `<seat>.events`, and its tool `registry_holds` lists what is held, since when and for whom, and
releases one by hand. A `remove` or `hold` the holder refuses to the sweep writes the sweep's finding, naming the
thing and the reason, cleared by its next run with no refusal.
The Go seat keeps what the npm seat keeps: every referenced version and the newest five of each package series. A
Go version is referenced when a `go.mod` on a repository's default branch requires it; the forge module reads every
`go.mod` as it reads lockfiles, and a `go.mod` it could not read stops a real run before anything is deleted, as an
@@ -251,6 +277,22 @@ its own rule; nothing stores the order (ADR 0612 §2, ADR 0614 §1). The edges t
| The holders' `incoming` and `<seat>.events` exist before the holders start | bus state: each entry is a requirement on the provision `bus-state` of the bus module (ADR 0614 §5) | the assigner, as for any provision |
| The shared contracts, the forge module that holds their registry and the build module that builds them | package and build module edges are triggers, never waits; the cycle they make holds nothing and is not refused (ADR 0614 §6); inside the group every member takes `mesh-contracts` and mesh-sdk by the group's head (the checker's group pin) | the indexer's graph |
**How many builds** (ADR 0603 §3). The three build modules change in this milestone, so every module they build is
built twice: at once with the recipe that runs, and again once a majority of build nodes runs the new build module.
The contracts move rides that first build and adds none (ADR 0613 §1). Whether the rebuild that a first numbered
publish of mesh-sdk or `mesh-contracts` triggers in their users (ADR 0614 §6) joins the second build or adds a third
is not decided by ADR 0603 or ADR 0614, so this design gives no count beyond two per module; it is put to the
operator.
**The node-engine's own build keeps the old reference form** until every node-engine states the capability to read
the new one: a node-engine away across the milestone must be able to read the build it wakes to, or it could never
update itself (ADR 0603 §7, §9). The builder writes the node-engine's done key in the old form until the stated
capabilities, which the status view reads, show it on every node.
**When the group is pushed.** Only once the release that adds the node-engine's stated capability (design 47) runs on
every node, read from the status view's haves before the push, and once the progressive insights of ADR 0595 and
ADR 0597 this design is written to are accepted.
**The milestone's own check** runs the changed build modules (ADR 0603 §10) against the running holders, which serve
no `publish`: a changed build module whose manifest invokes a verb no running build serves is one the check cannot
run, and the modules it builds are composed from their manifests, the verdict naming them and the verb (ADR 0614 §7).
@@ -267,7 +309,10 @@ guard go in the same change (ADR 0606 §6, read with ADR 0612 §1).
**Rolled back by one revert** (ADR 0612 §4): reverting the milestone's merge brings back the build modules'
manifests that require the write credentials, so the vault mints them again as own secrets on the revert's delivery,
and the builds that publish through the seats go with it. The old seat name still retires on the node-engines'
reports (ADR 0610 §4). **On a fresh mesh** the foundation's bootstrap (research 075) pushes the holders' own images
reports (ADR 0610 §4). Two things of the revert are not decided by any record and are tracked on issue 1966: which
edge delivers `distribution`'s reverted build, which takes direct writes again, before the build modules' reverted
builds that push straight to the store; and whether a done key written in the new form is built again after the
revert or must stay readable by the reverted readers, which the old mesh-sdk refuses. **On a fresh mesh** the foundation's bootstrap (research 075) pushes the holders' own images
with a one-off credential of the bootstrap, discarded once the holders serve `publish`.
### Acceptance tests
@@ -301,17 +346,30 @@ answers (a holder down whole is its module's own health check's); `registry_inco
- **Holder, added for the milestone.** A write to the store's port from any account but `distribution`'s process is
refused, and a read from a node's container runtime is answered; the artifact sweep's `remove` with the reason
retention removes an untagged bundle and emits `removed`, and the same call from another caller is refused; `hold`
keeps an archive from removal; the forge bridge says a branch-deleted kind for the forge module's `forge.delete`.
keeps an archive from removal, emits `held` and is listed by `registry_holds`, which releases it with `released`; a
refused sweep `remove` writes the sweep's finding and its next clean run clears it; the forge bridge says a
branch-deleted kind for the forge module's `forge.delete`, a `forge.delete` from another account is refused by the
bus, and an existing consumer of `forge` ignores the new kind; with the program stopped a read fails and the
holder's finding is written, and after its restart the read is answered and the finding cleared; a build module's
old build pushing straight to the store is accepted until every node runs a build module publishing through the
seats, and refused after.
- **Builder, added for the milestone.** With the seat's holder down at a first claim, a bundle's trunk build is built
unnumbered and published by its digest, and is numbered once the floor answers; a package's trunk build waits;
a second trunk build of a numbered package's revision keeps its version and publishes nothing.
unnumbered and published by its digest, the holder's `published` makes the builder ask the floor again and number
it, and the build module, hearing `numbered`, publishes the version with the held digest; a package's trunk build
waits; a second trunk build of a package's revision held `published` keeps its version and publishes nothing,
while one numbered but refused is published by its second build under the same version; with `bridge_tags`
silenced (a floor source other than the publish target) the floor finding is written and cleared once it answers.
- **Checker.** A changed build module invoking a verb no running holder serves is not run, and the verdict names the
modules composed from their manifests instead, and the verb.
- **Vault.** Reverting the milestone's merge mints the build modules' credentials again; the bootstrap's one-off
credential is gone once the holders serve `publish`.
- **Live.** The status view shows each build module's new build waiting on its holder's build serving `publish`
until that runs healthy (ADR 0612 *How it is checked*); no build module's provision key holds a write credential;
`registry_old_name` names the nodes holding the old name open, an away node among them.
`registry_old_name` names the nodes holding the old name open, an away node among them. After the delivery:
mesh-sdk's first numbered version is `published` on the Go seat and an image is read back by its digest, both
shown by `builder_versions`; a node runs a module from a reference in the new form; a node-engine away across the
milestone wakes and applies the new node-engine. Go's `direct`: its removal is named on the milestone's issue, and
the change that removes it adds to the Go build module's recipe test a case that refuses `direct` in the fetch.
## Reviewed by architectural domain