Designs 51, 26, 32: answer the first review by architectural domain (ADR 0596 paths, holder-side caller lists, the file in the publish verb, findings for the condition keeper, windows on node-engine reports)

Claude-Session: https://claude.ai/code/session_01JG73KJNrH5EgFXMKKYSrig
This commit is contained in:
2026-11-03 18:02:09 +01:00
parent 27063a492b
commit 20157fcafe
3 changed files with 180 additions and 101 deletions
+22 -14
View File
@@ -178,21 +178,27 @@ research 081). The registry seats (design 51) are the first defined this way, th
bodies, its state and the forms of its provisions. It lives in the contracts module of its area. Its Go and
TypeScript types are generated from it and committed beside it; nobody writes them by hand.
- **The generator** is each language's build module's: the Go build module's package for Go, the Node and TypeScript
build module's for TypeScript (the logic belongs to the module whose work it is). A check regenerates and fails,
naming the file, when a committed type differs.
- **On the bus**: the indexer reads every seat definition from every trunk and open pull request and keeps them in its
state `seat-definitions`, keyed by seat and source (the trunk, or a pull request's head), as their only writer, and
emits `seat-definition-changed`. A definition that does not parse fails its pull request's check; the trunk's
stays. The merge is the only approval. The foundation's own definitions are bootstrapped before the bus exists, as
the bootstrap of a fresh mesh has it (research 075).
- **Conditions**, each raised and cleared by the indexer: a seat definition that does not parse; generated types out
of step with their definition. **Tools** of the indexer: `indexer_seat_definition` (read one),
build module's for TypeScript (the logic is the module's whose work it is). The check of a change regenerates and
fails, naming the file, when a committed type differs; its verdict event names the out-of-step files, and that is
how the indexer learns of them. The indexer runs no generator.
- **On the bus**: the indexer reads every seat definition from every trunk and open pull request, from the forge
bridge's messages, and keeps them in its own state `seat-definitions`, keyed by seat and source (the trunk, or a
pull request's head), as their only writer; a definition is at most 256 KiB. It emits `seat-definition-changed`
(seat, source, digest), kept seven days. **Only the trunk's entry is the seat's contract**: the readers (the contracts
modules' builds, the tool runner's descriptions, the review) ask `indexer_seat_definition`, which answers the trunk's
unless a pull request's head is named, and says which it answered. A pull request's entries are removed on the forge
bridge's event that it closed or merged. A definition that does not parse fails its pull request's check; the
trunk's stays. The merge is the only approval. The foundation's own definitions are bootstrapped before the bus
exists, as the bootstrap of a fresh mesh has it (research 075).
- **Findings**, written by the indexer, from which the condition keeper raises and clears the conditions (ADR 0564): a
trunk's seat definition that does not parse (a pull request's is its check's verdict, not a condition); generated
types on a trunk out of step with their definition. **Tools** of the indexer: `indexer_seat_definition` (read one),
`indexer_seat_definitions_again` (read a trunk's or a pull request's again now) and `indexer_out_of_step` (the
generated types out of step).
- **Contracts modules.** A seat's contract is published by a contracts module, which holds contracts only (every file
generated from a seat definition) and no seat; the seat's holder implements it. The seat contracts mesh-sdk holds
move into `mesh-contracts` in one change, delivered with the first numbered publish (design 51, *The move*); an
area's contracts move into `mesh-<area>-contracts` when the area's contract next changes, and that change creates
move into `mesh-contracts` in one change, delivered with the first numbered publish (design 51, *The move*, step 2);
an area's contracts move into `mesh-<area>-contracts` when the area's contract next changes, and that change creates
the module. mesh-sdk keeps its name and the shared runtime. A catalog check refuses a contracts module holding a file
its generator did not write, and a seat-specific type in mesh-sdk once `mesh-contracts` publishes.
- **Older readers keep working**: a moved contract keeps its subjects, keys and message shapes, and a reader ignores
@@ -201,9 +207,11 @@ research 081). The registry seats (design 51) are the first defined this way, th
like any package; a later holder of the seat may publish its own the same way. A seat's definition stays
vendor-neutral, which the seams review checks.
Acceptance tests: research 081's 14, 21, 25 and 26, and an integration test of the indexer: a definition that does not
parse raises its condition and a fixed one clears it; a generated type changed by hand raises the out-of-step condition
and regenerating clears it; each of the three tools answers.
Acceptance tests: research 081's 14, 21, 25 and 26, and cases of the indexer's whole-module integration test: a trunk
definition that does not parse writes its finding and a fixed one clears it; a check verdict naming out-of-step types
writes the finding and a clean one clears it; `indexer_seat_definition` answers the trunk's entry while a pull request
holds another; a pull request's entries go when it closes; a definition over 256 KiB is refused; each of the three
tools answers.
## The foundation's seats
+32 -17
View File
@@ -481,40 +481,55 @@ complete, because a team that believes it is complete stops reviewing for the ca
way; one that declares none is numbered in `0.1`. Nobody types a version: the builder counts it (design 51).
- **Raising a package series** is a reviewed change of the package's manifest, for a change that breaks its users.
The checker's verdict names every change of a package series it sees, so the review of the change sees it.
- **A user names, in each `uses` of a mesh package, the package series it accepts.** It builds, and is checked,
against the newest version of that series; a publish in another series does not move it, and moving is a reviewed
change of the user's manifest. A user that names none accepts the series of the package's first numbered version,
so nothing is edited in today's users.
- **A user names the mesh packages it takes in a section of its own, `packages`**, each with the package series it
accepts. It is not `uses`, which grants publishing on a seat's `accepts` subjects (§ 3); a package is not a seat. A
user builds, and is checked, against the newest version of that series; a publish in another series does not move
it, and moving is a reviewed change of the user's manifest. A user that names none accepts the series of the
package's first numbered version, so nothing is edited in today's users. The indexer's tool `indexer_package_users`
lists, per package, its users and the series each accepts, so a user left on an older series is seen.
- **Inside one repository** a module uses its sibling package by path, from the same commit, and waits for no publish.
- **A user outside the package's delivery group is not built before the merge**: it is built after it, when the new
version is in its series. A user that breaks fails its own build, is not wanted, keeps its previous build, and a
condition tells the operator. A group's members are built in the group's check, against the group's tagged version.
- **Cost**: the new manifest fields are read through mesh-sdk's manifest types, so their change rebuilds every module
once; it is delivered with design 51's step 2, the one rebuild that step already takes.
### A config value renamed (from 2026-11-03)
(ADR 0609 §4.) A config value's declaration may name its old name, `renamed-from`. On the merge, mesh-config moves the
value it holds under the old name to the new name in its own state, without the operator giving it again, and records
the move. This design decides what ADR 0609 leaves open:
value it holds under the old name to the new name in its own state, without the operator giving it again, emits
`renamed` (module, old name, new name, when) and records it. This design decides what ADR 0609 leaves open:
- **Which values**: a module's own config values only, never a secret, and never another module's value.
- **How long the old name is read**: until every node's want of the module names a build that reads the new name, as
the delivery view's wants show; until then mesh-config answers both names. A later change may drop `renamed-from`
only after that, and the checker refuses it before.
- **Tools of mesh-config**: `config_renames` (what moved, from which name, when) and `config_rename_again` (move one
value by hand when a move was refused).
- **Which values**: a module's own config values only, never a secret, never another module's value. A rename keeps
the value's trust: a value set without the operator's Approve stays untrusted under its new name (ADR 0348 §8), and
the checker refuses a rename that would change a value's trust.
- **How long the old name is read**: until every node-engine reports applied a build of the module that reads the new
name, as the node-engine seat's reports answer; until then mesh-config answers both names. A later change may drop
`renamed-from` only after that, and the checker refuses it before. The checker also refuses `renamed-from` in any
manifest until a build of mesh-config that understands it is delivered.
- **A refused move** (the old name holds nothing, or a secret) is mesh-config's finding, from which the condition keeper
raises the condition. **Tools of mesh-config**: `config_renames` (what moved, from which name, when) and
`config_rename_again` (move one value by hand when a move was refused).
### A value no longer read (from 2026-11-03)
A config value or secret no manifest reads any more is marked retiring by mesh-config, which emits `retiring` and asks
the operator once, through the operator channel, whether to remove it; it is removed only on the operator's Approve.
`config_retiring` lists what is retiring and since when.
### The mesh delivery cases this design carries
Research 081 listed the mesh delivery cases; these are the ones it handed to a design, each with its acceptance test.
Research 081 listed the mesh delivery cases; these are the ones it handed to a design, each with its acceptance test,
each a case of its module's one whole-module integration test unless it says live.
| case | what holds | acceptance test |
|---|---|---|
| C6, a package series raised | users on `0.1` keep the newest `0.1`; a reviewed change moves a user | integration, builder: a `0.2.0` publish moves no `0.1` user, its check takes the newest `0.1` too, a reviewed change naming `0.2` moves it, a user naming no series builds against the newest `0.1` |
| C14, a provision's form gains a field | the provider serves it in a new version; the consumer reads it once rebuilt; an older consumer ignores it | integration, mesh-sdk: a consumer built against the older form reads a provision with the new field |
| C6, a package series raised | users on `0.1` keep the newest `0.1`; a reviewed change moves a user | builder: a `0.2.0` publish moves no `0.1` user, its check takes the newest `0.1` too, a reviewed change naming `0.2` moves it, a user naming no series builds against the newest `0.1` |
| C14, a provision's form gains a field | the provider serves it in a new version; the consumer reads it once rebuilt; an older consumer ignores it | mesh-sdk: a consumer built against the older form reads a provision with the new field |
| C18, a new required config value with no default | the check names the nodes that lack it; the want waits; the operator is asked once | live: a module gaining such a value waits on each node lacking it, the operator is asked once naming the nodes, and once given the module is applied |
| C20, a config value renamed | the value moves; both names read until every node runs the new build | integration, mesh-config: a renamed value is present under the new name with no operator input, the old name answers until the wants show the new build everywhere, and dropping `renamed-from` early is refused |
| C20, a config value renamed | the value moves, keeps its trust, both names read until every node runs the new build | mesh-config: a renamed value is present under the new name with no operator input and its trust; the old name answers until the node-engines report the new build; dropping `renamed-from` early, or changing trust, is refused; a refused move writes its finding |
| C22, a secret only a person can give | the want waits; the operator is asked once what to give and where | live: a module requiring such a secret waits, one ask names it and where, and once given the module starts |
| C24, a config value or secret no longer read | it is retiring, removed only on the operator's Approve | integration, mesh-config: a value no manifest reads any more is marked retiring and stays until an Approve removes it |
| C24, a config value or secret no longer read | it is retiring, the operator is asked, removed only on the Approve | mesh-config: a value no manifest reads is marked retiring, `retiring` is emitted, one ask reaches the operator, and it stays until an Approve removes it |
## 9. Provisioning over the bus
@@ -11,6 +11,7 @@ decisions:
- "ADR 0605"
- "ADR 0606"
- "ADR 0607"
- "ADR 0610"
- "ADR 0311"
- "ADR 0251"
- "ADR 0189"
@@ -30,8 +31,9 @@ extending ADR 0189.)
## What is published, and how (from 2026-11-03)
**Every registry is a seat; a build module publishes into it over the bus; the builder gives every version.**
(ADR 0605, ADR 0606, ADR 0607, from research 081.) What follows is the to-be shape; the sections from *The parts*
on keep what the registries hold, and stand.
(ADR 0605, ADR 0606, ADR 0607, ADR 0610, from research 081.) What follows is the to-be shape; the sections from
*The parts* on keep what the registries hold, and stand. Every interaction below is a seat verb, a module's tool or an
event: no module reads or writes another's bus state (ADR 0596).
### The four registry seats
@@ -43,101 +45,155 @@ on keep what the registries hold, and stand.
| `oci-artifact-registry` | bundles and archives, by digest | OCI distribution | `distribution` |
Each is held once for the mesh. The last two replace the seat `mesh-artifact-store` and front the one artifact store;
the provision `artifact-store` keeps its name. `mesh-artifact-store` stays readable until every node's want of the
node-engine names a build that reads the new names, which the delivery view's wants show. `cargo-package-registry`
stays named, without a holder, until a build module publishes cargo packages.
while one module holds both, both deliver the provision `artifact-store`, and splitting them between two holders takes a
decision of its own. `mesh-artifact-store` stays served until every node-engine reports applied a build that reads the
new names (ADR 0610 §4); a sleeping node holds it open. `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*): the verbs `publish`, `versions` and
`remove` (a tagged version only), and the events `published` and `removed`, each carrying the name, version, kind,
tag, digest, fingerprint, commit and holder. A holder stores under the version it is given and counts nothing.
**One contract, a seat definition** (design 26, *A seat's definition*). Verbs: `publish`, `versions`, `remove` (a
tagged version only; the holder itself refuses `remove` of a version with no tag), and on `container-image-registry`
`mirror` and `tags`. Events: `published`, `refused` (added to ADR 0605 §2's two, additively) and `removed`, each
carrying the name, version, kind, tag, digest, fingerprint, commit and holder, and `refused` its reason. A holder stores
under the version it is given and counts nothing.
**Who may call which verb is the holder's to say**, in a trusted config value of the holder listing its callers, the
form ADR 0363 gives a restricted family: `publish` the build modules; `mirror` mesh-build-image; `remove` the builder.
Declaring `invokes` alone grants nothing. The holder answers any other caller with a refusal naming the verb.
### Publishing
```
builder ── build request (name, version or tag) ──▶ build module
│ hands the file: object store `publish-handoff`
│ calls <registry seat>.publish
│ publish(name, version, digests of every part)
▼
registry seat's holder ── reads the builder's `versions` (announced state)
│ stores, deletes the handed object
└── emits `published` ──▶ builder (entry → published), indexer, status view
registry seat's holder ── answers the parts it lacks
│ the build module sends those parts, in chunks, by `publish`
holder: hashes what it received, asks `builder_versions`,
stores in its own store, emits `published` or `refused`
▼
builder (durable consumer: entry, done key), indexer, status view, checker
```
- **Only build modules may call `publish`**, and only from a trunk build or for a tagged version; the bus grants
nothing else. No build module holds a registry's write credential: the npm write token and the image push
credential go, and each holder keeps its write credential as an own secret beside a read-only tool token (the forge
module: `write:package`, its tool token `read:package`).
- **The handed object** lives in the object store `publish-handoff`, which the builder owns: build modules write,
holders read and delete. An image is handed as an OCI layout holding only the layers the holder lacks: `publish`
names every layer's digest, the holder answers the digests it is missing, and only those are handed. The store is
capped at 20 GiB, an object lives at most 24 hours, and a publish not stored within 10 minutes is refused by its
build module, naming the seat, which leaves the entry `refused` (the condition below).
- **A holder refuses loudly**: a version or digest the builder's `versions` do not record, and a held version or tag
offered with other bytes. A rebuild of a held fingerprint answers the held version and stores nothing.
- **The done key** names the reference the seat answered: `<registry>/<module>/<artifact>@sha256:<digest>` for an
image, bundle or archive, `<name>@<version>` for a package.
- **Go packages** are fetched through the Go seat's provision first and Go's public proxy second, with no
`GOPRIVATE` and the checksum database off for the forge's paths; `go.sum` and the recorded digest refuse a mesh
package with other content. Outside Go modules keep coming from the public proxy; no mirror is built, and one could
come later as a holder of a seat of its own without any build module changing.
- **The file travels in the verb.** `publish` opens with every part's digest (an image's layers, a package's file,
a bundle's blob); the holder answers the digests it lacks, and the build module sends only those, in chunks no
larger than the bus's message limit, into the holder's own store. This is how ADR 0605 §3's hand-off holds under ADR
0596: no store is reached by two modules. The holder hashes every part it received and refuses one whose bytes do
not match its digest; a part it never received completed is dropped after 24 hours. The holder's tool
`registry_incoming` lists what is in transit, its fill against a 20 GiB cap, and drops one by hand; past 80 % of the
cap the holder writes a finding.
- **The holder asks the builder**, through `builder_versions`, whether the name, version and digest are recorded, and
refuses loudly otherwise (ADR 0605 §4). A rebuild of a held fingerprint answers the held version and stores nothing.
- **How a publish ends**: the holder emits `published` or `refused`. The build module waits for one of them, at most
10 minutes, narrating "waiting on <seat>" at least every five minutes; past the bound it writes the key refused,
naming the seat. A `published` that arrives after that is still heard by the builder on its durable consumer: the
entry turns `published` and the builder writes the done key then, so a late store is never lost.
- **What is published when**: a package or an image from a trunk build under its version, or as a tagged version; a
bundle or an archive from every done key by its digest alone, a candidate's included, and at the trunk's claim the
build module calls `publish` with the version and the held digest, which stores nothing. An upstream image is copied
by `mirror` with its pinned digest. The done key names the reference the seat answered; a key in the old
`artifact-store://` form keeps it, every reader reads both forms, and none is built again for its form.
- **No build module holds a registry's write credential**: the npm write token, the image push credential and the
artifact store's write binding go in step 4. 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`).
- **Go packages** are fetched through the Go seat's provision first and Go's public proxy second, with no `GOPRIVATE`
and the checksum database off for the forge's paths; `go.sum` and the digest the builder records (Go's `h1:` hash,
computed by the Go build module at the publish) refuse a mesh package with other content. The builder also tags
each Go version in git (`go/v0.1.<n>`), so a user that names a version can fetch it from the forge's git as well.
No mirror of outside modules is built; one could come later as a holder of a seat of its own.
### The versions
- **The builder numbers every trunk build** of a package, image, bundle or archive at its claim: the package series
its manifest declares (design 32 § 6; `0.1` when it declares none) and the next count for that name, under
compare-and-set. Numbers follow merge order.
- **The builder's state `versions`** (announced, the builder its only writer, readable by the indexer, the registry
seats' holders and the status view) holds per name, and per tag, every version with its fingerprint, commit,
digest, time and state: `numbered`, `published`, `refused` with its reason, or `removed` with the time, the branch
and the forge bridge's event that removed it. The build log carries each change too.
its manifest declares (design 32; `0.1` when it declares none) and the next count for that name, under
compare-and-set. Numbers follow the order of the forge bridge's merge events: a claim of a later merge waits until
every earlier merge of the same name is numbered. A fingerprint already numbered keeps its number.
- **The builder's state `versions`** holds one key per name, tag and version, with its fingerprint, commit, digest,
time and state: `numbered`, `published`, `refused` with its reason, or `removed` with the time, the reason (a
branch's end, retention, the sweep) and the event that removed it. History one, cap 1 GiB; past 80 % the builder
writes a finding. Only the builder reaches it; others call `builder_versions` or hear its events. The build log
carries each change too.
- **The floor**: on a name's first claim, or when `versions` holds nothing for it, the count starts above the highest
of what the seat's `versions` answers, the package's highest git tag (the forge bridge's state) and the highest
pseudo-version a consumer requires (the indexer's state); a name with none starts at `0.1.0`. Today that gives Go
`v0.1.14` and npm `0.1.18`.
- **A trunk build that declares a package is done** only once its done key names a published version for each.
- **Events of the builder**: `numbered`. **Tools of the builder**: `builder_versions` (a name's versions in order,
one call), `builder_publish_again` (one fingerprint under its number, after a holder failed; it refuses a version
refused for other bytes), `builder_tag` (ask a tagged version) and `builder_untag` (remove a branch's tagged versions).
of the seat's `versions`, the package's highest git tag (the forge bridge's tool `bridge_tags`) and the highest
pseudo-version a consumer requires (the indexer's tool `indexer_requirements`). The builder numbers nothing for that
name until all three answered, and writes a finding naming the silent one meanwhile. Today that gives Go `v0.1.14`
and npm `0.1.18`; a name with none starts at `0.1.0`.
- **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**:
`builder_versions` (a name's versions in order, one call), `builder_publish_again` (one fingerprint under its number,
after a holder failed; when the build's file is gone it builds the fingerprint again under the same number; it
refuses a version refused for other bytes), `builder_tag` (ask a tagged version), `builder_tag_asks` (open asks and
their state) and `builder_untag` (remove a branch's tagged versions).
### Tagged versions
- **A tagged version is asked by a key, never by a push.** The checker, for each head of a delivery group member that
declares a package, and a person, through `builder_tag`, write an ask in the builder's state `tag-asks`, keyed by
repository, branch and head; the builder watches it. The tag is cleaned to Semantic Versioning's characters.
- **It is numbered below the next version** (`0.1.15-feat-x.2`), never the latest; npm keeps it under a dist-tag of its
own and Go's `@latest` skips it. A package new in a group is numbered from its floor (`0.1.0-feat-x.1`).
- **A group's check takes it from the registry**: Go members by the tagged version, TypeScript members with their lock
naming it and the registry's integrity. Nothing is packed for a check, and a lone pull request's check publishes
nothing.
- **It is removed** when its branch is merged or deleted: the builder hears the forge bridge's event on a durable
consumer, calls each seat's `remove`, the seat emits `removed`, and the entry turns `removed`. A version without a
tag is never removed.
- **Asked by an event, never by a push** (ADR 0610 §1). The checker emits `tag-wanted` (repository, branch, head,
tag) for each head of a delivery group member that declares a package; a person asks through `builder_tag`. The
builder consumes the event on a durable consumer and keeps the ask in its own state `tag-asks`, removed when its
branch is merged or deleted. An ask the builder has not answered within five minutes is narrated in the build log
with what it waits on.
- **Numbered below the next version** (`0.1.15-feat-x.2`), never the latest; npm keeps it under a dist-tag of its own
and Go's `@latest` skips it. A package new in a group is numbered from its floor (`0.1.0-feat-x.1`).
- **A group's check builds after `published`**: Go members by the tagged version, TypeScript members with their lock
naming it and the registry's integrity; nothing is packed for a check, and a lone pull request's check publishes
nothing. No `published` within the publish's bound (10 minutes) fails the check naming the package.
- **Removed with its branch**: on the forge bridge's merged or deleted event, heard on a durable consumer, the builder
calls each seat's `remove`. It acknowledges the event only once every seat emitted `removed`; until then it retries
every minute and, past 15 minutes, writes a finding naming the seat. A version with no tag is not removed by a
branch's end; retention may remove an old unreferenced one (below).
### Retention
The retention of *The package registry* below, and the artifact sweep, keep working, now through the seats: each
removal emits `removed` and the builder marks its entry, so `versions` never says `published` for bytes that are gone.
The Go seat keeps what the npm seat keeps: every referenced version and the newest five of each package series.
### Health and conditions
Every holder declares a health check. Conditions, each raised and cleared: a publish refused (cleared when it is
stored), a registry seat not answering (cleared when it answers). A build narrates its publish: asked, stored, refused.
Observers write findings; the condition keeper raises and clears the conditions from them (ADR 0564). Findings: a
publish refused (the holder; cleared when it is stored), a registry seat not answering (each holder's health check,
which calls `versions` on each seat it holds and probes the read side every minute, so a dead seat is seen with no
publish running), a removal not done (the builder), a floor source silent (the builder), a store past 80 % (the holder,
the builder). A build narrates its publish: asked, waiting on a seat, stored, refused.
### The move, in four steps
Each step is its own delivery. 1: the holders serve the verbs and `versions` exists. 2: mesh-sdk's first publish
through the seats, read back, delivered together with the move of mesh-sdk's seat contracts into `mesh-contracts`
(design 26), so every module is rebuilt once for both. 3: the Go fetch switches to the seat. 4: the old credentials
and `package-version.sum` go. Steps 1 to 3 are rolled back by reverting their pull request; step 4 by reverting it,
which mints the build modules' credentials again. On a fresh mesh the builder publishes into a holder the
foundation's bootstrap raised.
Each step is its own delivery, and no credential goes before its replacement works (ADR 0605 §7).
1. The holders serve every verb of the four seats with their caller lists; the builder's `versions`, `tag-asks`, its
events and tools; the forge bridge's `bridge_tags` and the indexer's `indexer_requirements`.
2. Every build module publishes through the seats: mesh-sdk's first numbered publish, images, bundles, archives and
upstream copies, each read back; delivered with the move of mesh-sdk's seat contracts into `mesh-contracts` (design
26), so every module is rebuilt once for both. The checker emits `tag-wanted`. Until step 3, Go users fetch from the
forge's git, where the builder's tags let them take the numbered version.
3. The Go fetch switches to the Go seat. Its rollback stays safe after a user moved, since the git tags exist.
4. The three credentials go, and `package-version.sum`. The old seat name retires after it, on the node-engines'
reports.
Steps 1 to 3 are rolled back by reverting their pull request; step 4 by reverting it, which mints the build modules'
credentials again. **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
Research 081's tests 1-3, 5-7, 9, 10, 12, 13, 15-20, 22 and 23 (its Direction) are this design's, with their labels
(live or integration, and the module). Added here:
Research 081's tests 1-3, 5-7, 9, 10, 12, 13, 15-20, 22 and 23 (its Direction) are this design's, with their labels.
Each integration test below is a case of its module's one whole-module integration test, which starts the module
against the real bus (test budget: mesh-delivery holds 76 integration tests today, over its 50; these add cases, not
tests, and each change that touches the builder's or the indexer's tests merges two old ones into its whole-module
test).
- **Integration, builder.** A branch pushed with no ask publishes nothing; a lone pull request's check publishes
nothing; a group introducing a new package takes `0.1.0-<tag>.1`; `builder_untag` removes a branch's tagged
versions with `removed`, and the entry shows the branch and the event.
- **Integration, builder.** An image whose layers the holder holds hands only the missing ones; a publish not stored
within 10 minutes leaves the entry `refused` and raises the condition.
- **Live.** A revert of step 4 on one node mints the build modules' credentials again before it reaches the others.
- **Builder.** A branch pushed with no ask publishes nothing; a lone pull request's check publishes nothing; a group
introducing a new package takes `0.1.0-<tag>.1`; the checker's `tag-wanted` yields `numbered` and `published`, and
members are built only after `published`; no `published` in 10 minutes fails the check naming the package;
`builder_untag` removes a branch's tagged versions with `removed`; a `remove` while the holder is down is retried and
past 15 minutes writes its finding; a late `published` after the bound writes the done key; `builder_publish_again`
after the build's file is gone builds again under the same number; with the seat's holder stopped at a first claim
nothing is numbered and the finding names it; two merges claimed in reverse order are numbered in merge order; a
retention removal turns the entry `removed`.
- **Holder (gitea, distribution).** A caller not on the caller list is refused for each verb; a part whose bytes do
not match its digest is refused; only the missing layers are sent; `remove` of a version with no tag is refused;
with no publish running, a stopped Go seat writes its finding and clears it when it answers; `registry_incoming`
lists and drops.
- **Vault.** Reverting step 4 mints the build modules' credentials again.
## The parts