Designs 20, 25, 26, 32, 51, 55 and playbook 07: the registry milestone as built (issue 1966) #514

Merged
mesh-tools merged 12 commits from docs/registry-milestone-designs into main 2026-11-04 18:19:24 +00:00
9 changed files with 339 additions and 81 deletions
+9 -1
View File
@@ -26,7 +26,15 @@ only: never push, never open a pull request, never call a verb that writes on th
ssh, never a shell helper for the mesh. Tests: every change carries a test that fails without it (check it);
rows or subtests in existing tests where the repository is over the test budget the instructions set; no real
sleeping. Run `go vet ./...`, `go vet -tags integration ./...` and `go test ./...` in every touched module as
the running checker places siblings (playbook 07). Use a temporary directory under `/var/tmp` (TMPDIR).
the running checker places siblings (playbook 07). Use a temporary directory under `/var/tmp` (TMPDIR). Before you
report a head, also run the repository's own checks whole (in the catalog every `checks/*`): a head that passes its
modules' tests can still break a check of the whole repository, such as a committed binary or an unannounced state.
Start no agents of your own: the lead decides who works on what.
**Stop at about two thirds of your context.** Commit what is done, write a section under your agent label at the
end of the handover file the caller named (the subtasks done by label, the head of each worktree, what is left in
order, the findings), and report. A fresh agent continues from that section; work past that point is lost when the
context runs out.
Report in under 20 lines: per clause or subtask the change (file:line) and its test, that each test failed
first, the full commit ids, anything the prepare got wrong, and what is left. Glossary words only.
+21 -5
View File
@@ -98,14 +98,26 @@ and repair).
- **tool** — one capability a module serves through the tool runner, addressed `<module>.<tool>`. A
seat's **verb** is the same thing carried by a seat rather than a module.
- **mesh-sdk** — the library a module's own code is written against, including the SDK harness that serves a
tools bundle to the tool runner. The SDK harness is part of it, not a library of its own. It keeps the shared
runtime, and its seat contracts move into `mesh-contracts` (ADR 0608 §5).
tools bundle to the tool runner. The SDK harness is part of it, not a library of its own. It keeps the runtime
every module uses, and the seat-neutral parts two or more seats' clients or holders share; its seat contracts and
the logic it held over seats move into the contracts modules, and its seat list into the seat-set package
(ADR 0608 §5, ADR 0625 §2, §9, ADR 0627 §2).
*Not:* ~~tools-sdk~~
- **contracts module** — a module whose work is to publish the contract packages of seats (their verbs, events,
the layouts of their states, their messages) in every language the seats' users are written in, and never logic:
`mesh-contracts` for the seat contracts mesh-sdk held, `mesh-<area>-contracts` for an area's. A seat's holder
implements the contract; the contracts module holds no seat (ADR 0608 §5).
the layouts of their states, their messages), one per seat defined whole and per language, the seat-set package,
and the seats' client packages; every file but a client's is generated. `mesh-contracts` for the Core domain's
seats and every domain that has none yet, `mesh-<domain>-contracts` for a domain's, named after its heading on
this page; a seat defined outside the mesh's own contracts has one of its own. A seat's holder implements the
contract; the contracts module holds no seat (ADR 0608 §5, ADR 0625 §1, §5 to §7).
*Not:* ~~SDK module~~, ~~domain package~~
- **client package** — the one hand-written package a seat may carry beside its contract, in the seat's contracts
module and never in a holder: the logic two or more callers would otherwise each write over that seat. It uses
only its own seat's contract and mesh-sdk's runtime, never another seat's client, and names no holder, no module's
tool and no module's subject (ADR 0625 §4, ADR 0627 §1, §3). Bare "client" only where the sense is plain (see
[Homonyms](#homonyms)).
- **seat-set package** — the generated package in `mesh-contracts` holding every seat not yet defined whole at the
seat list's grain (its name, scope, verbs and provision); mesh-sdk's `catalogue` is its one importer in mesh-sdk
(ADR 0625 §2, §3).
- **contracts marker** — the field `generated-from` of a contracts module's package artifact, naming the directory
of the seat definitions its files are generated from; the judge refuses a module carrying it that claims a seat,
and the build modules' test step regenerates and compares every module carrying it (design 26).
@@ -1186,6 +1198,10 @@ renaming one sense, the old sense moves to a *Not:* line and becomes mechanical.
| harness | the part of `mesh-sdk` that serves a tools bundle | **SDK harness** | Module |
| | the program that runs an agent's sessions | **agent harness** | Operator and conversation |
| | the TypeScript `provisioner` helper for database providers | **`provisioner`**, renamed by design 57 | Provisioning |
| client | the hand-written logic beside a seat's contract | **client package** | Module |
| | mesh-provisioner's part on one node | **provisioner client** | Provisioning |
| | a module fetching a media release | **download client** | Media |
| | the operator's mail in a browser | **mail web client** | Mail |
| series | the first two numbers of a package's versions | **package series** | Module |
| | a kind of content told in episodes | the kind **series** | Media |
| role | what a seat names, at a scope | **seat**; the bare word in older designs is tracked by issue 872 | Module |
+16
View File
@@ -83,6 +83,22 @@ Three questions, answered from the machine:
thing works: the credential authenticates, a wrong one is refused, the containers reach each
other, the data survives a restart.
## A module that calls or holds a seat
*Added 2026-11-04* (ADR 0625, ADR 0627). Where each piece of a seat's code goes is
[design 20](../../03-DESIGN/01-to-be/20-writing-a-module.md) *Where a seat's code goes*; in short:
- **Import the seat's contract and, where it has one, its client package**, from the seat's contracts module
(`mesh-contracts` today), never the holder module and never a copy of the seat's types in your own code.
- **Logic you would write over a seat that another caller would write too** belongs in that seat's client package,
through a change of the contracts module; it never goes into mesh-sdk, and never into the holder.
- **Holding a seat** means implementing its contract in your module. A seat you define yourself gets a contracts
module of its own (it may share your repository); a role no other module could ever take over is your module's
own tools and events, not a seat.
The review of the pull request checks this; the checks design 26 lists refuse a stray file in a contracts module,
a seat named in mesh-sdk and a client reaching another seat's client.
## What the first port actually cost
Six attempts, one real bug. Recorded because the ratio is the lesson: **the mesh was right every
+5 -2
View File
@@ -377,8 +377,11 @@ is a breach the lead names.
**While a group is being checked, nothing else is merged or pushed into a repository that is one of its members:**
a moved main asks the group's check again. How it is checked: the lead's wait list names the group's members, and
`mesh-delivery.show` shows one check per head.
**A group is one check.** Before the push, every member pins the group's own mesh-sdk head exactly (the checker refuses an
older commit of the same branch), then all members are pushed and opened together, so the group's check runs once. A head the review replaces
**A group is one check.** Before the push, every member pins the group's own package heads (mesh-sdk, and any other
package a member publishes) so it builds with `GOWORK=off`; the check itself builds against the group's heads whatever a
member's go.mod names (ADR 0573 §2, §4), so an older pin is not refused there, but the trunk builds after the merge read
the go.mod as written. Two members that import each other are pinned in order, each to the other's commit before its own
pin. Then all members are pushed and opened together, so the group's check runs once. A head the review replaces
counts for nothing against a running check; say so, and ask no other until the new head is reviewed.
**Before a session trusts an agent's "pushed", it reads the forge branch's head** (`git ls-remote origin refs/heads/<branch>`) and compares it with the commit reviewed; an agent may commit on a detached HEAD and report a push that never happened (2026-10-26: the first check of a reviewed fix failed "not our ref"). The pull request is opened only on that read head. Pull requests in two or
more repositories that share the branch name are one delivery group (ADR 0239 §2).
+29 -1
View File
@@ -5,8 +5,11 @@ code:
- mesh-catalog modules/showcase
- mesh-controller internal/builder
- mesh-sdk src
updated: 2026-10-02
- mesh-contracts
updated: 2026-11-04
decisions:
- "ADR 0625"
- "ADR 0627"
- "ADR 0150"
- "ADR 0099"
- "ADR 0053"
@@ -47,11 +50,36 @@ Split the way the protocol is split:
mesh set. Reading a file is not a thing an SDK needs to teach.
- **No API clients.** A Plex client belongs in the Plex module. It changes when Plex changes,
which has nothing to do with any other module — *frequent **and** cascading is the disease.*
Logic over a *seat* is not such a client: it is the seat's client package, in the seat's contracts
module (below), never in the SDK and never in a holder.
- **No storage, no HTTP framework, no logging library.** Use the language's.
If you find yourself wanting to add something to the SDK, the test is ADR 0039's: **does editing it
recompile unrelated modules, and does it change often?** Both, and it does not belong.
## Where a seat's code goes
*Added 2026-11-04* (ADR 0625, ADR 0627; [design 26](26-the-seats.md) *Where a seat's code goes* is the
whole rule). A module that calls or holds a seat meets three places, and each piece of code has one:
| the code | lives in | written |
|---|---|---|
| a seat's types, subjects and a typed call per verb or accept | the seat's **contract**: a package of its own per language in the seat's contracts module (`mesh-contracts`, or `mesh-<domain>-contracts` once its domain moves), or the seat-set package while the seat is not yet defined whole | generated from the seat's definition, never by hand |
| logic two or more callers would otherwise each write over that seat (an ask and its awaited answer, publishing a file in parts to a registry) | the seat's **client package**, beside its contract, owned by the contracts module | by hand; it uses only its own seat's contract and mesh-sdk's runtime, never another seat's client, and names no holder, no module's tool and no module's subject |
| what every module needs whatever the seat (the bus connection, the tools bundle's runtime, state), and a part several seats' clients or holders share that names no seat, verb or seat's type (moving a file in parts) | **mesh-sdk** | by hand; it takes no seat-specific type |
| the seat's implementation | the **holder** module | by hand, against the contract; never the place of the contract or a client |
**So a module imports the contracts and clients of the seats it uses, and mesh-sdk.** Its users rebuild when a
seat they use changes, not when any seat does. A seat you define yourself, outside the mesh's own contracts, gets a
contracts module of its own, which may share your holder module's repository (ADR 0625 §7): a second holder then
imports only the contract, never your holder. And a role no other module could ever take over is no seat at all:
it is your module's own tools and events (ADR 0625 §8).
*How it is checked:* by the checks design 26 lists: the generators' tests, mesh-sdk's import and package-list tests,
the build modules' regenerate-and-compare step, the catalog check of a client and the judge's refusal of a
contracts module that claims a seat; a role made a seat, or a seat left as one module's tools, by the review of the
pull request that adds it.
## A module with four capabilities, in four languages
A module is **one piece of software** (ADR 0040) and
+106 -47
View File
@@ -13,6 +13,12 @@ code:
updated: 2026-11-04
decisions:
- "ADR 0595"
- "ADR 0626"
- "ADR 0631"
- "ADR 0620"
- "ADR 0259"
- "ADR 0627"
- "ADR 0436"
- "ADR 0619"
- "ADR 0615"
- "ADR 0621"
@@ -210,6 +216,9 @@ holding `mesh-broker` — has an account of its own whose only grant is the snap
names, a stream's information, the snapshot request and its acknowledgements, and its own inbox. The
night's backup takes every stream through it ([to-be 43](43-backups-against-mistakes.md)). The
controller stays the only writer of stream definitions; the writers table holds that at composition.
*Amended 2026-11-04, ADR 0626 §2:* that account keeps exactly this grant; beside it, the bus module's process holds
a process account of its own for the bus state it creates (§ 12.2), so the module holding `mesh-broker` holds two
accounts, each doing only what its record grants.
### The store window, and what moving it into the server changes
@@ -732,16 +741,25 @@ modules' names meet.
creates streams and buckets under modules' names only, and deletes only through `drop` and `recreate`. Its health
turns unhealthy, naming the cause, when an accepted verdict has waited on its durable consumer, unanswered, for
more than three minutes, or when its last pass over fill, which runs every minute, is more than three minutes old, so the only guard against a store filling never stops without a sound; another module's entry past the raise mark
raises that module's condition and leaves the bus module healthy. **Its process has a bus account of its own**,
composed by the grants part for the holder of the seat `mesh-broker` (a seat, never a module's name), minted by the
vault as an own secret: JetStream create and update under modules' names only (streams, buckets and object stores
`<module>_*`), their delete subjects, and the seat's subjects for `create`, the seat's `apply`, `recreate` and
`drop`, which no other account holds, the tool runner's included.
raises that module's condition and leaves the bus module healthy. **Its process has a process account of its own,
granted more than any other process's** (ADR 0626 §1; ADR 0436 §1, §2). The process declares `bus` as any process
may, with one key more, `keeps`, which is allowed on this process alone; the grants part composes the account from
that declaration as it composes every process account, and the vault mints it as the module's own secret. Beyond
what a process's `bus` grants, `keeps` grants the bus's create and update subjects for streams, buckets and object
stores under modules' names only (`<module>_*`), the snapshot request for an entry before `recreate` or `drop` with
its chunks' acknowledgement subjects and its own inbox, and the delete subjects for those two acts. It is granted
no subject on which a tool or a seat's verb is answered (ADR 0436 §3), and no subscription to any restricted
stream's own subjects: the snapshot is the one way a restricted stream's data reaches it, kept only in the bus's
snapshot store, and the process reads no message of it. No other account holds those subjects, the tool runner's
included. **The snapshot account stays as it is** (ADR 0626 §2, § 3 above). **The judge refuses any other bus
declaration of the bus module** (ADR 0626 §3): anything it would say or hear on the bus beyond its process's `bus`
and the bus state the foundation keeps on the old sections (ADR 0595 §10), `keeps` on any other process or module,
and a `bus` of its process that names a subject on which a tool or a verb is answered.
- **The binding travels on the bus module's events** (ADR 0595 §1, §7, as their progressive insight of 2026-11-03
has it, waiting for the operator's Approve). The grants part, which issues every membership, hears them on a durable
consumer: it writes the **binding** (the names on the bus) into the module's membership on `created`, on every
machine the module is assigned, rewrites it on `recreated` and removes it on `dropped`, kept in its own state
`memberships`, so no part writes another's state and a binding outlives the 30 days its event is kept.
`memberships`, so no part writes another's state and a binding outlives the 7 days its event is kept on the shared stream (§ 12.3).
A module whose binding is missing at start fails loudly, naming the entry; it never starts blind. A `refused`
create hands no binding. **A refused or overdue creation is loud**: overdue means no `created` or `refused` within
five minutes of an accepted verdict that asks for the entry. The condition is raised by the watchman, which today raises the judge's
@@ -754,17 +772,23 @@ vault as an own secret: JetStream create and update under modules' names only (s
foundation parts that read each other (the grants part, the tool runner, the indexer and the assigner) stop
reading the indexer's and the assigner's bus state in the milestone of ADR 0612, with the other 19 readers of that
state (ADR 0615 §1). **Each owner serves them as tools of its own module**, the indexer `manifests`,
`modules` and `verdicts` (ADR 0622 §1), the assigner `placement`, `seat-holders` and `wants`, and declares `runs-as`, so its tools bundle runs in
`modules`, `verdicts` (ADR 0622 §1) and `graph` (ADR 0631 §1), the assigner `placement`, `seat-holders` and `wants`, and declares `runs-as`, so its tools bundle runs in
a runtime of its own on the module's own bus account, never in the node's tool runner, and the tool runner never
asks through itself (ADR 0621 §1). No new manifest form is added: a process still answers no tool call or seat verb
(ADR 0436 §3). **Each verb is granted to exactly its readers** (ADR 0621 §2, as ADR 0615 §3 lists them): `placement`
to the grants part, the tool runner and the indexer; `seat-holders` to the tool runner; `wants` to the grants part;
`manifests` and `modules` to the grants part, the tool runner and the assigner; `verdicts` to the grants part
alone, its `callers` list holding the grants part only (ADR 0622 §1). These six are the first-view tools.
alone, its `callers` list holding the grants part only (ADR 0622 §1); `graph` to the assigner alone, its `callers`
list holding the assigner only, the indexer itself refused, and the assigner's `reads` entry of the indexer's
`graph` goes (ADR 0631 §1). These seven are the first-view tools, four of the indexer and three of the assigner.
`verdicts` follows the first-view rules whole (ADR 0622 §2, under ADR 0615 §4 to §6): its answer carries the
revision, the grants part then follows `manifest-accepted` and `manifest-refused` and asks once more on a gap in
their revisions, it raises the condition naming the indexer and `verdicts` past the bound and never reads the
indexer's state, and `grants_first_views` and `grants_first_view_again` cover it. A reader declares each in `invokes`
indexer's state, and `grants_first_views` and `grants_first_view_again` cover it. `graph` follows them whole too
(ADR 0631 §2, under ADR 0615 §4 to §6): its answer carries the revision, the assigner then follows the indexer's
`indexer.views` events for the bucket `graph`, which carry the revision, and asks once more on a gap, said in its
log; started while the indexer is down, it raises the condition naming the indexer and `graph` past the bound and
never reads the indexer's state; and `assigner_first_views` and `assigner_first_view_again` cover it. A reader declares each in `invokes`
as `<module>.<tool>` only where it is granted, which is the grant the grants part composes as for any tool, and
each tool checks the proved caller against a `callers` list holding exactly its readers and refuses any other, the
owner itself included. **The judge of the milestone is its own group's** (ADR 0621 §4): since no field is added,
@@ -821,7 +845,7 @@ vault as an own secret: JetStream create and update under modules' names only (s
part and the tool runner their first views while the bus module still waits on it. The order of owner before reader is kept by the module graph's served-verb edge for the 19 readers (ADR 0614 §2), and,
for the four, whose verb edges lie on cycles the graph leaves out (ADR 0614 §3), by the owner's answer at once and
the reader's wait (ADR 0615 §2).
- **The other 19 readers ask the owners' read tools, one per bucket they read** (ADR 0615 §1, §2). Beside the six
- **The other 19 readers ask the owners' read tools, one per bucket they read** (ADR 0615 §1, §2). Beside the seven
first-view tools above, each owner serves a read tool for every bucket of its state one of the 19 reads today,
named `indexer_read_<bucket>` or `assigner_read_<bucket>` and marked as a read: fifteen. The indexer's nine:
`indexer_read_manifests`, `indexer_read_modules`, `indexer_read_predefined`, `indexer_read_config_candidates`,
@@ -831,8 +855,8 @@ vault as an own secret: JetStream create and update under modules' names only (s
the nine buckets of the indexer and the six of the assigner the 19 read (§ 12.6), `modules`, `wants` and
`placement` among them for the status view; the indexer's tenth, `verdicts`, is read by the grants part alone,
through its first-view tool. **They are ordinary module tools**: granted, as any tool is, to every
reader that declares it in `invokes`, and callable by a session; the `callers` list of ADR 0621 §2 and ADR 0622 §1 covers only the six
first-view tools. **The status view keeps all its tools**, their names, arguments and answers
reader that declares it in `invokes`, and callable by a session; the `callers` list of ADR 0621 §2, ADR 0622 §1 and ADR 0631 §1 covers
only the seven first-view tools. **The status view keeps all its tools**, their names, arguments and answers
unchanged, its `indexer_*` and `assigner_*` tools included: as a reader it answers them by asking the owners' read
tools instead of reading their buckets. Checked live after the delivery: a session's call of each status view tool
answers, and `mesh_search` finds each owner's read tool by its bucket's words.
@@ -843,9 +867,9 @@ vault as an own secret: JetStream create and update under modules' names only (s
### 12.3 The seat's verbs, events and fill
The seat `mesh-broker`, until now a seat with no verbs, gains these (ADR 0595 §5, §6). It is one of the nine seats
defined whole in the milestone of ADR 0612, its contract in `mesh-contracts` (ADR 0613 §1; design 26 *A seat's
definition*), which settles where its definition lives (ADR 0595 §12). An entry's arguments are the module, its local
The seat `mesh-broker`, until now a seat with no verbs, gains these (ADR 0595 §5, §6). It is one of the ten seats
defined whole in the milestone of ADR 0612, its contract a package of its own in `mesh-contracts` (ADR 0613 §1,
ADR 0627 §1, ADR 0625 §1; design 26 *A seat's definition*), which settles where its definition lives (ADR 0595 §12). An entry's arguments are the module, its local
name, its storage type, its template, its options and its configuration; what is held answers the settings held and
the fill (count, bytes, the cap); `drop` and `recreate` carry the warrant and why.
@@ -855,27 +879,35 @@ the fill (count, bytes, the cap); `drop` and `recreate` carry the warrant and wh
| `show` | one entry, the same | the same |
| `check` | compares what the bus holds with what was asked, entry by entry | the same |
| `orphans` | entries the bus holds that no manifest asks for; never one whose manifest the judge refused or outgrew, nor one of the foundation's old sections | the same |
| `create` | creates one entry now | the bus module's own process only |
| `apply` | applies a difference that only grows: a larger cap, a longer age | the bus module's own process only |
| `recreate` | rebuilds an entry whose settings differ in a way that cannot grow, from its data where the storage type allows | the bus module's process, on the operator's warrant |
| `drop` | removes one orphan | the bus module's process, on the operator's warrant |
| `create` | creates one entry now, for an entry an accepted manifest asks for | an act of the bus module's process alone, answered on no subject |
| `apply` | applies a difference that only grows: a larger cap, a longer age | the same |
| `recreate` | rebuilds an entry whose settings differ in a way that cannot grow, from its data where the storage type allows | the same, on the operator's warrant, which the process verifies itself |
| `drop` | removes one orphan | the same, on the operator's warrant, which the process verifies itself |
`create`, `apply`, `recreate` and `drop` are granted to the bus module's own credential alone, so a module calling
them is refused by the bus. A person asks for them through the bus module's tools, and `recreate` and `drop` act
only on the operator's warrant (ADR 0259 §6), asked through the operator channel. Before `recreate` or `drop` acts,
the bus module takes a stream snapshot of the entry. A drifted entry is said, never quietly fixed: only a difference
that grows is applied without a person.
`create`, `apply`, `recreate` and `drop` stay in the seat's definition as acts its holder's process alone performs,
answered on no subject (ADR 0626 §4, ADR 0436 §3): they are not verbs whose subjects a credential is granted, but
acts on the bus's own create, update and delete subjects, which only the process's account holds, so no module can
ask the bus for them. A person asks for them through the bus module's tools, which hand the request to the process
through its own state (below); `recreate` and `drop` act only on the operator's warrant (ADR 0259 §6), asked
through the operator channel. Before `recreate` or `drop` acts, the process takes a stream snapshot of the entry
(ADR 0595 §5), and it acts only once the snapshot is whole. A drifted entry is said, never quietly fixed: only a
difference that grows is applied without a person.
**Events**: `created`, `refused` (with why), `drifted` and `drift-cleared`, `full` and `full-cleared`, `orphaned`
(cleared by a `drop`, or by a manifest asking for the entry again, which emits `created` for it), `recreated` and
`dropped` (ADR 0595 §6, as its progressive insight of 2026-11-03 has it, waiting for the operator's Approve);
each carries the module, the local name, the bus name, the template, and the settings asked against those held. The
bus module's own kept data stays on the old sections (ADR 0595 §10), because it cannot wait for its own process to
create it: it declares this stream in its `streams` section, kept 30 days (ADR 0363's bound), created by the grants
part as today, with a cap of 256 MiB; it drops its oldest when full, which loses no live state, because every drift,
orphan and fill stands as a condition until it clears. Thirty days, because a drift or an orphan is read back days later by a person; the conditions hold the
live state. The tool `history` of one entry reads them back: every event the bus module said of it, in order. This
settles where they are kept, which ADR 0595 §12 left to the design.
each carries the module, the local name, the bus name, the template, and the settings asked against those held.
**They follow every seat's events**: each is said on `mesh.seat.mesh-broker.event.<event>`, on the shared stream
`EVENTS` (§ 3), kept 7 days, and heard through `consumes` `mesh-broker.<event>` like any seat's event. **The bus
module keeps their history apart**, because a drift or an orphan is read back days later by a person: a stream of
its own in its old `streams` section, on its module subjects, kept 30 days (ADR 0363's bound), created by the grants
part as today, with a cap of 256 MiB, to which the process appends every event it says. Its own kept data stays on
the old sections (ADR 0595 §10), because it cannot wait for its own process to create it. That stream drops its
oldest when full, which loses no live state, because every drift, orphan and fill stands as a condition until it
clears; the conditions hold the live state. The tool `history` of one entry reads it back: every event the bus
module said of the entry, in order. This settles where they are kept, which ADR 0595 §12 left to the design, and
needs no new manifest form: a module's stream never takes a seat's subjects, and every seat keeps one form for its
events.
**Fill is the bus module's one periodic read** (ADR 0595 §6): every minute, for every entry that refuses when full,
because the bus says nothing as a store fills. The condition `full` is raised at a raise mark and cleared below a
@@ -890,15 +922,35 @@ clear mark), `drifted` (cleared by `drift-cleared`), `orphaned` (cleared by a
**Its tools** (tools-are-how-a-module-is-spoken-to): the four reading verbs, marked reads, answer every entry the
bus module created and its fill, and `history` its events for one entry; `create` and `apply`, marked acts, and
`recreate` and `drop`, marked dangerous and so granted only by name (ADR 0386 §6, ADR 0387), are its tools for a
person. Such a tool, in the bus module's bundle, holds no grant of those verbs' subjects, and neither does the tool
runner's account: it hands the request to the process on the process's own tool subject. The caller travels as the
token the identity seat signs (ADR 0386 §8), whose signature the process checks, so no account granted that subject
can name another caller. **Until that token exists** (design 58 builds the identity seat), the process's tool subject
may be published on by tool runners' accounts alone, and the process trusts the caller the tool runner proved and
stamped (ADR 0386 §7), never a name in the request's body; `recreate` and `drop` act only on the operator's warrant,
which is the guard that holds either way. The process acts only for a caller whose role grants that tool, for
`recreate` and `drop` only once the operator's warrant is held, records each as a hand act (`mesh-hand-acts`), and
answers what it did. **The mesh deletes kept data only through `drop` and `recreate`** (ADR 0595 §4, as its
person. **A person's request reaches the process through the process's own state** (ADR 0626 §4), because a process
account answers no tool call (ADR 0436 §3). Such a tool, in the bus module's bundle, holds no grant of the bus's
create, update or delete subjects, and neither does the tool runner's account: it writes the request into the bus
module's state `requests` (an old `state` section, as `asked` is, ADR 0595 §10), stamped with the caller the tool
runner proved and recorded (ADR 0386 §8), and watches that key for the process's answer, never asking again on a
timer. The process watches its own state `requests` (ADR 0596 §4) and takes a request up when it is written. The stamp names
who asked, for the record; it is no boundary, because every session on a machine reaches the bus as the tool
runner's account (ADR 0436 §9). **So the process verifies the operator's warrant itself**: for `recreate` and `drop`
it trusts nothing in the request, reads the operator channel's record of the ask under its own account and checks
that the warrant binds exactly this act on this entry (ADR 0259 §6), as the mail sender does (ADR 0436 §9); a request
without a valid warrant it refuses, deleting nothing and writing why into its answer; with one, it takes the entry's
stream snapshot first. **The process wakes on the warrant, never on a timer**: a warrant is said on the operator
channel's `decided` for the asker that raised the ask (ADR 0259 §6; ADR 0620 §2), here the bus module itself, which
hears its own `decided` on a durable consumer of the process, whose filter the grants part derives from the
process's `uses` of the operator channel seat (not from a `consumes`, which a process has none of), and runs its pass over the waiting `recreate` and
`drop` requests when one arrives, so a warranted `drop` acts within seconds of the operator's answer, and one given
while the process was down is heard when it is back. **A warrant acts once** (ADR 0259 §6): before it acts, the
process claims the warrant's ask id by writing the key `acted.<ask id>` in the state `spent` of the process's own bus state, which the tools bundle is not given (a person's request and the
record of what it spent are never in one bucket, and no tool can write the record), created by compare-and-set after the snapshot and before any deletion, and a request naming an ask it already
acted on is refused, deleting nothing and saying which act that warrant already performed. **An ask lasts 12 hours**,
shorter than the 24 hours `requests` keeps a request: a request whose ask ended unanswered is ended and said, and a
warrant is not acted on once its ask ended; the tool raises the ask before it hands the request over. A request
that cannot act yet says why in its field Waiting, and a hand act the process could not record is written on the
request in its field Unrecorded. The tool `nats_requests` reads the waiting and ended requests, and the warrants
spent from `spent`, which is declared `read-only: true` (design 32 § 4) so that the tool reads it and holds no publish on it, and drops a waiting request by hand, recorded as a hand act. `create` and `apply` need no warrant, since they only make or grow what an accepted manifest
asks for, and the process refuses either for an entry no accepted manifest asks for. A tool acts only for a caller
whose role grants it; the process records each act as a hand act (`mesh-hand-acts`) and answers what it did. What
stands between the tool runner's account and the process's delete power is that check of the warrant, so its tests
are this section's central check (§ 12.7). **The mesh deletes kept data only through `drop` and `recreate`** (ADR 0595 §4, as its
progressive insight of 2026-11-03 has it): the bus module's older remove tools (its stream, bucket and consumer
removals, ADR 0564) refuse an entry of the new form, naming `drop`'s tool, and keep working for the old sections. The bus seat's `apply` (a difference that grows) and the
ledger's `apply` (§ 12.4) are two verbs on two subjects, granted apart; this design writes them qualified. Its health
@@ -995,7 +1047,8 @@ not by the 19 the first plan counted; and 151 state entries were counted, not 15
the first count, which chose the templates). The owners first gain their events, first-view tools and read tools: the
indexer a read tool per bucket its readers read, the assigner the same (§ 12.2 lists them). Then all 23 readers move in the milestone of ADR 0612, the
four foundation readers included, by the first view of § 12.2 (ADR 0615 §1), the grants part's of the indexer's
`verdicts` by its first-view tool `verdicts` (ADR 0622) and then `manifest-accepted` and `manifest-refused`; their order is the module graph's, not
`verdicts` by its first-view tool `verdicts` (ADR 0622) and then `manifest-accepted` and `manifest-refused`, and the
assigner's of the indexer's `graph` by its first-view tool `graph` (ADR 0631) and then `indexer.views`; their order is the module graph's, not
a review reading an owner delivered (ADR 0615 §2). The status view's move keeps every tool it serves, each answered by
asking the owners' read tools of § 12.2. **The node-engine's reads granted in code stay**: the grants part's code
grants each node's node-engine, with no manifest declaring them (`CandidateReads` and the composed reads beside it),
@@ -1010,19 +1063,19 @@ issue 1966.
|---|---|
| A manifest's bus state is valid, once | the judge's unit tests, each refusal naming the field: an unknown template, an option outside its bound, past values 0, "when full" given, a fixed setting given, a name used twice across storage types, two modules' bus names alike, a `reads` in the new form, an effect outside the fixed set (ADR 0595 §8, ADR 0596 §2, ADR 0597 §2) |
| The bus module creates, and the binding follows | live, by tool call: a module asking for a state, a log, objects and a ledger in `requires` receives each binding in its membership, written after `created`, never before; `show` gives each entry's template, settings and fill; a module calling `create` is refused |
| The bus module runs whole | one integration test in the nats module's repository that starts the bus module whole against the real bus server: a verdict, then `created`, then the binding in the membership; a restart that catches up a verdict given while it was down; a bucket whose settings differ raises `drifted`, the seat's `apply` of a larger cap clears it with `drift-cleared`; a refused create raises its condition and hands no binding; `recreate` and `drop` without the operator's warrant are refused, and with it act after a stream snapshot; an orphan is listed, raises `orphaned`, and goes only by `drop`; each template at a small cap refuses, events drop the oldest, and `full` is raised at the raise mark and cleared only below the clear mark; `recreated` rewrites the binding and `dropped` removes it; at start the first view from `indexer_bus_state` creates what a verdict given while it was down asked; the older remove tools refuse a new-form entry, naming `drop`'s tool |
| The bus module runs whole | one integration test in the nats module's repository that starts the bus module whole against the real bus server: a verdict, then `created`, then the binding in the membership; a restart that catches up a verdict given while it was down; a bucket whose settings differ raises `drifted`, the seat's `apply` of a larger cap clears it with `drift-cleared`; a refused create raises its condition and hands no binding; the process connects on its own process account, composed by the grants part with that account's grants and limits, never on an account without them; a `create` handed through `requests` is answered on the same key, which the tool watches; `recreate` and `drop` without the operator's warrant are refused, and a warranted `drop`, its warrant read from the operator channel's own record of the ask under the process's account and never from a stand-in, takes the entry's stream snapshot before it deletes, and acts within seconds of the bus module's `decided`, with no timer between; the same warrant handed in a second request is refused and deletes nothing; each event said is on `mesh.seat.mesh-broker.event.<event>` and appended to the bus module's history stream, and `history` reads it back; an orphan is listed, raises `orphaned`, and goes only by `drop`; each template at a small cap refuses, events drop the oldest, and `full` is raised at the raise mark and cleared only below the clear mark; `recreated` rewrites the binding and `dropped` removes it; at start the first view from `indexer_bus_state` creates what a verdict given while it was down asked; the older remove tools refuse a new-form entry, naming `drop`'s tool |
| A module asking bus state waits for the bus module | the module graph's table test: a manifest with a ledger entry has a requirement edge to the module providing `bus-state`, one without has none (ADR 0614 §5) |
| A foundation part's first view | unit tests of each owner and reader (ADR 0615's How it is checked), as table cases, and these cases of the whole-module integration tests that exist today (mesh-delivery is over its integration budget, so no new test function): the indexer's (`modules/mesh-delivery-indexer`, its one whole-module test, which the milestone's packages test merges into), the assigner's (`modules/mesh-delivery-assigner`) and the grants part's (`modules/mesh-grants`), and the tool runner's in mesh-tools: the owner answers its verb at once from what it holds, with its revision, before its own first view; the reader asks once at start, then only on a gap in revisions; a reader whose owner is down raises its condition only past the bound and never reads the owner's state, and the owner's answer clears it; an empty answer at revision 0 drops no login; an owner's event carries the changed value whole, and a reader applies a put and a delete from the events alone and never asks the verb; a revision lower than the one held makes the reader ask its first view again; an existing waiter on `manifest-accepted` or on the assigner's `put` does not wake on the new families; for the indexer's `verdicts` (ADR 0622), called by the grants part while the node's tool runner is stopped, it answers, and from any other account, the indexer's own included, it is refused; a missed revision on `manifest-refused` makes the grants part ask `verdicts` once more; the grants part and the indexer started at the same moment, `verdicts` answers within the bound with its revision; with the indexer down past the bound the grants part raises the condition naming the indexer and `verdicts` and never reads the indexer's state, and the answer clears it; the read tool shows the revision each reader holds, and the re-read tool asks once and records the hand act; in the
| A foundation part's first view | unit tests of each owner and reader (ADR 0615's How it is checked), as table cases, and these cases of the whole-module integration tests that exist today (mesh-delivery is over its integration budget, so no new test function): the indexer's (`modules/mesh-delivery-indexer`, its one whole-module test, which the milestone's packages test merges into), the assigner's (`modules/mesh-delivery-assigner`) and the grants part's (`modules/mesh-grants`), and the tool runner's in mesh-tools: the owner answers its verb at once from what it holds, with its revision, before its own first view; the reader asks once at start, then only on a gap in revisions; a reader whose owner is down raises its condition only past the bound and never reads the owner's state, and the owner's answer clears it; an empty answer at revision 0 drops no login; an owner's event carries the changed value whole, and a reader applies a put and a delete from the events alone and never asks the verb; a revision lower than the one held makes the reader ask its first view again; an existing waiter on `manifest-accepted` or on the assigner's `put` does not wake on the new families; for the indexer's `verdicts` (ADR 0622), called by the grants part while the node's tool runner is stopped, it answers, and from any other account, the indexer's own included, it is refused; a missed revision on `manifest-refused` makes the grants part ask `verdicts` once more; the grants part and the indexer started at the same moment, `verdicts` answers within the bound with its revision; with the indexer down past the bound the grants part raises the condition naming the indexer and `verdicts` and never reads the indexer's state, and the answer clears it; for the indexer's `graph` (ADR 0631), called by the assigner it answers with its revision, and from any other account, the indexer's own included, it is refused; a missed revision of the bucket `graph` on `indexer.views` makes the assigner ask `graph` once more; started with the indexer down, the assigner raises past the bound the first-view condition naming the indexer and `graph` and reads nothing of the indexer's state, and the answer clears it; the read tool shows the revision each reader holds, and the re-read tool asks once and records the hand act; in the assigner's whole-module test, the assigner starts before any new-form bus state exists, with no binding of the bus module, and the indexer, the grants part and the tool runner each get their first view of it. Live, after the delivery: the indexer's `indexer_old_sections`, which the milestone extends to count the `reads` entries on each owner's state, reads 0 declared `reads` entries on the indexer's state and 0 on the assigner's, and says the node-engine's reads granted in the grants part's code remain (§ 12.6); and each owner's read tool of § 12.2 answers what its bucket holds, each status view tool answers a session's call as before the move, and `mesh_search` finds each owner's read tool |
| The owners' first views are their own tools, in a runtime of their own (ADR 0621) | cases of the indexer's and the assigner's whole-module integration tests: each first-view tool, called from a reader's account while the node's tool runner is stopped, answers (§1); each answers its granted readers and refuses every other account, the owner's own included (§2); the two runtimes started at the same moment, each asking the other's first view at start, both answer within the first-view bound with their revisions (§3); and the milestone's group passes its merge check with no change to the judge's known fields for these verbs (§4) |
assigner's whole-module test, the assigner starts before any new-form bus state exists, with no binding of the bus
module, and the indexer, the grants part and the tool runner each get their first view of it. Live, after the delivery: the indexer's `indexer_old_sections`, which the milestone extends to count the `reads` entries on each owner's state, reads 0 declared `reads` entries on the indexer's state and 0 on the assigner's, and says the node-engine's reads granted in the grants part's code remain (§ 12.6); and each owner's read tool of § 12.2 answers what its bucket holds, each status view tool answers a session's call as before the move, and `mesh_search` finds each owner's read tool |
| A foundation part's first view comes within its bound | live, after the delivery: each reader's read tool (`grants_first_views`, the mesh MCP server's `mesh_first_views`, `indexer_first_views`, `assigner_first_views`) shows, for the indexer's and the assigner's verbs, the time from its ask to the owner's answer under the two-minute bound, and no first-view condition raised by the delivery |
| The bus module's first view | cases in the nats module's one integration test: a replayed verdict older than the first view is not applied; an `indexer_bus_state` that does not answer turns the health unhealthy naming it and is asked again on the backoff; a set-aside manifest missing from the first view keeps its `asked` key, an empty `manifest-accepted` removes it; a deleted module's entry, after its empty `manifest-accepted`, is listed by `orphans` and raises `orphaned`, and a set-aside module's entry is neither; `nats_first_view_again` asks at once and records the hand act |
| `manifest-accepted` is never lost silently | unit tests of the indexer: a failed publish is published again, and one still failing writes the finding; `indexer_say_accepted` says it again for one module from the verdict held, and refuses a module with no accepted verdict |
| Until the identity seat, the caller is the one the tool runner proved | a unit test of the bus module's process: a request on its tool subject naming another caller in its body acts for the proved caller; `drop` without the warrant is refused |
| A person's request goes through `requests`, and the process verifies the warrant itself and acts on it once (ADR 0626 §4, ADR 0259 §6) | the process's unit tests: a `recreate` and a `drop` written to `requests` with no warrant, with a warrant for another act or entry, with a warrant the operator channel's record does not hold, and with a warrant whose ask id the process already acted on are each refused, delete nothing and say why; a `create` or the seat's `apply` for an entry no accepted manifest asks for is refused; the stamp is the caller the tool runner proved, whatever the request's body names |
| The process's account, and the bus module's only bus declaration (ADR 0626 §1 to §3) | the judge's unit test: the bus module's manifest with its process's `bus` and `keeps` is accepted; `keeps` on any other process or module, any other bus declaration of the bus module, and a subject of its process on which a tool is answered are refused; the composition test: the process is granted the create, update, delete and snapshot subjects under modules' names, nothing on which a tool or a verb is answered, and no subscription to a restricted stream's subjects beyond the snapshot request, its acknowledgements and its inbox; the snapshot account is granted the same subjects as before; no tool runner and no tool of the bus module holds the bus's create, update or delete subjects |
| The process's fixes of review round 1 hold | the nats module's whole-module integration test and unit rows: an event or condition the bus did not take is kept, named by the health tool and said again at the next fill pass; a second accepted manifest asking for an entry raises no overdue condition; the warrant's wake on `decided` acts within seconds without a call to the pass by the test; a second request naming a spent ask is refused naming the act it spent; an ask past 12 hours is not acted on |
| Drift and orphans, live | live, made only by a manifest change: a manifest that shrinks a setting raises `drifted`; the conditions clear as above. Nothing is filled on the live bus, and no orphan is made there |
| Overdue and stopped are loud | live, carried by the change of the first module that moves in step 5, never by a merge made for the test alone: with the bus module's process stopped, an accepted manifest asking for an entry raises the overdue condition within five minutes, from the watchman, and a later `created` clears it; the bus module's health turns unhealthy when its pass is more than three minutes old |
| A person's tools reach the process, and only by role | live: a role without the dangerous grant is refused `drop`'s tool; the tool runner's account holds no grant of `create`, the seat's `apply`, `recreate` or `drop` |
| A person's tools reach the process, and only by role | live: a role without the dangerous grant is refused `drop`'s tool; the tool runner's account holds no grant of the bus's create, update or delete subjects |
| The author is the proven caller | the ledger's integration test: an `apply` naming another module's tool call is refused; the author stamped is the caller the tool runner proved, whatever the module passed |
| Only the owner reaches its data | live: another module's account reading an entry in the new form, a ledger's history, snapshot or state among them, is refused; a raw publish on a ledger's history is refused |
| The ledger keeps its promises | one integration test against the real bus (ADR 0597's How it is checked): two writers race and one is refused; two interleaved applies leave the state at the higher sequence; a write cut between history and state is repaired; a full state bucket answers taken with its sequence and catches up; an idle instance whose operations expired rebuilds from its snapshot; `rebuild`, `reindex` and `lag` repair by hand; a forged author is refused; every `apply` leaves the snapshot equal to the state; an index behind raises its condition and `reindex` clears it |
@@ -1057,6 +1110,12 @@ ruled on every finding. What was found, and what answers it:
| upgrade | the build order lacked the grants part's binding and two undo steps | design 25 § 12.6 |
| tools | the ledger's repairs had no tool for the next owner; the bus module's events had no read tool | design 25 § 12.5 (served for every ledger owner), § 12.3 |
**Answered since, 2026-11-04** (ADR 0626, and the registry milestone's ruling on where the seat's events go): the
process's own tool subject of the security and seams rows is gone, since a process account answers no tool call; a
person's request travels through the process's state `requests` and the process verifies the warrant itself
(§ 12.3); the process holds a process account of its own beside the snapshot account (§ 12.2); and the seat's
events are on the shared stream like every seat's, with the bus module's 30-day history its own stream (§ 12.3).
**Answered since**: the margin of `full` and the events of `drop` and `recreate` were the operator's answers of
2026-11-03 and are now ADR 0595 §6's progressive insight, with the correction of fact for ADR 0595 §4 (the bus
module deletes through `recreate` as well as `drop`); both wait for the operator's Approve and are written in § 12.2
+106 -18
View File
@@ -17,6 +17,8 @@ code:
- mesh-controller internal/inventory/migrations/0064-the-resolver-file-is-the-uplinks.sql
updated: 2026-11-04
decisions:
- "ADR 0625"
- "ADR 0627"
- "ADR 0608"
- "ADR 0613"
- "ADR 0620"
@@ -176,12 +178,13 @@ merges.
## A seat's definition (from 2026-11-03)
**A seat is defined once, in one language-neutral file, and every part learns it from the bus** (ADR 0608, from
research 081). The registry seats (design 51), `mesh-broker` (design 25 § 12.3) and the four seats the milestone
adds verbs or events to are the first defined whole this way, in the milestone of ADR 0612 (ADR 0613 §1,
ADR 0620).
research 081). The registry seats (design 51), `mesh-broker` (design 25 § 12.3), the four seats the milestone
adds verbs or events to, and `node-engine` are the first ten defined whole this way, in the milestone of ADR 0612
(ADR 0613 §1, ADR 0620, ADR 0627 §1).
- **The file** is a schema with conformance fixtures: the seat's verbs and their messages, its events and their
bodies, its state and the forms of its provisions. It lives in the contracts module of its area. Its Go and
bodies, its state and the forms of its provisions. It lives in the contracts module of its domain (*Where a seat's
code goes*, below). Its Go and
TypeScript types are generated from it and committed beside it; nobody writes them by hand. The format is JSON, its
messages and bodies JSON Schema, as the seat set's verb inputs already are, with the format's own schema checked in;
the fixtures are data, never functions.
@@ -212,6 +215,9 @@ ADR 0620).
`ask`, `cancel`, `condition` and `decided`; and `records` `asks`, as the holder's seat entry has them. The
holder's own, beside it: `refused`, and `offered` and `closed`, new, on its restricted stream (design 55 § 11).
mesh-sdk's seat list holds no entry for it today, so its definition is written from the holder's seat entry.
- `node-engine` (held by the node-engine on every node): the verb `manifest-reads` and its seat event
`manifest-reads-changed`, which the milestone gives it (ADR 0618 §3, §4; design 51), so it is defined whole too
(ADR 0627 §1).
**How it is checked**: each of these definitions' conformance fixtures names every event's subject, the seat's and
the holder's own, and a case of the holder's whole-module test takes a fixture from the running holder (an event
@@ -222,10 +228,11 @@ ADR 0620).
at once (ADR 0608 §2), holding the seat list's grain: its name, whether it is held once or on every node, its verbs
with their inputs and its provision, generated from the seat list mesh-sdk compiles today, so the move changes
nothing a reader sees (ADR 0613 §2). Its messages, events and state layouts are added to
a seat's definition when its area's contract next changes, which moves that area's seats whole into its contracts
module in that change (ADR 0613 §3); which area moves when is tracked by issue 167 (ADR 0613 §4).
a seat's definition when its domain's contract next changes, which moves that domain's seats whole into its contracts
module in that change (ADR 0613 §3); which domain moves when is tracked by issue 167 (ADR 0613 §4).
- **The seat set has one source.** The judge, the node-engine, the tool runner and claude-code each compile a copy of
the seat list today; all four import the set generated in `mesh-contracts` instead. The tool runner's descriptions
the seat list today; all four import the seat-set package generated in `mesh-contracts` instead, which is where
mesh-sdk's seat list moves (ADR 0625 §2), and mesh-sdk's `catalogue` reads the seat set from there. The tool runner's descriptions
and claude-code keep that compiled set, equal to the trunk's definition, in this milestone, so neither depends on
the indexer at its start; they move to `indexer_seat_definition` and its event when their next change moves them,
tracked on issue 1966.
@@ -256,15 +263,16 @@ ADR 0620).
`generated-types-out-of-step`, cleared only by a clean build end of the same contracts module. **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 contracts marker**: a contracts
- **Contracts modules.** A seat's contract is published by a contracts module, which holds the seats' contracts and
their client packages and no seat (ADR 0625 §4, §5); the seat's holder implements the contract. **The contracts marker**: a contracts
module's package artifact names the directory of its definitions in the field `generated-from`, and the judge
refuses a module carrying it that claims a seat, naming the seat. `mesh-contracts` declares two packages, Go and
TypeScript, in package series `0.1`, published through the two package seats (design 51) and taken by its users by
version, or inside one delivery group by the group's head. No `mesh-<area>-contracts` is created in this milestone:
an area's contracts module is created by the change that next moves its area. mesh-sdk keeps its name and the
shared runtime, and takes no new seat-specific type (ADR 0613 §5): a unit test of mesh-sdk refuses an exported
identifier or string constant naming a seat the definitions define. The two checks every change already runs, the
refuses a module carrying it that claims a seat, naming the seat. `mesh-contracts` declares its packages in Go and
TypeScript, cut per seat as *Where a seat's code goes* says, in package series `0.1`, published through the two
package seats (design 51) and taken by its users by version, or inside one delivery group by the group's head. No
`mesh-<domain>-contracts` is created in this milestone: a domain's contracts module is created by the change that
next moves its domain (ADR 0625 §6). mesh-sdk keeps its name and the shared runtime, and takes no new seat-specific
type (ADR 0613 §5): a unit test of mesh-sdk refuses an exported identifier or string constant naming a seat the
definitions define. The two checks every change already runs, the
build modules' compare step and that test, are where a stray file and a stray type are refused; mesh-catalog's own
checks cannot see another repository.
- **A contracts cycle holds nothing.** `mesh-contracts` is published through the forge module's package seats and
@@ -277,6 +285,84 @@ ADR 0620).
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.
### Where a seat's code goes
*Added 2026-11-04* (ADR 0625, ADR 0627; the operator, 2026-11-04: "many small sdk's instead of 1 large sdk"). A
module is rebuilt only for the seats it uses, so a seat's code is cut per seat, in the contracts module of its
domain; mesh-sdk keeps what every module shares.
```
contracts module (mesh-contracts, or mesh-<domain>-contracts)
├── definitions and fixtures written by hand, the generation's source, outside the published packages
├── one package per seat defined whole, per language generated: types, subjects, a typed call per verb
│ └── its client package (where callers share logic over the seat) written by hand
└── the seat-set package generated: every seat not yet defined whole, at the seat list's grain
mesh-sdk the runtime every module uses, and seat-neutral parts that several seats' clients or holders share
imports of a contracts module: only `catalogue`'s, of the seat-set package
```
- **Each seat defined whole has a package of its own, per language** (ADR 0625 §1, ADR 0627 §1): its generated
contract, and a module imports only the packages of the seats it uses, so a change of that seat rebuilds only its
users. Where the fingerprint counts a language's module rather than its package, each such package is a module of
its own in the same repository. In the milestone of ADR 0612 these are the ten seats above.
- **Every other seat is in one seat-set package** in `mesh-contracts` (ADR 0625 §2), holding each seat's name, scope,
verbs and provision at the seat list's grain. A change of any of them rebuilds every importer of that package,
mesh-sdk's importers included, until its domain moves and it is defined whole.
- **The imports run one way per package** (ADR 0625 §3): a generated package imports nothing of mesh-sdk; mesh-sdk's
only import of a contracts module is `catalogue`'s, of the seat-set package; a client imports its own seat's
contract and mesh-sdk's runtime. Where a language's module is the unit of import, the clients are a module of
their own in the repository, so the two never form a cycle.
- **A client package** (ADR 0625 §4) is the one hand-written package a seat may carry beside its contract, owned by
the seat's contracts module, never by a holder. It holds the logic two or more callers would otherwise each write
over that seat, and only where such logic exists; it uses only its own seat's contract and mesh-sdk's runtime,
never another seat's client, and never names a holder, a module's tool or a module's subject. A seat's types and
typed calls are its contract's, never its client's.
- **Seven clients come in the milestone of ADR 0612** (ADR 0627 §1), for the seats whose shared logic mesh-sdk holds
today, each moved out of mesh-sdk with every importer in the same group, mesh-sdk keeping no copy and no alias:
`operator-channel` (an ask and its awaited answer, mesh-sdk's `asks` today); `mesh-vault` (the calls its callers
share; mesh-sdk's hand-written vault types are replaced by the vault contract's, not moved into the client);
`node-engine` (the checks of its compose-candidate ask and answer); and `npm-package-registry`,
`go-package-registry`, `container-image-registry` and `oci-artifact-registry` (publishing a file in parts, one
client per seat, each binding mesh-sdk's seat-neutral parts transfer to its own seat's verbs and types).
- **Moving a file in parts stays seat-neutral in mesh-sdk, on both sides** (ADR 0627 §2): the sending side and the
going on from what a holder already holds, and the holder's side (taking the parts into the holder's own objects,
checking their hashes, dropping an upload left idle, saying when its objects fill), name no seat, verb or seat's
type and take subjects, shapes and checks from their caller. Each registry seat's client binds the sending side,
and each holder (the distribution module, the forge's two package seats) binds the receiving side to the seat it
holds, so no holder copies another's transfer.
- **In a contracts module's published packages every file outside a client is generated** (ADR 0625 §5): the build
modules' regenerate-and-compare step fails any other hand-written file there, naming it. The definitions and their
fixtures sit outside the published packages and are not judged by that step.
- **The contracts modules follow the glossary's domains** (ADR 0625 §6): a domain's seats move into
`mesh-<domain>-contracts`, named after the domain's heading in the glossary, when that domain's seats move; a seat
of the Core domain, or of a domain with no contracts module yet, is in `mesh-contracts`. The order of the domains
stays issue 167's (ADR 0613 §4).
- **A seat defined outside the mesh's own contracts has a contracts module of its own** (ADR 0625 §7), which may
share a repository with its first holder; its definition, contract and client never live in the holder module, so
another module can hold the seat by importing only the contract, and the holder can be retired while the contract
stays.
- **A role only one module could ever play is no seat** (ADR 0625 §8): it is that module's own tools and events,
reached by the module's name (ADR 0596 §2). The test is whether another module could take the role over, not how
many hold it today, so every seat in force stays a seat, `mesh-broker`, `node-engine` and `route-proxy` included.
- **mesh-sdk takes only the runtime every module uses** (ADR 0625 §9, as ADR 0627 §2 widens it): the bus connection,
the tools bundle's runtime, state and its helpers, and a seat-neutral part two or more seats' clients or holders
share, as long as it names no seat, verb or seat's type. After the milestone it holds no package, type or function
over the seven seats above (ADR 0627 §4); the value layouts of seats not defined whole stay in it until their domain
moves, as ADR 0613 §3 has it.
**How it is checked** (ADR 0625 §11, ADR 0627 §3, both built in the milestone of ADR 0612):
| Rule | Checked by |
|---|---|
| One package per seat defined whole, one seat-set package | a unit test of the generator (one program in the Go build module, emitting both languages and run by the Node build module too) emitting one package per seat defined whole and one seat-set package; the generator refuses a seat marked whole in the seat set without its own package; the judge refuses a contracts module whose package holds the generated files of two seats defined whole, naming them |
| The imports run one way | a mesh-sdk unit test that no package but `catalogue` imports a contracts module, and `catalogue` only the seat-set package; the generator tests check no generated package imports mesh-sdk |
| mesh-sdk holds no logic over the seven seats | mesh-sdk's package-list test refuses by name `asks`, the vault's types and its shared calls, the compose-candidate checks and any seat-named publish code, and refuses the seven seats' names in mesh-sdk outside `catalogue`, so a copy kept without importing a contract fails too |
| A client uses only its own seat's contract | the catalog check of a client: its own test refuses a client importing another seat's client and one naming a module's tool or subject, and passes the seven clients |
| The parts transfer is seat-neutral | its unit test runs both sides against two different subjects and shapes the caller gives, and the seat-name check holds it seat-neutral; the review refuses a holder that takes parts in without it |
| Only a client is written by hand in a published package | each build module's regenerate-and-compare test fails on a hand-written file outside a client package and passes a contracts module holding its definitions and fixtures |
| A contracts module holds no seat; a role only one module could ever play has none | the judge's refusal of a contracts module that claims a seat; review both ways: a new seat definition's pull request is asked whether another module could take the role over, and a module's pull request is asked the same of each new tool or event family other modules call |
Acceptance tests: research 081's 14, 21, 25 and 26; a unit test that the generated seat set equals the seat list
mesh-sdk compiled before the move, entry for entry; mesh-sdk's seat-naming test; each build module's compare step
failing on a differing, a missing and an unwritten file, naming each; the checker's `verdict` naming out-of-step
@@ -478,6 +564,8 @@ and 32 by the four reviewers by architectural domain; the findings and what answ
indexer learns out-of-step types from the check's verdict event and runs no generator (seams), findings go to the
condition keeper (health), a pull request's entries are removed when it closes (running qualities).
The milestone's amendment (ADR 0612, ADR 0613, ADR 0614: nine seats defined whole, the rest at the seat list's grain,
the contracts marker, out of step learned from the checker's `verdict` and the builder's build end, the seat set's one
source) awaits its own review by architectural domain, which rewrites this section.
The milestone's amendment (ADR 0612, ADR 0613, ADR 0614, ADR 0620: the seats defined whole, the rest at the seat
list's grain, the contracts marker, out of step learned from the checker's `verdict` and the builder's build end, the
seat set's one source), with the amendment of 2026-11-04 (ADR 0625, ADR 0627: ten seats defined whole, a package per
seat, the seat-set package in `mesh-contracts`, the seven clients and the seat-neutral parts transfer, *Where a
seat's code goes*), awaits its own review by architectural domain, which rewrites this section.
@@ -306,6 +306,12 @@ replaced by one form, a **provision** of the bus module, as a database is postgr
of a module's kept data it asks through that module's tools or seat verbs (§ 4's tool relationship, `serves`),
and what a module lets others hear is its declared events, heard through `consumes` (ADR 0363, unchanged; ADR 0596
§1). A bucket's layout is never a contract another module depends on; a tool or a declared event is.
- **A state may be read-only to the module's tools.** A state's declaration may carry `read-only: true`: only the
processes of the module whose `bus.state` names that state write it, and the module's tools read it, holding no
publish on its subjects. The judge refuses a read-only state that no process of the module names. How it is checked:
the judge's unit tests for the refusal, and the composition test that a tools bundle's account holds a read on the
state and no write. The bus module's state `spent` (design 25 § 12.3) is the first user. (A design limit of the
milestone's review; no record decides it yet.)
- **The move is a module at a time** (ADR 0595 §11, ADR 0596 §3), each when it is next touched, keeping its local
names and its old settings as options so its data stays. A reader moves after its owner: the owner declares the
event or gains the tool its readers need first, and the judge refuses a moved module's subscription to an event
@@ -482,6 +488,12 @@ complete, because a team that believes it is complete stops reviewing for the ca
- **A package is declared as an artifact**: its name, the kind `package`, its language (`go` or `typescript`), its
paths and its **package series** (`0.1`). An image, a bundle or an archive may declare a package series the same
way; one that declares none is numbered in `0.1`. Nobody types a version: the builder counts it (design 51).
- **A package is named by its name in its own language** (a design limit of the registry milestone, 2026-11-04): a
Go package by the module path of its `go.mod`, a TypeScript package by the name in its `package.json`. The
indexer reads that name at each merge, the builder numbers the package under it, the package's registry seat
publishes it under it, and a user's `packages` names it so. One name per package across the mesh, because the
language's own tools fetch by that name and no other. How it is checked: the judge refuses a name in `packages`
that no package of the shelf is named, or that a member of the change's delivery group does not name at its head.
- **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 the mesh packages it takes in a section of its own, `packages`**, each with the package series it
@@ -24,6 +24,7 @@ decisions:
- "ADR 0607"
- "ADR 0610"
- "ADR 0611"
- "ADR 0627"
- "ADR 0595"
- "ADR 0596"
- "ADR 0615"
@@ -138,8 +139,15 @@ the verb.
module sends only those through `publish-part` (transfer, part digest, sequence, last), each at most 1 MiB, with
mesh-sdk's bus helper for sending in parts, added to mesh-sdk's runtime in the milestone's one rebuild. A `publish` opening a
transfer already open answers the parts held, so a cut transfer goes on from there. The parts land in the holder's
own object store `incoming`, asked for as bus state in its manifest (design 25 § 12) with a cap of 20 GiB and a lifetime of 24 hours; a transfer with
no part for 24 hours is dropped and its parts deleted. The holder hashes every part and refuses one whose bytes do not
own object store `incoming`, asked for as bus state in its manifest (design 25 § 12) with a cap of 8 GiB, the most the
objects template allows (design 25 § 12.1), and no lifetime, since that template keeps for good; the holder counts its
fill against the cap its binding carries, never a number of its own. The holder itself drops a transfer with no part
for 24 hours and deletes its parts (ADR 0611 §3), so an abandoned transfer does not stay. **Both are mesh-sdk's
`parts.Receiver`, shared by every holder** (ADR 0611 §3 for the drop, ADR 0627 for the shared receiving side), never copied into a holder: an expiring key per open
transfer, in a state of the holder with a key lifetime, is armed when a transfer is opened or opened again, and its
expiry (an event, never a timer) drops the transfer with its parts, or arms it again for the rest of the day if a
part came; a key that cannot be written fails the opening loudly. The cap is read once from the binding's fill and
kept; only where no binding is read does the holder state the 8 GiB it asked for. The holder hashes every part and refuses one whose bytes do not
match its digest. At 80 % of the cap it writes a finding (cleared below 80 %); at the cap it refuses a new transfer
with `refused` naming the cap. Its tool `registry_incoming` lists open transfers and the fill, and drops one by hand.
- **The holder asks the builder**, through `builder_versions`, whether the name, version and digest are recorded, and
@@ -161,6 +169,10 @@ the verb.
want whose build carries it reaches a node only once that node's engine states the capability to read both forms
and both seat names; the assigner holds the node's want meanwhile and names the capability as the reason, and a
node away keeps its want and catches up on return (ADR 0612 §2, made general by ADR 0614 §4).
- **The forge's accounts and teams are what the catalog declares.** The forge module removes, at each start and
when its teams change, the teams and the accounts of the retired per-node package-reading form that nothing
declares any more; one the forge refuses to delete because it owns something has its login prohibited. How it is
checked: after a delivery the forge module's team and account tools list none of them.
- **No build module holds a registry's write credential** (ADR 0605 §5). The npm write token goes. The image push
credential and the artifact store's write binding that ADR 0605 §5 revokes do not exist today: the store takes
writes from any process on the mesh network, so the rule protects nothing until its write side is closed. So
@@ -436,7 +448,13 @@ modules' manifests for such an edge. The done keys written in the new form stay
revert and are not built again for their form; each still carries every form a counted node-engine states, the
oldest in its single reference, so a node is held from them only when its engine reads none of those forms (ADR 0624
§1-3). **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`.
to the store on its node's loopback, with no credential, as the publish contract allows for a write that reaches the
store from its own node, and records their references under the seat's reach. No one-off credential exists: the
node-engine writes no push credential and the holder's front keeps no bootstrap token. How the rest of the bootstrap
goes is research 075's. **A fresh mesh cannot raise the front of `distribution` by that push alone**: the front is
the holder's program, a bundle the builder builds and publishes through the front itself (ADR 0605 §7), so the
bootstrap must also push that bundle to the loopback store, and the holder's node fetch it there, with the first steps
naming the loopback reference until the front serves. That is the genesis fix of issue 1152, not this milestone's.
### Acceptance tests
@@ -449,7 +467,11 @@ test).
- **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
`builder_untag` removes a branch's tagged versions with `removed`; a tagged version whose digest another tag names
keeps its tag on the store pinned today (a registry that cannot remove a tag alone), the holder answers `kept` with
that reason, `removed` is not said, and the builder records the version as kept on the store under its shared digest,
which `builder_versions` names so (it is not recorded as gone), and its bytes go with the other tag's removal, the move to a registry that
removes a tag alone being the next milestone's; 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
@@ -461,7 +483,8 @@ test).
- **Holder (gitea, distribution).** A caller not on the caller list is refused for each verb, and one module naming
another as the caller in the request's body is refused; a part sent by a module other than its transfer's opener is
refused; a transfer cut in the middle resumes from the parts held,
and one left 24 hours is dropped; at the cap a new transfer is refused naming the cap; a version a `go.mod` requires
and one left 24 hours is dropped by the holder, its parts deleted; at the cap a new transfer is refused naming the cap
its binding carries, and the 80 % finding is written against that cap; a version a `go.mod` requires
survives retention, and an unreadable `go.mod` stops the run; 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 Go seat that stops answering while its holder runs writes its finding and clears it when it
@@ -531,8 +554,13 @@ test).
`mesh.seat.node-engine.event.manifest-reads-changed`, and no other module's subscriptions hold it.
- **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`.
- **Vault.** Reverting the milestone's merge mints the build modules' credentials again; the bootstrap holds no
credential of its own to revert.
- **Round 1's fixes, unit and integration cases of the modules' existing tests.** A shared-digest tagged `remove` on the
store's 2.8 form answers `kept`; the idle drop fires on the key's expiry, including one that fell due while the holder
was stopped (checked at its start); `family-not-heard` is raised when a family stays silent past its bound, and the
builder's `publish-refused` and `publish-unanswered` findings name the name and the seat, each cleared when the
family or the answer returns.
- **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. After the delivery: