Author SHA1 Message Date
jschoubben cb4b3c2398 Prove a push over SSH from g14 (issue 955) 2026-10-17 02:30:30 +02:00
25 changed files with 338 additions and 1190 deletions
+49 -105
View File
@@ -98,9 +98,6 @@ and repair).
*Not:* ~~tools-sdk~~ *Not:* ~~tools-sdk~~
- **invokes** — the manifest word for the tools a module calls, `<module>.<tool>` each or `*` for every - **invokes** — the manifest word for the tools a module calls, `<module>.<tool>` each or `*` for every
one. A grant on the publish side and nothing else; a module that declares none calls nothing. one. A grant on the publish side and nothing else; a module that declares none calls nothing.
- **run for** — an assignment, or a start of a module, that serves one agent identity with that identity's machine
account, home and bus password. Only a module whose manifest declares it may be run for an identity; running for
the operator is the default run, as the operator account, with no setting (ADR 0412).
- **upgrade policy** — how a module's new builds reach its nodes: `roll` (one node first, judged, then - **upgrade policy** — how a module's new builds reach its nodes: `roll` (one node first, judged, then
the rest), `together`, or `record` (register the build, send it nowhere) the rest), `together`, or `record` (register the build, send it nowhere)
(ADR 0236). (ADR 0236).
@@ -227,14 +224,14 @@ every other domain has something to run on. **Recorded by** the controller and t
the two and raises `mirror-drifted` on any difference the two and raises `mirror-drifted` on any difference
(ADR 0289, ADR 0356). Builds, rollouts and assignments are no mirrors: they are (ADR 0289, ADR 0356). Builds, rollouts and assignments are no mirrors: they are
the chain, kept on the bus as their only source (ADR 0408). Shortened to *mirror* where the the chain, kept on the bus as their only source (ADR 0408). Shortened to *mirror* where the
text is about the controller's records; the builder's *mirrored* `upstream` image (Delivery) is text is about the controller's records; the builder's *mirrored* `upstream` image (Change and delivery) is
a different thing, a copy of an outside image in the artifact store. a different thing, a copy of an outside image in the artifact store.
## Placement — what runs on which node ## Placement — what runs on which node
**Purpose.** To decide, send and apply what each node runs, and to say why. **Purpose.** To decide, send and apply what each node runs, and to say why.
**Recorded by** the controller's `inventory` database. **Upstream of** Provisioning, Connectivity, Data **Recorded by** the controller's `inventory` database. **Upstream of** Provisioning, Connectivity, Data
and Health and repair. **Decided by** ADR 0100, 0126, 0176, 0181, 0207, 0221, 0252, 0261, 0411. and Health and repair. **Decided by** ADR 0100, 0126, 0176, 0181, 0207, 0221, 0252, 0261.
**Uses:** node, machine, module, seat, manifest (Module), config value (Config). **Uses:** node, machine, module, seat, manifest (Module), config value (Config).
- **assignment** — a module put on a node. Its config values on that node (module-assignment config, - **assignment** — a module put on a node. Its config values on that node (module-assignment config,
@@ -267,15 +264,15 @@ and Health and repair. **Decided by** ADR 0100, 0126, 0176, 0181, 0207, 0221, 02
other things (see [Homonyms](#homonyms)). other things (see [Homonyms](#homonyms)).
- **apply** — the node-engine making its node match its declaration; **reconcile** is doing so again - **apply** — the node-engine making its node match its declaration; **reconcile** is doing so again
until nothing differs. until nothing differs.
- **operator account** — the machine account of the operator's person identity on a node, read from the - **operator account** — the login name of the person who works on a node, stated on the node record;
identity and no longer stated on the node record; empty for a node nobody logs into. Everything the mesh empty for a node nobody logs into. Everything the mesh places under a person's home is resolved
places under a person's home is resolved against this account's home and owned by it against this account's home and owned by it
(ADR 0181, ADR 0411). (ADR 0181).
Not a name a manifest carries. Not a name a manifest carries.
- **agent account** — the machine account of the agent identity the identity seat marks as a node's agent account, - **agent account** — the login every coding agent on a node runs as, when the node names one of its own;
on the operator's Approve, never by an assignment or a config value. Where none is marked, agents run as the stated on the node record at the controller's terminal, never by a verb or a config value. Empty means agents
operator account. It cannot run as the operator account. It cannot become root without a person, and the node-engine judges that on
become root without a person, and the node-engine judges that on every look (ADR 0266, ADR 0411). every look (ADR 0266).
- **account group** — a group of the machine's group database that a module puts an account in, by - **account group** — a group of the machine's group database that a module puts an account in, by
declaring the account with that group and nothing else of it. Several modules may each add one to the declaring the account with that group and nothing else of it. Several modules may each add one to the
same account; the mesh takes back only an account group it added same account; the mesh takes back only an account group it added
@@ -343,8 +340,8 @@ and vault (Identity and access).
**Purpose.** To issue, hold, rotate and check the credentials and grants by which modules, nodes, agents **Purpose.** To issue, hold, rotate and check the credentials and grants by which modules, nodes, agents
and the operator act. **Recorded by** the controller's `identity` and `licences` databases and the vault. and the operator act. **Recorded by** the controller's `identity` and `licences` databases and the vault.
**Upstream of** Provisioning, Delivery, and Operator and conversation. **Upstream of** Provisioning, Change and delivery, and Operator and conversation.
**Decided by** ADR 0085, 0113, 0183, 0225, 0234, 0386, 0387, 0388, 0411, 0413, 0414. **Decided by** ADR 0085, 0113, 0183, 0225, 0234, 0386, 0387, 0388.
**Uses:** module, node, seat, tool (Module), operator, consumer and provision (Provisioning), agent and ask (Operator and conversation). **Uses:** module, node, seat, tool (Module), operator, consumer and provision (Provisioning), agent and ask (Operator and conversation).
- **credential** — anything that proves an identity: a password, a key, a token. A **pair credential** - **credential** — anything that proves an identity: a password, a key, a token. A **pair credential**
@@ -358,13 +355,8 @@ and the operator act. **Recorded by** the controller's `identity` and `licences`
- **vault** — the module that owns every secret (`mesh-vault`, - **vault** — the module that owns every secret (`mesh-vault`,
[24 — The secrets vault](../03-DESIGN/01-to-be/24-the-secrets-vault.md)). [24 — The secrets vault](../03-DESIGN/01-to-be/24-the-secrets-vault.md)).
- **identity** — a person, an agent or a module as the mesh knows it: a name, a kind, a role, and whether it - **identity** — a person, an agent or a module as the mesh knows it: a name, a kind, a role, and whether it
is switched on; a person or an agent also has a login name, the machines it is present on and, for a person, is switched on. Every identity and role is held by the identity seat, in the bus's key-value store, and
the login kept on a machine where it differs. Every identity and role is held by the identity seat, in the changes only through its verbs (ADR 0386).
bus's key-value store, and changes only through its verbs (ADR 0386, ADR 0411). Each person and agent
identity has a bus account of its own, carrying its role (ADR 0413).
- **machine account** — an identity's login on one machine, made by the mesh from the identity: the
controller composes it and the node-engine applies it, adopting a login that exists and never renaming or
deleting one (ADR 0411).
- **role** — what an identity may call, as a set of grants (ADR 0386). Not a seat: a bare *role* is a - **role** — what an identity may call, as a set of grants (ADR 0386). Not a seat: a bare *role* is a
homonym (see [Homonyms](#homonyms)). homonym (see [Homonyms](#homonyms)).
- **grant** — one line of a role: an effect everywhere, an effect on one seat, or one named tool, and may be - **grant** — one line of a role: an effect everywhere, an effect on one seat, or one named tool, and may be
@@ -397,13 +389,12 @@ and the operator act. **Recorded by** the controller's `identity` and `licences`
agent can run as that account: the control-node, and a node whose agents run under an account of their own agent can run as that account: the control-node, and a node whose agents run under an account of their own
(ADR 0272). (ADR 0272).
## Delivery — a commit on its way to the nodes ## Change and delivery — a commit on its way to the nodes
**Purpose.** To take one commit from its pull request to every node that should run it, and to put it **Purpose.** To take one commit from its pull request to every node that should run it, and to put it
back when it fails. The domain is continuous integration and delivery, named **delivery**, and its parts live in back when it fails. **Recorded by** the `mesh-delivery` module (deliveries) and the controller (the
the repository `mesh-delivery` (ADR 0429 §2). **Recorded by** the `mesh-delivery` module (deliveries) and the planner, the rollout). **Downstream of** Module, Core, Health and repair and Data.
controller (the planner, the rollout) until release 6 of ADR 0429; from each release on, by the parts it builds. **Downstream of** Module, Core, Health and repair and Data. **Decided by** ADR 0162, 0218, 0236, 0237, 0238, 0239, 0250, 0267, 0270, 0276, 0323, 0401–0408.
**Decided by** ADR 0162, 0218, 0236, 0237, 0238, 0239, 0250, 0267, 0270, 0276, 0323, 0401–0408, 0428, 0429.
**Uses:** module, node, declaration and send (Placement), health (Health and repair), person. **Uses:** module, node, declaration and send (Placement), health (Health and repair), person.
- **merge check** — the two statuses a pull request carries before it may merge: the **merge gate** - **merge check** — the two statuses a pull request carries before it may merge: the **merge gate**
@@ -419,7 +410,7 @@ controller (the planner, the rollout) until release 6 of ADR 0429; from each rel
(ADR 0237). (ADR 0237).
*Not:* ~~build machine~~ *Not:* ~~build machine~~
- **build queue** — the builds waiting to run, kept by the bus as three work queues a builder takes in order: - **build queue** — the builds waiting to run, kept by the bus as three work queues a builder takes in order:
delivery builds, then checks of pull requests whose Approve is asked or given (ADR 0421 §10), then every other check; each check queue keeps one delivery builds, then checks of pull requests marked ready, then every other check; each check queue keeps one
request per pull request, the newest (ADR 0406); one entry in it is a request per pull request, the newest (ADR 0406); one entry in it is a
**build request**. **build request**.
The queue's verbs still say *ask* for an entry; the conversation's ask is another thing The queue's verbs still say *ask* for an entry; the conversation's ask is another thing
@@ -474,58 +465,24 @@ controller (the planner, the rollout) until release 6 of ADR 0429; from each rel
- **delivery plan** — what a delivery does to the mesh: its **build plan** (the modules it moves and - **delivery plan** — what a delivery does to the mesh: its **build plan** (the modules it moves and
their dependents, in tiers), its **deploy plan** (per node, what it receives and what waits for a their dependents, in tiers), its **deploy plan** (per node, what it receives and what waits for a
person) and its verdict (the composed nodes, the replays). Computed by the controller's planner from a person) and its verdict (the composed nodes, the replays). Computed by the controller's planner from a
diffset; the controller's verb `delivery-plan` shows it. *Deploy* is used in this one place. Deleted at diffset; the controller's verb `delivery-plan` shows it. *Deploy* is used in this one place.
release 6 of ADR 0429 (§8): the word stays in use until that release is delivered, and then retires.
*Not:* ~~change plan~~, ~~release plan~~ *Not:* ~~change plan~~, ~~release plan~~
- **merge window** — the rolling wait in which the controller collects merges before it plans them: each - **merge window** — the rolling wait in which the controller collects merges before it plans them: each
merge restarts it, and it closes when none arrived for its length (90 seconds by default) or at the latest merge restarts it, and it closes when none arrived for its length (90 seconds by default) or at the latest
10 minutes after its first merge (ADR 0276). Kept as two keys with a time to live on the bus; the first to 10 minutes after its first merge (ADR 0276). Kept as two keys with a time to live on the bus; the first to
expire closes the batch (ADR 0407). Goes at release 6 of ADR 0429, when nothing collects merges (§12). expire closes the batch (ADR 0407).
- **batch** — the merges one merge window collected, planned once as one rollout. For a repository merged more - **batch** — the merges one merge window collected, planned once as one rollout. For a repository merged more
than once in it, the rollout takes the latest commit, and the earlier merges are answered by it. Not *group*, than once in it, the rollout takes the latest commit, and the earlier merges are answered by it. Not *group*,
which is a delivery group. Goes at release 6 of ADR 0429 (§11, §12). which is a delivery group.
- **mesh-delivery** — the repository of the delivery domain, holding its parts as modules prefixed - **mesh-delivery** — the module that owns deliveries and delivery groups, holding the mesh-scoped seat
`mesh-delivery-` (ADR 0429 §2), made by the forge module from its config (ADR 0428). Until release 3 of of the same name. It records and decides; the controller sends, judges and rolls back when it is asked.
ADR 0429 also the module in the catalogue that owns deliveries and delivery groups, holding the mesh-scoped
seat of the same name: it records and decides, and the controller sends, judges and rolls back when it is
asked. Release 3 splits that module into the parts, and then the name means the repository alone.
- **part** — one independent process of the delivery domain with its own bus credential, which watches keys
and writes keys and never calls another part (ADR 0429 §1). Each key has one writer; the operator's
credential may also write a want, the one manual escape. The parts: the **forge bridge** (forge events in,
the only caller of the forge), the **indexer** (fingerprints, the candidates of a group, the modules of the
trunk), the **builder** (claims and builds one fingerprint once), the **checker** (the merge check on built
artifacts), the **gate** (merges a group and moves the source revision; *gate part* where it could be read
as another gate, see [Homonyms](#homonyms)), the **teller** (the operator's phone, and the only writer of an
ask's answer), the **assigner** (writes each node's wants by its six rules), the **node agent** (the
node-engine's own process, applying its wants), the **watchman** (presence and pulses) and the **status
view** (`mesh-delivery-cli` and a web view, writing only its answers to asks). The reviewer is the review
agent's session, not a part. Built over releases 1 to 6 of ADR 0429.
- **fingerprint** — what identifies a module's build by its content: a hash over its source files, its
manifest and the fingerprints of its build-time inputs, so equal content gives an equal fingerprint whatever
the commit (ADR 0429 §5). It is today's source fingerprint with a base entering by its module's
fingerprint, the builder by its own module's fingerprint, and a build resolving packages by its lock. From
release 4 a build's key, so one source is never built twice.
- **source revision** — one revision of the key `source.main`, written only by the gate: every repository's
merged head and the group that moved it. A group reaches the mesh as one source revision (ADR 0429 §11); a
source revision is **delivered** when every want equals its module's fingerprint and every have equals its
want and is healthy, derived from the keys and recorded by no part (§1).
- **canary** — the first node of a module's nodes, ordered by ring then name: a new fingerprint reaches it
first and the module's other nodes only once it runs healthy there (ADR 0429 §6). Replaces sending to every
machine at once from release 6 (§15).
- **want** and **have** — a want (`want.<node>.<module>`) is the fingerprint a node should run, written by the
assigner; a have (`have.<node>.<module>`) is what the node runs, its phase and health, written by that node's
agent (ADR 0429 §4, §6). A node's facts that no module owns are its node key. They replace assignments and
rollouts at release 6 (§17); haves are written from release 1.
- **pulse** — a key the watchman writes for each key whose next move a machine owes, with a time to live; its
expiry is a stall, raised as a condition, so no part needs a timer (ADR 0429 §1, research 063). A person's
wait carries none.
- **rollout** — the controller's sending of one batch's builds across nodes, at most one trunk commit per - **rollout** — the controller's sending of one batch's builds across nodes, at most one trunk commit per
repository, tier by tier, to every node that runs each module at once (ADR 0276, ADR 0360). Rollouts that share a repository, tier by tier, to every node that runs each module at once (ADR 0276, ADR 0360). Rollouts that share a
repository, a module or a graph edge run one after another; others run side by side (ADR 0407). It names every repository, a module or a graph edge run one after another; others run side by side (ADR 0407). It names every
merge it answers, and its record is one key of the bus's rollouts bucket, a link of the chain (ADR 0408). A primitive the delivering stage merge it answers, and its record is one key of the bus's rollouts bucket, a link of the chain (ADR 0408). A primitive the delivering stage
asks for, not an object anyone manages. A **tier** is one step of a rollout: a module, then the modules asks for, not an object anyone manages. A **tier** is one step of a rollout: a module, then the modules
built against it. The controller's verb `plans` lists the rollouts. A module is rolled out by its `roll` built against it. The controller's verb `plans` lists the rollouts. A module is rolled out by its `roll`
policy. Rollouts and tiers go at release 6 of ADR 0429 (§8, §15), replaced by wants and haves (§17). policy.
*Not:* ~~walk~~ (hq) *Not:* ~~walk~~ (hq)
- **first-node gate** — the judgement on a delivery's first node: healthy three times, no witness put it - **first-node gate** — the judgement on a delivery's first node: healthy three times, no witness put it
back, no condition raised since the send (ADR 0236 §2). Not *the gate* bare (see [Homonyms](#homonyms)). back, no condition raised since the send (ADR 0236 §2). Not *the gate* bare (see [Homonyms](#homonyms)).
@@ -555,7 +512,7 @@ controller (the planner, the rollout) until release 6 of ADR 0429; from each rel
**Purpose.** To notice what is wrong with the mesh, say it once, repair what may be repaired unattended, **Purpose.** To notice what is wrong with the mesh, say it once, repair what may be repaired unattended,
and record what was done by hand. **Recorded by** the controller's condition store. and record what was done by hand. **Recorded by** the controller's condition store.
**Upstream of** Delivery (the first-node gate) and Operator and conversation (a condition **Upstream of** Change and delivery (the first-node gate) and Operator and conversation (a condition
becomes a message). **Decided by** ADR 0227, 0231, 0240, 0252, 0283. becomes a message). **Decided by** ADR 0227, 0231, 0240, 0252, 0283.
**Uses:** node, module, node-engine (Core). **Uses:** node, module, node-engine (Core).
@@ -597,7 +554,7 @@ becomes a message). **Decided by** ADR 0227, 0231, 0240, 0252, 0283.
**Purpose.** To know every item of data a module holds, how precious it is, and to keep it recoverable. **Purpose.** To know every item of data a module holds, how precious it is, and to keep it recoverable.
**Recorded by** the manifests' `data` declarations and every node's `node-backup` seat. **Recorded by** the manifests' `data` declarations and every node's `node-backup` seat.
**Upstream of** Delivery and Health and repair. **Decided by** ADR 0232, 0233, 0235. **Upstream of** Change and delivery and Health and repair. **Decided by** ADR 0232, 0233, 0235.
**Uses:** module, node, provider and consumer (Provisioning), person. **Uses:** module, node, provider and consumer (Provisioning), person.
- **data class** — how precious an item of data is, ranked by the operator: `irreplaceable`, - **data class** — how precious an item of data is, ranked by the operator: `irreplaceable`,
@@ -644,10 +601,10 @@ it should not. **Recorded by** the controller. **Upstream of** Provisioning and
**Purpose.** To let the mesh and its operator talk: tell, ask, answer, and act only on an answer it can **Purpose.** To let the mesh and its operator talk: tell, ask, answer, and act only on an answer it can
trust. **Recorded by** the router (asks, warrants and the operator's identities). trust. **Recorded by** the router (asks, warrants and the operator's identities).
**Downstream of** Health and repair, Delivery, and Identity and access. An authorising **Downstream of** Health and repair, Change and delivery, and Identity and access. An authorising
answer acts in whichever domain asked, through that domain's own verb; the conversation owns the answer acts in whichever domain asked, through that domain's own verb; the conversation owns the
asking, never the act. **Decided by** ADR 0152, 0175, 0234, 0259, 0272, 0383, 0384, 0385, 0390. asking, never the act. **Decided by** ADR 0152, 0175, 0234, 0259, 0272, 0383, 0384, 0385, 0390.
**Uses:** operator, person, node, module, seat, tool (Module), proof and identity (Identity and access), release (Delivery). **Uses:** operator, person, node, module, seat, tool (Module), proof and identity (Identity and access), release (Change and delivery).
- **mesh MCP server** — the endpoint on a node's loopback through which every agent and person on that - **mesh MCP server** — the endpoint on a node's loopback through which every agent and person on that
node reaches the mesh: the MCP server named `mesh`, with its five tools `mesh_overview`, node reaches the mesh: the MCP server named `mesh`, with its five tools `mesh_overview`,
@@ -661,8 +618,8 @@ asking, never the act. **Decided by** ADR 0152, 0175, 0234, 0259, 0272, 0383, 03
line as the **controller's terminal** or as an ordinary call line as the **controller's terminal** or as an ordinary call
(ADR 0272). (ADR 0272).
- **nox** — `mesh-cli`'s alias, in interactive zsh only. A script, `sudo` and a document meant to work everywhere - **nox** — `mesh-cli`'s alias, in interactive zsh only. A script, `sudo` and a document meant to work everywhere
say `mesh-cli`. The operator also calls the mesh itself *nox mesh*. *nox* is also the agent (ADR 0414 §7): see say `mesh-cli`. The operator also calls the mesh itself *nox mesh*; on this page and in the records *nox* is the
[Homonyms](#homonyms), and say which is meant. It is the one command the operator types: every line the mesh addresses to the operator — a alias alone. It is the one command the operator types: every line the mesh addresses to the operator — a
condition's words, a verb's answer, a README, a design — names a `nox` line, never a daemon's (the controller's condition's words, a verb's answer, a README, a design — names a `nox` line, never a daemon's (the controller's
or the node-engine's binary followed by a command), which `words.py` and the catalogue's check refuse or the node-engine's binary followed by a command), which `words.py` and the catalogue's check refuse
(ADR 0272, (ADR 0272,
@@ -767,7 +724,7 @@ it was told, and the servarr apps' own libraries. **Upstream of** nothing. **Dec
Not a domain of the mesh but of the way it is built, listed because its words meet the mesh's. Not a domain of the mesh but of the way it is built, listed because its words meet the mesh's.
**Recorded in** this repository, the issue tracker and the knowledge base. **Decided by** ADR 0019, 0080, 0244, 0275, **Recorded in** this repository, the issue tracker and the knowledge base. **Decided by** ADR 0019, 0080, 0244, 0275,
0284, 0290, 0291, 0423, 0424. 0284, 0290, 0291.
- **research effort**, **graduation**, **hand-off**, **playbook** — an investigation, held whole in the - **research effort**, **graduation**, **hand-off**, **playbook** — an investigation, held whole in the
knowledge base and named "research NNN"; knowledge base and named "research NNN";
@@ -781,23 +738,14 @@ Not a domain of the mesh but of the way it is built, listed because its words me
(ADR 0290). Not a *rule*, which is a line of (ADR 0290). Not a *rule*, which is a line of
`00-META/how-we-build.md`. `00-META/how-we-build.md`.
- **knowledge base** — what holds the decision records and research efforts whole (number, status, topic, - **knowledge base** — what holds the decision records and research efforts whole (number, status, topic,
typed links, clauses and reasoning, with every version), and, once they are moved, the designs and the glossary typed links, clauses and reasoning, with every version), takes them as proposals, has them reviewed and accepted,
entries too (ADR 0423); takes them as proposals, has them reviewed and accepted,
and answers what holds: the seat `knowledge-base`, held by the module `mesh-knowledge` and answers what holds: the seat `knowledge-base`, held by the module `mesh-knowledge`
([55 — The knowledge base](../03-DESIGN/01-to-be/55-the-knowledge-base.md)). Not **the record**, the literal reader ([55 — The knowledge base](../03-DESIGN/01-to-be/55-the-knowledge-base.md)). Not **the record**, the literal reader
of this repository. of this repository.
- **design** — a record of the knowledge base, named "design NN", with a **to-be** section (what is being built) - **design** — a document in `03-DESIGN/`: **as-is** (what runs) or **to-be** (what is being built).
and an **as-is** section (what runs), its owning repositories and its links to its decisions and other designs - **milestone** — one step of a design's work breakdown, usable on its own once delivered and named by what the
(ADR 0423). Until the move, a document in `03-DESIGN/`, in `01-to-be/` or `00-as-is/`. operator or the mesh can then do; delivered as one feature (playbook 04, ADR 0401).
- **design state** — of a design: proposed, designed, in progress or implemented, worked out by the knowledge base - **epic** — a run of milestones in one design, released milestone by milestone (ADR 0401).
from the issues with a design link to it and whether their deliveries are done; never written by anyone
(ADR 0424).
- **glossary entry** — one entry of the glossary as a record of the knowledge base: its head word or words, its
domain, its meaning, its *Not:* line and its identifier line (ADR 0423). Until the move, an entry of this page.
- **milestone** — a group of a design's issues delivered together, as one delivery group, or one pull request when
it touches one repository; named by what the operator or the mesh can do once it is delivered (playbook 04,
ADR 0401).
*Not:* ~~epic~~ (hq)
- **issue** — a question about the mesh that someone must answer, numbered and tracked by the issue tracker, which - **issue** — a question about the mesh that someone must answer, numbered and tracked by the issue tracker, which
holds it whole: its report, diagnosis and resolution included (ADR 0291); holds it whole: its report, diagnosis and resolution included (ADR 0291);
the folders in `04-ISSUES/` are imported there and removed. the folders in `04-ISSUES/` are imported there and removed.
@@ -839,24 +787,22 @@ renaming one sense, the old sense moves to a *Not:* line and becomes mechanical.
| Word | Sense | Say | Domain | | Word | Sense | Say | Domain |
|---|---|---|---| |---|---|---|---|
| plan | what the controller would send one node | that node's **declaration** (verb `plan`) | Placement | | plan | what the controller would send one node | that node's **declaration** (verb `plan`) | Placement |
| | what a delivery would do to the mesh | **delivery plan** | Delivery | | | what a delivery would do to the mesh | **delivery plan** | Change and delivery |
| | the sending of one batch of commits across nodes | **rollout** (verb `plans`) | Delivery | | | the sending of one batch of commits across nodes | **rollout** (verb `plans`) | Change and delivery |
| | a step a person starts, like the bus's | **planned step** | Core | | | a step a person starts, like the bus's | **planned step** | Core |
| membership | the subjects a bus account is issued | **membership** | Provisioning | | membership | the subjects a bus account is issued | **membership** | Provisioning |
| | an account in a group of the machine | **account group** | Placement | | | an account in a group of the machine | **account group** | Placement |
| push | the controller giving nodes their declarations | **send** (verb `push`) | Placement | | push | the controller giving nodes their declarations | **send** (verb `push`) | Placement |
| | a git push | **git push** | The record | | | a git push | **git push** | The record |
| | a phone notification | **push notification** | Operator and conversation | | | a phone notification | **push notification** | Operator and conversation |
| release | a person letting a held delivery go on | **release** | Delivery | | release | a person letting a held delivery go on | **release** | Change and delivery |
| | one milestone built, checked and delivered as one (playbook 07; releases 1 to 6 of ADR 0429) | **release** of a milestone | The record |
| | taking a consumer off a licence | **unbind** (verb `release`, to rename) | Identity and access | | | taking a consumer off a licence | **unbind** (verb `release`, to rename) | Identity and access |
| | a copy of a work an engine can fetch | **media release** | Media | | | a copy of a work an engine can fetch | **media release** | Media |
| gate | the judgement on a delivery's first node | **first-node gate** | Delivery | | gate | the judgement on a delivery's first node | **first-node gate** | Change and delivery |
| | the pull request status | **merge gate** | Delivery | | | the pull request status | **merge gate** | Change and delivery |
| | the part that merges a group and moves the source revision | **gate part** (`mesh-delivery-gate`) | Delivery |
| | a failed step stopping its module's later steps (ADR 0136) | *a failed step holds its module* | Placement | | | a failed step stopping its module's later steps (ADR 0136) | *a failed step holds its module* | Placement |
| tier | a level of the mesh | **layer** | Core | | tier | a level of the mesh | **layer** | Core |
| | a step of a rollout | **tier** | Delivery | | | a step of a rollout | **tier** | Change and delivery |
| | how much proof an ask needs | **assurance level** | Identity and access | | | how much proof an ask needs | **assurance level** | Identity and access |
| harness | the part of `mesh-sdk` that serves a tools bundle | **SDK harness** | Module | | 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 program that runs an agent's sessions | **agent harness** | Operator and conversation |
@@ -864,30 +810,28 @@ renaming one sense, the old sense moves to a *Not:* line and becomes mechanical.
| role | what a seat names, at a scope | **seat**; the bare word in older designs is tracked by issue 872 | Module | | role | what a seat names, at a scope | **seat**; the bare word in older designs is tracked by issue 872 | Module |
| | what an identity may call | **role** | Identity and access | | | what an identity may call | **role** | Identity and access |
| ask | a request for the operator's input | **ask** | Operator and conversation | | ask | a request for the operator's input | **ask** | Operator and conversation |
| | an entry in the build queue | **build request** | Delivery | | | an entry in the build queue | **build request** | Change and delivery |
| store | the one database server | **store** | Core | | store | the one database server | **store** | Core |
| | the OCI registry | **artifact store** | Delivery | | | the OCI registry | **artifact store** | Change and delivery |
| | a node's own copy of its last declaration | **last declaration** | Placement | | | a node's own copy of its last declaration | **last declaration** | Placement |
| record | a numbered decision | **decision record** | The record | | record | a numbered decision | **decision record** | The record |
| | the literal reader of this repository, searched first for a symptom | **the record** (the `records` module) | Operator and conversation | | | the literal reader of this repository, searched first for a symptom | **the record** (the `records` module) | Operator and conversation |
| | the upgrade policy that sends nowhere | `record` | Module | | | the upgrade policy that sends nowhere | `record` | Module |
| nox | the mesh's one agent identity, its logins and accounts (ADR 0414) | **`nox`, the agent** | Identity and access |
| | the operator's terminal command, `mesh-cli` | **the `nox` command** | Operator and conversation |
| knowledge base | the module holding the decision records and research efforts whole | **knowledge base** | The record | | knowledge base | the module holding the decision records and research efforts whole | **knowledge base** | The record |
| | the predecessor's operational memory, in older playbooks (issue 430) | none: it no longer exists | — | | | the predecessor's operational memory, in older playbooks (issue 430) | none: it no longer exists | — |
| check | a pull request's two statuses | **merge check**, **merge gate**, **repository check** | Delivery | | check | a pull request's two statuses | **merge check**, **merge gate**, **repository check** | Change and delivery |
| | a delivery group's verdict | **composed check** | Delivery | | | a delivery group's verdict | **composed check** | Change and delivery |
| | the controller's own examination | **self-check** | Health and repair | | | the controller's own examination | **self-check** | Health and repair |
| | a file in `00-META/checks/` | **hq check** | The record | | | a file in `00-META/checks/` | **hq check** | The record |
| agent | a coding agent | **agent** | Operator and conversation | | agent | a coding agent | **agent** | Operator and conversation |
| | the build seat's holder; from release 4 of ADR 0429 the part `mesh-delivery-builder` | **builder** | Delivery | | | the build seat's holder | **builder** | Change and delivery |
| the user | an account of a wrapped program, or of the bus server | **its account**, **bus account** | Identity and access | | the user | an account of a wrapped program, or of the bus server | **its account**, **bus account** | Identity and access |
| | a human | **operator** or **person** | words every domain uses | | | a human | **operator** or **person** | words every domain uses |
| firewall | what the mesh enforces | **packet filter** | Connectivity | | firewall | what the mesh enforces | **packet filter** | Connectivity |
| | what was found on a machine | **found firewall** | Connectivity | | | what was found on a machine | **found firewall** | Connectivity |
| ticket | an answer's one-time token (ADR 0258) | **answer ticket** | Operator and conversation | | ticket | an answer's one-time token (ADR 0258) | **answer ticket** | Operator and conversation |
| | a question someone must answer | **issue** | The record | | | a question someone must answer | **issue** | The record |
| deploy | the per-node part of a delivery plan | **deploy plan** | Delivery | | deploy | the per-node part of a delivery plan | **deploy plan** | Change and delivery |
| | a wrapped program's own deploy | its own word | — | | | a wrapped program's own deploy | its own word | — |
| managed | a placed file of which only named keys or a marked block are the mesh's | **managed** (`owner-at-runtime`) | Provisioning | | managed | a placed file of which only named keys or a marked block are the mesh's | **managed** (`owner-at-runtime`) | Provisioning |
| | a file the mesh writes, such as the agent's instruction file or `managed_settings` | the file's own name, or *a file the mesh places* | — | | | a file the mesh writes, such as the agent's instruction file or `managed_settings` | the file's own name, or *a file the mesh places* | — |
+1 -6
View File
@@ -94,10 +94,7 @@ about an hour, and said to the operator if it runs past.
deferral no issue tracks. The knowledge base then reviews the proposal itself, through a judgement that deferral no issue tracks. The knowledge base then reviews the proposal itself, through a judgement that
sees only the record, the records it links and the rules, and answers `approve` or `changes`. On sees only the record, the records it links and the rules, and answers `approve` or `changes`. On
`changes` the record stays proposed with the findings: revise it by proposing it again, whole, with `why`; `changes` the record stays proposed with the findings: revise it by proposing it again, whole, with `why`;
after three rounds the lead does not stop to ask, but fixes each further finding itself and proposes again at most three rounds, then the findings go to the operator with a recommendation.
(the operator, 2026-10-18: ADR 0429 went past three rounds, each finding narrower than the last, and asking
first only added a wait). Only a finding that would change what was decided, rather than how the record
says it, goes to the operator, with a recommendation.
On `approve` it is accepted, or, when it says `needs-operator`, it waits for the operator's Approve on a On `approve` it is accepted, or, when it says `needs-operator`, it waits for the operator's Approve on a
channel that proves who answered. Nothing proposed is in force. `knowledge-base.get` shows a record's channel that proves who answered. Nothing proposed is in force. `knowledge-base.get` shows a record's
reviews and the operator's ask when it waits for one. reviews and the operator's ask when it waits for one.
@@ -167,5 +164,3 @@ when one fails. In this repository, `records.py` checks that a design's `decisio
order's six topics, which the knowledge base reads. The three habits of graduating step 2 (side by side, clauses order's six topics, which the knowledge base reads. The three habits of graduating step 2 (side by side, clauses
from the direction, the pre-check) are the lead's, read by the reviewer; `propose` refuses a link to nothing, from the direction, the pre-check) are the lead's, read by the reviewer; `propose` refuses a link to nothing,
a deferral no issue tracks and a retired word, and the review returns a record named but not linked. a deferral no issue tracks and a retired word, and the review returns a record named but not linked.
The rounds are read in `knowledge-base.get` (its reviews, one per version): a question to the operator about a
record's wording after its third round is the failure of step 4 of *Writing a decision record*.
+5 -11
View File
@@ -12,12 +12,8 @@ A design becomes **milestones**, and each milestone becomes **issues**:
- **A milestone is usable on its own.** Once its delivery plan is done, the operator or the mesh can do - **A milestone is usable on its own.** Once its delivery plan is done, the operator or the mesh can do
something new, said in one sentence ("a Claude Code session is mirrored into a thread on the bus"). something new, said in one sentence ("a Claude Code session is mirrored into a thread on the bus").
A milestone that changes nothing anyone can use is not a milestone; fold it into the next. A milestone that changes nothing anyone can use is not a milestone; fold it into the next.
- **A milestone is one release** of playbook [07](07-feature-branches.md): prepared, developed whole, reviewed, - **A milestone is one feature step** of playbook [07](07-feature-branches.md): prepared, developed in one to
checked and delivered as one, never cut for its size or for time (the operator, 2026-10-17). two hours, reviewed, checked and delivered as one. A milestone larger than that is split.
- **Milestones are cut by release order.** A milestone ends where one repository's change must be released before
another can use it (a new manifest word before the catalogue reads it, an SDK change before its users, until
design 47's milestones 3 and 6 lift those orders); the lead checks this when cutting, so no milestone needs a
second release.
- **An issue is one repository's part of a milestone,** sized so a fixing agent finishes it inside the - **An issue is one repository's part of a milestone,** sized so a fixing agent finishes it inside the
milestone's development time. Issues that must land in order are linked `depends-on`. milestone's development time. Issues that must land in order are linked `depends-on`.
@@ -44,10 +40,8 @@ A design becomes **milestones**, and each milestone becomes **issues**:
entry in the design. entry in the design.
7. **Flip the status.** `status: in-progress`, `updated:` today; the design change goes through playbook 7. **Flip the status.** `status: in-progress`, `updated:` today; the design change goes through playbook
[08](08-small-fixes.md) (one hq pull request). [08](08-small-fixes.md) (one hq pull request).
8. **Hand each milestone to playbook 07, one at a time.** The milestone is one release of playbook 07; its issues 8. **Hand each milestone to playbook 07, one at a time.** The milestone is the feature step; its issues are the
are the release's issues; the next milestone starts when the previous one's delivery plan is done. A design waits step's issues; the next milestone starts when the previous one's delivery plan is done.
while another design's milestones are being built: one design is built at a time (playbook
[07](07-feature-branches.md)), so this step starts only when the design before it is delivered.
9. **On completion of the last milestone**, run the "when something ships" section of playbook 9. **On completion of the last milestone**, run the "when something ships" section of playbook
[02](02-graduation.md). [02](02-graduation.md).
@@ -58,7 +52,7 @@ A design becomes **milestones**, and each milestone becomes **issues**:
owns what is not. The knowledge base's rule `deferral` checks the decision record when it is proposed owns what is not. The knowledge base's rule `deferral` checks the decision record when it is proposed
([issue 354](../../04-ISSUES/354-a-decided-rename-was-never-scheduled-and-the-operator-believed-it-live/00-report.md)). ([issue 354](../../04-ISSUES/354-a-decided-rename-was-never-scheduled-and-the-operator-believed-it-live/00-report.md)).
- HQ is not a code repository and never carries implementation. - HQ is not a code repository and never carries implementation.
- How the building is done — worktrees, agents, review, checks, the Approve and the delivery — is playbook - How the building is done — worktrees, agents, review, checks, the ready mark and the delivery — is playbook
07's, not this one's. 07's, not this one's.
## How it is checked ## How it is checked
+77 -220
View File
@@ -28,115 +28,72 @@ A small fix (one repository, one issue, about an hour of work) follows
hours of local work, met its first check on 2026-10-16 and failed on 29 problems, TypeScript errors among hours of local work, met its first check on 2026-10-16 and failed on 29 problems, TypeScript errors among
them, that a unit suite run continuously while working finds at once. them, that a unit suite run continuously while working finds at once.
- **A green pull request nobody marks.** An approved, green pull request that was not marked ready waited - **A green pull request nobody marks.** An approved, green pull request that was not marked ready waited
12 h 17 min for someone to merge it (mesh-catalog #300, 2026-10-14). There is no ready mark since ADR 0421: 12 h 17 min for someone to merge it (mesh-catalog #300, 2026-10-14).
the operator's Approve is asked as soon as the checks pass.
- **A module proven only after it reached the machines.** The mail modules were delivered with unit tests - **A module proven only after it reached the machines.** The mail modules were delivered with unit tests
only, then failed live three times (issues 859, 865, 868) and took five more hours of fixes. only, then failed live three times (issues 859, 865, 868) and took five more hours of fixes.
## The shape ## The shape
One feature is **one branch name**, **one worktree per repository**, **one pull request per repository**, One feature is **one branch name**, **one worktree per repository**, **one pull request per repository**,
opened **once**, after the review. It runs in five steps, and its time is bounded. A milestone has no phases opened **once**, after the review. It runs in five phases, and its time is bounded:
(the operator, 2026-10-17): these are its steps, and a report names the step it is in:
| Step | What happens | Bound | | Phase | What happens | Bound |
|---|---|---| |---|---|---|
| 0. Prepare | the lead settles the open questions and writes one context file per repository | 15 minutes | | 0. Prepare | the lead settles the open questions and writes one context file per repository | 15 minutes |
| 1. Develop | one fixing agent per repository builds every issue locally, the whole unit suite before every commit | the milestone's issues, 30 minutes each | | 1. Develop | one fixing agent per repository builds every issue locally, unit tests only | 1 to 2 hours |
| 2. Review | one reviewer reads all of it; reports go both ways; at most 3 rounds | 15 minutes | | 2. Review | one reviewer reads all of it; reports go both ways; at most 3 rounds | 15 minutes |
| 3. Check | push; `mesh-controller.try` runs the integration tests on each pushed branch, then the pull requests are opened and the forge's merge checks run them again; no further review | one try and one check run | | 3. Check | push; the forge's merge checks run the integration tests; no further review | one check run |
| 4. Deliver | merge; the delivery plan builds, publishes and sends the whole feature | its delivery plan | | 4. Deliver | merge; the delivery plan builds, publishes and sends the whole feature | its delivery plan |
**One milestone is one release** (the operator, 2026-10-17). A milestone is developed whole, reviewed once, A feature that cannot be developed in about two hours is split into **steps**. Each step runs all five
pushed once and delivered once. It is never cut into steps for its size or for time: every cut adds a review, a phases on its own; the next step starts only when the previous step's delivery plan is done. A step is the
merge check, an Approve and a delivery plan of waiting, so the fewest cuts is the fastest. Milestones are cut by unit that ships, so nothing finished waits for work that is not.
release order when the design is broken up (playbook [04](04-build-handoff.md)); a release-order dependency found
inside a milestone is a fault of that cut, said to the operator, never a silent extra step.
**One design is built at a time** (the operator, 2026-10-17). While the milestones of one design are being **Scope is fixed at phase 0.** An issue found during a feature goes to the next step or the next feature,
built, no milestone of another design starts: its issues stay `open`, and nobody moves them to `diagnosing` or
`fixing`. The next design starts when the last milestone of the one being built has its delivery plan done. A
lead who finds another design's milestone already in `fixing` asks its owner before starting its own. A small
fix ([playbook 08](08-small-fixes.md)) and an issue that is in no design's milestones are not held by this rule.
**Scope is fixed at the prepare step.** An issue found during a feature goes to the next milestone or the next feature,
never into the running one, unless it blocks the running one. never into the running one, unless it blocks the running one.
## Step 0 — Prepare (the lead, larger model) ## Phase 0 — Prepare (the lead, larger model)
1. **Name the feature once:** `feat/<slug>`, the same branch name in every repository it touches (but see the 1. **Name the feature once:** `feat/<slug>`, the same branch name in every repository it touches. The
exception for a new manifest word in phase 3: a release that must merge first has a name of its own). The branch is never protected.
branch is never protected. **One milestone is one branch per repository** (the operator, 2026-10-17), with one
folder `.work/<slug>/`; never a second name for part of it (`-2`, `-step2`).
2. **Make the worktrees:** one per touched repository under `.work/<slug>/<repo>`, branched off `main` 2. **Make the worktrees:** one per touched repository under `.work/<slug>/<repo>`, branched off `main`
(`git worktree add .work/<slug>/<repo> -b feat/<slug> origin/main`). The lab beds find the other (`git worktree add .work/<slug>/<repo> -b feat/<slug> origin/main`). The lab beds find the other
repositories by sibling path, so add the untouched siblings the lab reads as detached worktrees on repositories by sibling path, so add the untouched siblings the lab reads as detached worktrees on
`main` (`git worktree add --detach .work/<slug>/mesh-tools main`), never symlinks. **A repository's own `main` (`git worktree add --detach .work/<slug>/mesh-tools main`), never symlinks.
clone stays on `main`** (the operator, 2026-10-17): no work is done in it and no branch is checked out
there, so a session that opens in it reads what holds now; fetch and fast-forward it before reading.
3. **Settle every open question** in the feature's issues before any agent starts: on the issue, through 3. **Settle every open question** in the feature's issues before any agent starts: on the issue, through
the knowledge base, or by asking the operator. A fixing agent that meets an undecided design question the knowledge base, or by asking the operator. A fixing agent that meets an undecided design question
stops, and its run is spent again. stops, and its run is spent again.
4. **Write one context file per repository**, `.work/<slug>/<repo>.context.md`. It is what a fixing agent 4. **Write one context file per repository**, `.work/<slug>/<repo>.context.md`. It is what a fixing agent
reads instead of rediscovering the code, and it is reused by every agent and every round: reads instead of rediscovering the code, and it is reused by every agent and every round:
- the issues it builds, in order, each with: the files and functions it changes, the decisions it builds - the issues it builds, in order, each with: the files and functions it changes, the decisions it builds
with (quoted, with where each was decided), and its acceptance criteria (item 5), each with the test that with (quoted, with where each was decided), the test to write first, done-when in one sentence;
proves it;
- the repository's map for this feature: the packages and files involved, with one line each on what they - the repository's map for this feature: the packages and files involved, with one line each on what they
hold, and the patterns to copy (file:line); hold, and the patterns to copy (file:line);
- what not to touch, and what lands beside it in other repositories; - what not to touch, and what lands beside it in other repositories;
- the local facts: the worktree, the branch, the check commands, a temporary directory for tests outside - the local facts: the worktree, the branch, the check commands, a temporary directory for tests outside
the home (`TMPDIR`), and the unit tests to run (never a database server: tests that need one are the home (`TMPDIR`), and the unit tests to run (never a database server: tests that need one are
integration tests, which `mesh-controller.try` and the forge's checks run). integration tests, which the forge's checks run).
When the reading is large, a read-only agent on the smaller model answers the lead's exact questions with When the reading is large, a read-only agent on the smaller model answers the lead's exact questions with
file:line, and the lead writes the file from its answers. file:line, and the lead writes the file from its answers.
5. **Write the findings into the issues** (the operator, 2026-10-17: the better the content of the issues, the 5. **Move the issues to `fixing`**, `by` naming the feature's agent for that repository.
better work the agents do, and the less review is needed). Before any agent starts, each issue carries, with
`issue-tracker.note`: every file and test that touches the change, found by search and not from memory
(file:line); how the change is checked and delivered end to end (which test proves its done-when, which
modules its delivery plan moves, to which machines); and for a group, its merge order. The context file
points to these notes; an issue reread by another session or a later agent then holds what was found.
When the reading is large, it starts with a read-only survey agent on the smaller model.
**Each issue carries its acceptance criteria, written by the lead** (the operator, 2026-10-18: the larger
model writes them, and they guide every test). Each criterion is one sentence, given, when, then, about what
the module or the operator observes, never about how the code does it, and names the level of the test that
proves it: unit, integration (a bus, a store or a process started) or end to end (none exist yet, so such a
criterion is proven live after the delivery and said so). Together they cover the issue's done-when and the
cases its life passes through: on 2026-10-18 mesh-delivery's group life (a group of three, a member's own
check, a close in a group, a second pull request at the same commit, a stop and a resend) had no criterion,
and each case was found by a failure on the live mesh. Each criterion is numbered in its note and cited as
`<issue>/AC<n>` (`1118/AC2`). They are an `issue-tracker.note` headed `Acceptance criteria`, a revision a new note that replaces the older one, until the tracker holds them as a
field (issue 1118). A criterion the reviewer adds in step 2 is written onto the issue the same way.
**The survey sizes each issue, and an issue sized over an hour is split before any agent starts** (the operator,
2026-10-17), into issues of about 30 minutes each, one change each, in the order they land: the bound of a
fixing agent's issue is then a real bound. On 2026-10-17 one issue held four changes sized at four to six
hours; it ran past its 30-minute bound for over an hour, and the reports could only say "still on part D".
6. **Move the issues to `fixing`**, `by` naming the feature's agent for that repository.
## Step 1 — Develop (one fixing agent per repository, smaller model) ## Phase 1 — Develop (one fixing agent per repository, smaller model)
- **One agent per repository, kept for the whole feature.** It reads its context file once and builds the - **One agent per repository, kept for the whole feature.** It reads its context file once and builds the
issues one after another, committing each locally with a message naming the issue ("Keep the assignments issues one after another, committing each locally with a message naming the issue ("Keep the assignments
on the bus (issue 652)"). It is not replaced per issue: the lead continues the same agent (by its id) with on the bus (issue 652)"). It is not replaced per issue: the lead continues the same agent (by its id) with
the next instruction, so its context is paid for once. the next instruction, so its context is paid for once.
- **Unit tests only, each well under a second** (how-we-build §5, ADR 0391). **The whole unit suite runs before - **Unit tests only, of what changed, each well under a second** (how-we-build §5, ADR 0391). Each issue's
every commit** (the operator, 2026-10-17), not only the tests of what changed: it takes under a second, and a first test fails before its change and passes after; `gofmt`, `go vet`
change breaks tests in packages it never touched. Each issue's first test fails before its change and passes and the repository's unit tests stay green. The integration tests (`merge-check.sh` with the store and the
after; `gofmt`, `go vet` and the whole unit suite stay green. **The tests are written from the issue's bus) wait for phase 3.
acceptance criteria**: one test or more per criterion at the level it names, each test's comment citing - **Nothing is pushed** and no pull request is opened in this phase. Never land on a red check: run the
the criterion it proves by its label (`// 1118/AC2`), and no criterion left without its test; an integration criterion is a test
that compiles and waits for the check step. The integration tests (those that start a store, a bus or a
process) wait for the check step, where `mesh-controller.try` runs them first.
- **Nothing is pushed** and no pull request is opened in this step. Never land on a red check: run the
checks, read the result, then land in a separate step. checks, read the result, then land in a separate step.
- **The fixing agent's brief** (the lead gives it with the context file): name your issue in every step and - **The fixing agent's brief** (the lead gives it with the context file): name your issue in every step and
message ("issue 805: landing in mesh-catalog"); reach the mesh only through the mesh MCP server, never ssh; message ("issue 805: landing in mesh-catalog"); reach the mesh only through the mesh MCP server, never ssh;
never push, open, merge or close a pull request; never write to a store's database by hand, a schema change never push, open, merge or close a pull request; never write to a store's database by hand, a schema change
is a numbered migration and the consolidated schema; Go only unless the context file says otherwise; never is a numbered migration and the consolidated schema; Go only unless the context file says otherwise; never
move another issue; **never run an integration test on the machine** (no `go test -tags integration`, no `merge-check.sh`, move another issue. Report back in under 150 words: commits, tests added and that they failed first, the
no store or bus started by hand): compile them with `go vet -tags integration ./...` and leave the running to
the check step, where `mesh-controller.try` runs them on the pushed branch. On 2026-10-17 three fixing agents ran them anyway, one for minutes on end, because the
brief only said they wait for the check (the operator). Report back in under 150 words: commits, tests added and that they failed first, the
check results, anything you stopped on. check results, anything you stopped on.
- **An agent that finds the code differs from its context file stops and reports** (file:line); the lead - **An agent that finds the code differs from its context file stops and reports** (file:line); the lead
settles it, updates the context file, and continues the agent. settles it, updates the context file, and continues the agent.
@@ -148,87 +105,53 @@ never into the running one, unless it blocks the running one.
tests pass; if another landed first, it merges again and repeats. In a shared clone, fetch the feature tests pass; if another landed first, it merges again and repeats. In a shared clone, fetch the feature
branch by name before branching from it. Never resolve a conflict with a blind `git merge-file --union`. branch by name before branching from it. Never resolve a conflict with a blind `git merge-file --union`.
## Step 2 — Review (one reviewer, larger model; at most 3 rounds, about 15 minutes) ## Phase 2 — Review (one reviewer, larger model; at most 3 rounds, about 15 minutes)
1. **Bring every feature branch up to the current trunk** (fetch by name, then merge), so the reviewed tree is 1. **Bring every feature branch up to the current trunk** (fetch by name, then merge), so the reviewed tree is
the tree that is pushed. the tree that is pushed.
2. **One reviewer reads every repository's local commits** together, against the context files and the 2. **One reviewer reads every repository's local commits** together, against the context files and the
decisions. **It is one agent, kept for the whole release** like the fixing agents: the lead continues it by decisions. Its report is per repository: each finding as file:line, the failure it causes, and the fix.
its id for every later round, for the extra cycle after a failed check (step 3) and for its review record, so it
reads the decisions, the issues, the context files and the commits once (issue 1042). A fresh reviewer per
round or per mark pays for all of that again. Its report is per repository: each finding as file:line, the failure it causes, and the fix.
It says which round it is in ("round 1 of at most 3"). It says which round it is in ("round 1 of at most 3").
3. **The report goes to the same fixing agents** that wrote the code, continued by their ids, so they fix 3. **The report goes to the same fixing agents** that wrote the code, continued by their ids, so they fix
with the context they already hold. Each answers the reviewer: what it changed, per finding, or why not. with the context they already hold. Each answers the reviewer: what it changed, per finding, or why not.
4. **Every new integration test is read against the code it drives before the first push.** It has never run: fixing agents only compile it, and its first run is the merge check, so each fault it hides costs a whole check. The reviewer traces each step of each new integration test through the code (fixtures against the manifest rules, what the store keeps and reads back, who holds a seat), and says which steps it could not trace. On 2026-10-17 a new test failed on its first two runs, on a fixture and then on a fault of the change; reading it against the code after the first failure found another fault, which would have failed a run too. The reviewer also checks that each new integration test enters through the same path the live trigger takes, not through a state it writes by hand: on 2026-10-17 milestone 4's test saved its delivery plan directly, so it skipped the cut from a batch that every live plan takes, and it passed while the backup step never ran (issue 1086). 4. **The reviewer reads the answers and the new commits**, and approves or reports again (step 5 says what a round
5. **The reviewer reads the answers and the new commits**, and approves or reports again (step 7 says what a round
may ask). After round 3 the lead stops and brings the operator what is left, with a recommendation. may ask). After round 3 the lead stops and brings the operator what is left, with a recommendation.
6. **The reviewer reads every acceptance criterion against its test**: each criterion has a test at its level 5. **A finding outside the issues' done-when is never a change asked, in any round.** More hardening, a
that would fail without the change, and no test proves less than its criterion says. A criterion missing
for a case the issue's done-when needs is written onto the issue (step 0, item 5) and is a finding; a test
missing for a criterion is a finding.
7. **A finding outside the issues' done-when and their acceptance criteria is never a change asked, in any round.** More hardening, a
further case, a nicer form: the reviewer opens it with `issue-tracker.open` and names the number in its further case, a nicer form: the reviewer opens it with `issue-tracker.open` and names the number in its
report, and the round does not wait for it. What a round asks is only what makes the change wrong against report, and the round does not wait for it. What a round asks is only what makes the change wrong against
its issues, their acceptance criteria and its decisions. its issues and its decisions.
## Step 3 — Check (no review) ## Phase 3 — Check (no review)
1. **Push every feature branch, try it, then open one pull request per repository, together.** 1. **Push every feature branch and open one pull request per repository, together.**
**The integration tests run once before the pull requests are opened**: `mesh-controller.try` with the The integration tests
repository and the pushed branch runs the repository's `merge-check.sh` (main's version) on the merge, in the run only in the merge check (the operator, 2026-10-16), never before. Pull requests in two or
toolchain containers and against the throwaway store and bus a merge check gets, and answers with an id to
read back. A try that fails goes to step 3 item 3 as a failed check does; the pull requests are opened only
when every try passed (the operator, 2026-10-18: three pushes in a row failed their first merge check on
faults a try finds in minutes). Never `merge-check.sh`, `go test -tags integration` or a store or bus started
by hand on the machine (the operator, 2026-10-18). Pull requests in two or
more repositories that share the branch name are one delivery group (ADR 0239 §2). more repositories that share the branch name are one delivery group (ADR 0239 §2).
Where a session pushes through the forge tool `gitea_commit` (one commit on the forge's current base), the Where a session pushes through the forge tool `gitea_commit` (one commit on the forge's current base), the
reviewer first confirms that commit's tree equals the reviewed one. **Each pull request's description carries reviewer first confirms that commit's tree equals the reviewed one.
the review record** (ADR 0421 §5), one line the lead writes from the reviewer's approval: 2. **The forge's merge checks run the integration tests once** (`mesh/merge-gate`, `mesh/repo-check`). No
`Reviewed-by: <who@node> at <full sha>` (the issue 1022 note gives its form). mesh-delivery reads it at every new second review: phase 2 reviewed this tree.
head and puts it in the Approve's ask; a missing line, or one naming another commit, is said first in the ask. 3. **A check that fails** goes back to the reviewer with the failure, for **one more cycle** of phase 2 with
It is never a gate. the same fixing agents; then push once again.
2. **The forge's merge checks run the integration tests again, as the gate** (`mesh/merge-gate`, `mesh/repo-check`). No 4. **The step's acceptance is proven before it is marked ready.** A step that adds or changes a module
second review: step 2 reviewed this tree.
3. **A check that fails** goes back to the same reviewer, continued by its id, with the failure, for **one more cycle** of step 2 with
the same fixing agents; then push once again. **The failure is read from the check's whole log**, never
reasoned from its summary: `mesh-delivery.show` names the check's build id, and `mesh-controller.builds` with
`log` set to that id prints everything the builder said. A check's summary picks only some failing lines;
on 2026-10-17 it showed a store test's `FAIL` without its cause, and a fix was first guessed from the code.
**A change to `merge-check.sh` is judged by main's version of it**, so a test the change adds must pass under
either version: one that runs the new script's behaviour runs a file of its own, never `merge-check.sh` itself.
**So a change that needs the new script to judge it ships the script first, as a small fix of its own**, and the
rest waits for its delivery: on 2026-10-17 milestone 3 of design 47 moved its integration tests behind a build
tag in the same pull request as the script that runs that tag, and main's script would have skipped every
tagged test, its acceptance tests among them. The prepare step looks for this: a change to `merge-check.sh` or to
what it selects (build tags, the store-tests list) is cut ahead of the milestone.
4. **The milestone's acceptance is proven by its check.** A milestone that adds or changes a module
carries an integration test that starts the module whole, its process against the real bus and store, and carries an integration test that starts the module whole, its process against the real bus and store, and
shows the milestone's acceptance sentence; the forge's checks run it (ADR 0391 §1, §6). Unit tests alone are not shows the step's acceptance sentence; the forge's checks run it (ADR 0391 §1, §6). Unit tests alone are not
that proof; the mesh has no end-to-end tests yet, and the lab is not used (the operator, 2026-10-16). that proof; the mesh has no end-to-end tests yet, and the lab is not used (the operator, 2026-10-16).
5. **The operator's Approve is asked as soon as the checks pass** (ADR 0421). Nobody marks, labels, comments or 5. **The reviewer marks each pull request ready as soon as the acceptance is shown** with
rechecks: mesh-delivery asks the Approve on Telegram, the channel that proves who answered today (ADR 0398 §3; `mesh-delivery.mark-ready` (the delivery of the head it approved, who reviewed it and why), which writes the
an Approve at the terminal is issue 953), once both checks passed, for a delivery group once for all its label `mesh/ready` and then `ready at <full sha>`, the order the forge counts (issue 913); mesh-delivery merges it when its checks pass
members, naming the review record. On the Approve it merges the approved heads, a group's in one pass and the operator approved the merge into main on Telegram, the channel that proves who answered today (ADR 0364,
(ADR 0418). A new head ends the Approve and is asked again once its checks pass. A watcher follows the checks, ADR 0398 §3; an Approve at the terminal is issue 953). Beyond that
the Approve and the merge, and wakes the lead. Beyond that Approve, the operator's word is waited for only when the step changes what the operator sees, removes
Approve, the operator's word is waited for only when the milestone changes what the operator sees, removes data, or needs a step only the operator's terminal can take; the lead says which before the step starts.
data, or needs a step only the operator's terminal can take; the lead says which before the milestone starts.
**One exception: a catalogue change that uses a new manifest word.** The catalogue's merge check reads **One exception: a catalogue change that uses a new manifest word.** The catalogue's merge check reads
manifests with the controller the mesh runs, so the controller's pull request is merged and delivered first; manifests with the controller the mesh runs, so the controller's pull request is merged and delivered first;
then `mesh-delivery.recheck` the catalogue's and merge it on the new verdict. Such work is two releases, the controller's first, and playbook 04 cuts its milestones that way. then `mesh-delivery.recheck` the catalogue's and merge it on the new verdict. Plan such a feature as two steps.
**A new key the node-engine checks is such a word too** (issue 1051): the controller judges every machine with the
node-engine's own validator, vendored at the commit `go.mod`'s `replace` pins, so a key added to the node-engine is
refused by the gate until the controller's pin moves to the delivered node-engine (`go mod vendor`); the feature is
then three releases, the node-engine's, the controller's, the catalogue's. **Every release that must merge before
the catalogue's is on a branch name of its own** (`feat/<slug>-node-engine`, `feat/<slug>-controller`): on the
feature's name it joins the catalogue's delivery group, and a group merges only whole, so neither merges.
The exception ends when milestone 3 of design 47 is delivered (issue 977, ADR 0402, ADR 0403): the group's composed The exception ends when milestone 3 of design 47 is delivered (issue 977, ADR 0402, ADR 0403): the group's composed
check, judged by the group's controller, is then each member's gate, and the feature is one group. check, judged by the group's controller, is then each member's gate, and the feature is one group.
## Step 4 — Deliver ## Phase 4 — Deliver
The merge starts the delivery plan: build, publish, send. A merge into a branch other than `main` is not a The merge starts the delivery plan: build, publish, send. A merge into a branch other than `main` is not a
delivery: no machine receives it (issue 889 while it still starts one). The lead follows it to done delivery: no machine receives it (issue 889 while it still starts one). The lead follows it to done
@@ -240,10 +163,10 @@ survive.
| Who | Model | Why | | Who | Model | Why |
|---|---|---| |---|---|---|
| The lead (step 0 decisions, the context files, following and reporting) | larger | judgement | | The lead (phase 0 decisions, the context files, following and reporting) | larger | judgement |
| Read-only questions about the code for the context files | smaller | reading, answered with file:line | | Read-only questions about the code for the context files | smaller | reading, answered with file:line |
| Fixing agents (step 1, and the fixing in steps 2 and 3) | smaller | building to a written context | | Fixing agents (phase 1, and the fixing in phases 2 and 3) | smaller | building to a written context |
| The reviewer (step 2, the extra cycle in step 3 and the review record; one agent, continued by its id) | larger | judgement, once per round | | The reviewer (phase 2, and the extra cycle in phase 3) | larger | judgement, once per round |
| Decision records | larger | judgement | | Decision records | larger | judgement |
Every agent the mesh runs draws on one weekly allowance shared by every session, and the larger model Every agent the mesh runs draws on one weekly allowance shared by every session, and the larger model
@@ -260,94 +183,57 @@ on, with when it started and its bound.
| a fixing agent's issue | 30 minutes | ask it where it stands; split or take over | | a fixing agent's issue | 30 minutes | ask it where it stands; split or take over |
| the review | 15 minutes a round | ask the reviewer; stop at round 3 | | the review | 15 minutes a round | ask the reviewer; stop at round 3 |
| a merge check | 15 minutes | read the build queue (`mesh-controller.queue`) and the check's log | | a merge check | 15 minutes | read the build queue (`mesh-controller.queue`) and the check's log |
| the Approve's ask | 5 minutes after both checks pass | read `mesh-delivery.show`: an ask not raised is a fault, said to the operator | | the ready mark | 15 minutes after the acceptance is shown | the reviewer marks it, or the lead does |
| a merge after the Approve | 5 minutes | read `mesh-delivery.show` for the delivery | | a merge after ready | 5 minutes after the checks pass | read `mesh-delivery.show` for the delivery |
| a delivery plan | its class's budget (`mesh-delivery.times`), at most 30 minutes | read `mesh-controller.plans`, then `mesh-delivery.stalled` | | a delivery plan | its class's budget (`mesh-delivery.times`), at most 30 minutes | read `mesh-controller.plans`, then `mesh-delivery.stalled` |
**Every wait on the list has a watcher** (the operator, 2026-10-17). A session runs only when a message reaches **Every five minutes, every two minutes while a delivery plan runs, and at once when something lands, stops or
it, so a check, a merge, a delivery plan, a record's review or the operator's Approve on a channel that lands
while the lead's turn has ended wakes nobody: on 2026-10-17 an approved group sat half merged for 14 minutes
unseen (issue 1032). For each wait the lead starts a watcher, an agent on the smaller model that changes nothing,
never the smallest (the operator, 2026-10-17).
It loops `mesh_wait` on the event families of what it watches (`mesh-delivery.transition` and
`mesh-delivery.group` for a delivery, for example) with a timeout under 60 seconds, since the call itself ends near
a minute. After every answer or timeout it reads the state from the verb that owns it (`mesh-delivery.show`,
`mesh-controller.plans`, `knowledge-base.get`), because an event only wakes it (ADR 0367). It finishes, and so wakes
the lead, when the state changes or the wait passes its bound. The lead never sleeps or polls by hand to wait.
**Every ten minutes (the operator, 2026-10-17), every two minutes while a delivery plan runs, and at once when something lands, stops or
needs the operator,** the lead reports, in one of the two layouts below (the operator, 2026-10-16: an overview needs the operator,** the lead reports, in one of the two layouts below (the operator, 2026-10-16: an overview
of the issues, often). Times are local and absolute, from the clock (`date`), never guessed; every number has its of the issues, often). Times are local and absolute, from the clock (`date`), never guessed; every number has its
unit and its bound or baseline; an issue is named by its number and a few words, never by its number alone. unit and its bound or baseline; an issue is named by its number and a few words, never by its number alone.
**The full layout** (in the session) is the whole design as a tree (the operator, 2026-10-17: always the full **The full layout** (in the session), the issues as a tree: grouped by repository, each issue under the one it
overview): every milestone of the design with its state, and under the running milestone its repositories and waits for, marked ✓ landed, ▶ fixing, ⏸ waiting, ✗ stopped:
their issues, each issue under the one it waits for, marked ✓ landed or delivered, ▶ fixing, ⏸ waiting, ✗ stopped:
``` ```
Feature <slug> · milestone <n> of <m> · step <name>, <minutes used> of <bound> min Feature <slug> · step <n> of <m> · phase <name>, <minutes used> of <bound> min
Issues: <landed> of <total> landed · <fixing> fixing · <waiting> waiting Issues: <landed> of <total> landed · <fixing> fixing · <waiting> waiting
Design <nn> · <its title> mesh-controller (agent: fixing, 2 of 3 landed)
├── ✓ milestone 1 <its outcome, a few words> delivered 12:58 ├── ✓ 652 assignments on the bus landed 14:02
├── ▶ milestone 2 <its outcome, a few words> ├── ▶ 736 placement words leave the layers fixing since 13:51, 23 of 30 min
│ ├── mesh-controller (agent: fixing, 2 of 3 landed) │ └── ⏸ 751 config keys named (mesh-catalog) waits for 736
│ │ ├── ✓ 652 assignments on the bus landed 14:02 └── ✓ 654 a file names its template landed 13:20
│ │ ├── ▶ 736 placement words leave the layers fixing since 13:51, 23 of 30 min mesh-catalog (agent: waiting)
│ │ │ └── ⏸ 751 config keys named (mesh-catalog) waits for 736 └── ⏸ 751 (above, under 736)
│ │ └── ✓ 654 a file names its template landed 13:20
│ └── mesh-catalog (agent: waiting)
│ └── ⏸ 751 (above, under 736)
└── ⏸ milestone 3 <its outcome, a few words> <its issues>
Opened along the way: <each issue opened during the feature, its number and a few words, and where it goes> — or "none" Opened along the way: <each issue opened during the feature, its number and a few words, and where it goes> — or "none"
Past its bound: <the wait, how long, what is being done> — or "none" Past its bound: <the wait, how long, what is being done> — or "none"
Needs you: <one line per question, with my recommendation> — or "nothing" Needs you: <one line per question, with my recommendation> — or "nothing"
Allowance: week <n> % (<+d> since the last report), session <n> % Allowance: week <n> % (<+d> since the last report), session <n> %
Next: <the next actions up to the delivery, in order> Next: <the next action, one line>
``` ```
**The short layout** (on a channel such as Telegram, or when nothing changed but time): **The short layout** (on a channel such as Telegram, or when nothing changed but time):
``` ```
<slug> milestone <n>/<m> · <step> <used>/<bound> min · <landed>/<total> landed · past bound: <none | what> · needs you: <nothing | what> · week <n> % <slug> step <n>/<m> · <phase> <used>/<bound> min · <landed>/<total> landed · past bound: <none | what> · needs you: <nothing | what> · week <n> %
``` ```
A report says what changed since the last one first. In the session every report is the full layout, the whole A report says what changed since the last one first. In the session every report is the full layout, the whole
tree of the milestone's issues included, also when only time passed: the operator reads the state from it without tree of the step's issues included, also when only time passed: the operator reads the state from it without
asking. The short layout is for a channel. asking. The short layout is for a channel.
## The context of the lead's own session ## The context of the lead's own session
At the end of each step, and whenever the session's context grows large or spread over unrelated work, the At the end of each phase, and whenever the session's context grows large or spread over unrelated work, the
lead prepares a `/clear` as the mesh's instruction "prepare before clear" says: a save point with the lead prepares a `/clear` as the mesh's instruction "prepare before clear" says: a save point with the
feature's step, its branches and heads, the context files' paths, the agents' ids, the wait list and the feature's phase, its branches and heads, the context files' paths, the agents' ids, the wait list and the
next action; then the prompt the operator pastes. The context files make the feature resumable after a clear. next action; then the prompt the operator pastes. The context files make the feature resumable after a clear.
## The design's state is written back
Other sessions know what is built only from the design's frontmatter and the issue tracker; what the lead
knows and does not write back, nobody else knows. A design merged in one delivery with three of its tasks
left, its frontmatter still saying `designed`, read to another session as not built, and a second design
was started beside it (issue 1009, 2026-10-17). So the lead writes the design's state back at each of these
moments, the same hour, an hq change going through [playbook 08](08-small-fixes.md):
- **The first milestone starts:** the design's `status: in-progress` ([playbook 04](04-build-handoff.md) step 7),
if it is not already.
- **A milestone's delivery plan is done:** its issues are resolved (step 4), with `fixed-by` naming its pull
requests. The design's milestone entry is not changed: it names the milestone's issues, and whether they are
delivered, when and by what is read from the issue tracker when it is asked, never copied into the design
(issue 1038; AGENTS.md: cross-cutting views are generated when they are read).
- **The building pauses with work left** (another design takes its turn, a hand-over, the session's end): the
design stays `in-progress`, and each issue left gets a note with `issue-tracker.note` saying what it waits
for and what comes after it ("built after design N is delivered, before design M"). The first issue of
a design that waits its turn gets the same note.
- **The last milestone is delivered:** [playbook 02](02-graduation.md), "When something ships": the as-is is
written and the design says `implemented`.
## Issues that stop ## Issues that stop
An issue stays in `fixing` only while an agent works on it. A milestone that stops, for a wait past its bound, a An issue stays in `fixing` only while an agent works on it. A step that stops, for a wait past its bound, a
hand-over, or the session's end, moves each of its issues not delivered back to `open` with hand-over, or the session's end, moves each of its issues not delivered back to `open` with
`issue-tracker.move` and a note saying how far it got (its branch, its last commit, what is left). A list of `issue-tracker.move` and a note saying how far it got (its branch, its last commit, what is left). A list of
issues in `fixing` that nobody works on reads to the operator as stuck work. issues in `fixing` that nobody works on reads to the operator as stuck work.
@@ -356,46 +242,17 @@ issues in `fixing` that nobody works on reads to the operator as stuck work.
- A feature's issues show their commits on the feature branch (`git log --grep 'issue <n>'`), and no pull - A feature's issues show their commits on the feature branch (`git log --grep 'issue <n>'`), and no pull
request into a feature branch exists. A pull request per issue, or an agent waiting on a merge check in request into a feature branch exists. A pull request per issue, or an agent waiting on a merge check in
step 1, is the failure this playbook prevents. phase 1, is the failure this playbook prevents.
- A context file exists per touched repository under `.work/<slug>/` while the feature runs. - A context file exists per touched repository under `.work/<slug>/` while the feature runs.
- No issue of a running milestone is sized over an hour in its prepare note; an issue whose fixing agent passes
twice its bound was not split.
- Every issue of a running milestone has a note from the prepare step naming its files and tests (file:line), how
it is checked and delivered, and for a group its merge order (`issue-tracker.get`); an issue moved to `fixing`
without one is a prepare step skipped.
- Every issue of a running milestone has a note headed `Acceptance criteria` before it moves to `fixing`
(`issue-tracker.get`), each criterion naming its test level, and every criterion's label is cited by a test in the
pushed tree (`git grep -n '<issue>/AC<n>'`); an issue without them is a prepare step skipped, and a
criterion no test names is a review that missed it (until issue 1118 makes the tracker refuse it).
- Every fixing agent's report gives the whole unit suite's result and time; a report naming only some packages'
tests is the whole-suite rule not kept.
- The lead's save point names each watcher with its model; a watcher on the smallest model is the failure of
"Following and reporting".
- A report or a save point that names a "phase" of a milestone is the old wording: a milestone has steps.
- Every repository's own clone says `main` (`git -C <clone> branch --show-current`); another branch there is
work done outside a worktree (issue 1010).
- After a feature's delivery, `git branch -r | grep feat/<slug>` and `git worktree list` return nothing for it. - After a feature's delivery, `git branch -r | grep feat/<slug>` and `git worktree list` return nothing for it.
- A design whose milestone issues are partly resolved says `in-progress`, and one whose issues are all - More than one open pull request in a repository that share no feature name, or a feature step whose issues
resolved and delivered says `implemented`: read its Milestones section against `issue-tracker.get` for each
issue. A design that says `designed` with a resolved milestone issue is a state not written back.
- A design's Milestones section names each milestone's issues and nothing of their state: a "delivered", a date
or a pull request in an entry is a copy of the tracker's state (issue 1038), read with
`grep -n 'delivered' <design>` over its Milestones section.
- Every open issue of a design that says `in-progress` and has no agent on it shows, in `issue-tracker.get`, a
note saying what it waits for; one without such a note is a pause not written back.
- The milestone issues in `fixing` (`issue-tracker.list`) belong to one design at most: milestone issues
of two designs in `fixing` at once is the failure of "one design is built at a time".
- More than one open pull request in a repository that share no feature name, or a milestone whose issues
stay in `fixing` for more than a working day, is the smell: stop and bring it to the operator. stay in `fixing` for more than a working day, is the smell: stop and bring it to the operator.
- A review report that asks a change outside the issues' done-when and acceptance criteria, or a fourth round, is the failure of the - A review report that asks a change outside the issues' done-when, or a fourth round, is the failure of the
review rules; `issue-tracker.list` with `flag: abandoned` lists issues left in `fixing` with nobody on them. review rules; `issue-tracker.list` with `flag: abandoned` lists issues left in `fixing` with nobody on them.
- A pull request green for more than 5 minutes with no Approve asked is a fault of mesh-delivery - A pull request reviewed and green but not marked ready for more than 15 minutes is a lead that did not
(`mesh-delivery.show`), said to the operator. mark it.
- Every review record of a release names one reviewer (`mesh-delivery.show`, the review record), and the lead's - A step that adds or changes a module marked ready with no integration test that starts the module whole is
save point names one reviewer id per release; a second reviewer within one release is a reviewer not kept the failure of phase 3's fourth step.
(issue 1042). A pull request whose ask says it has no review record at its head is a record not written.
- A milestone that adds or changes a module whose pull request has no integration test that starts the module
whole is the failure of the check step's fourth item.
- A bed run from `.work/<slug>/mesh-lab` that fails naming a `.work/<slug>/<repo>/…` path it cannot find is a - A bed run from `.work/<slug>/mesh-lab` that fails naming a `.work/<slug>/<repo>/…` path it cannot find is a
missing sibling worktree, not a mesh fault. missing sibling worktree, not a mesh fault.
+23 -33
View File
@@ -27,33 +27,28 @@ one delivery**. Everything before the push is local and fast.
model answering exact questions with file:line). If a design question is open, it is settled before any model answering exact questions with file:line). If a design question is open, it is settled before any
code is written: on the issue, through the knowledge base, or by the operator. A fix that turns out to code is written: on the issue, through the knowledge base, or by the operator. A fix that turns out to
need a second repository, or more than about an hour, stops here and becomes a feature; one that needs a need a second repository, or more than about an hour, stops here and becomes a feature; one that needs a
decision stays a small fix. **The session's own model (the larger) then writes the issue's acceptance decision stays a small fix.
criteria** as playbook [07](07-feature-branches.md) step 0 item 5 says: each one sentence, given, when, then,
naming the level of the test that proves it, as an `issue-tracker.note` headed `Acceptance criteria`. A
playbook or design edit has none: its check is `sh merge-check.sh`.
2. **Move the issue to `fixing`** with `by` as who@node. 2. **Move the issue to `fixing`** with `by` as who@node.
3. **Build on a local branch** `fix/<issue>-<slug>`, in a worktree of its own branched from `main`, never in 3. **Build on a local branch** `fix/<issue>-<slug>`, in a worktree of its own branched from `main`. Write the
the repository's own clone, which stays on `main` (playbook [07](07-feature-branches.md), step 0, item 2). Write the test that fails today first; make the change; keep `gofmt`, `go vet` and the unit tests of what changed green
test that fails today first; make the change; keep `gofmt`, `go vet` and the repository's whole unit suite green (each well under a second; never a merge check or a database locally: how-we-build §5, ADR 0391).
before every commit (each test well under a second; never a merge check or a database locally: how-we-build §5, ADR 0391). Commit, naming the issue. For a playbook or design edit, run the repository's own checks (in hq,
The tests are written from the acceptance criteria, at least one per criterion at its level, each citing the
criterion it proves by its label (`<issue>/AC<n>`, playbook 07 step 0 item 5). Commit, naming the issue. For a playbook or design edit, run the repository's own checks (in hq,
`sh merge-check.sh`). `sh merge-check.sh`).
4. **One review of the local commit, before anything is pushed** (a reviewer that did not write it, larger 4. **One review of the local commit, before anything is pushed** (a reviewer that did not write it, larger
model; at most 3 rounds, as playbook 07; from round 2 only what is wrong; it reads each acceptance criterion model; at most 3 rounds, as playbook 07; from round 2 only what is wrong; a finding outside the issue's
against its test, as playbook 07 step 2 item 6; a finding outside the issue's done-when and its acceptance done-when is opened as an issue of its own, never asked as a change). The reviewer's findings go back to the same agent that wrote the fix, continued by
criteria is opened as an issue of its own, never asked as a change). The reviewer's findings go back to the same agent that wrote the fix, continued by
its id, so the fix keeps its context. A playbook or design edit of a few lines may skip the reviewer: the its id, so the fix keeps its context. A playbook or design edit of a few lines may skip the reviewer: the
operator's Approve of its merge is its review, and the review record says so. operator's word on the pull request is its review, and the lead marks it ready on that word.
5. **Push once, try it, and open the pull request.** The trunk is what runs and nothing holds merged work back 5. **Push once and open the pull request.** The trunk is what runs and nothing holds merged work back
(ADR 0401), so a fix waits for no other change's delivery. `mesh-controller.try` on the pushed branch runs (ADR 0401), so a fix waits for no other change's delivery. The forge's merge
the integration tests once, and the pull request is opened only on a pass; the forge's merge check runs check runs the integration tests, the only
them again as the gate (playbook [07](07-feature-branches.md) step 3). They are never run on the machine. A place they run (the operator, 2026-10-16). A failed check
failed try or check goes back to the same agent with the failure, and the reviewer reads only the change it makes. goes back to the same agent with the failure, and the reviewer reads only the change it makes.
6. **The pull request's description carries the review record** (`Reviewed-by: <who@node> at <full sha>`, 6. **The reviewer marks it ready at once** (for an edit that skipped the reviewer, the lead, on the
playbook [07](07-feature-branches.md) step 3), and mesh-delivery asks the operator's Approve as soon as its operator's word) with `mesh-delivery.mark-ready` (the delivery of the head it approved, who reviewed it and
checks pass and merges the approved head (ADR 0421); nobody marks, labels, comments or rechecks. A fix to a module that changes how it runs carries an why), which writes the label `mesh/ready` and then `ready at <full sha>`, the order the forge counts (issue 913);
integration test starting the module whole against the real bus and store, run by `mesh-controller.try` and the forge's checks mesh-delivery merges it when its checks pass. A fix to a module that changes how it runs carries an
integration test starting the module whole against the real bus and store, run by the forge's checks
(ADR 0391 §1, §6); unit tests alone are not that proof, and the lab is not used. The operator's word is waited for only when the fix changes what the operator sees, removes data, (ADR 0391 §1, §6); unit tests alone are not that proof, and the lab is not used. The operator's word is waited for only when the fix changes what the operator sees, removes data,
or needs the operator's terminal. or needs the operator's terminal.
7. **Follow its delivery plan to done** (`mesh-controller.plans` with `wait` and `until: done`), prove the fix on 7. **Follow its delivery plan to done** (`mesh-controller.plans` with `wait` and `until: done`), prove the fix on
@@ -82,9 +77,9 @@ code.
|---|---|---| |---|---|---|
| building the fix | 1 hour | it is a feature: stop and plan it under playbook 07 | | building the fix | 1 hour | it is a feature: stop and plan it under playbook 07 |
| the review | 15 minutes | ask the reviewer where it stands | | the review | 15 minutes | ask the reviewer where it stands |
| the ready mark | 15 minutes after the review approved | the reviewer marks it, or the lead does |
| the merge check | 15 minutes | read `mesh-controller.queue` and the check's log | | the merge check | 15 minutes | read `mesh-controller.queue` and the check's log |
| the Approve's ask | 5 minutes after both checks pass | read `mesh-delivery.show`: an ask not raised is a fault, said to the operator | | the merge after ready | 5 minutes after the checks pass | read `mesh-delivery.show` for the delivery |
| the merge after the Approve | 5 minutes | read `mesh-delivery.show` for the delivery |
| the delivery plan | its class's budget (`mesh-delivery.times`), at most 30 minutes | read `mesh-controller.plans`, then `mesh-delivery.stalled` | | the delivery plan | its class's budget (`mesh-delivery.times`), at most 30 minutes | read `mesh-controller.plans`, then `mesh-delivery.stalled` |
A wait past its bound is said to the operator at once, with what is being done about it. A wait past its bound is said to the operator at once, with what is being done about it.
@@ -99,13 +94,8 @@ reading itself and uses one subagent only for the review.
- A small fix has one pull request with one or few commits naming its issue, opened after its review: a pull - A small fix has one pull request with one or few commits naming its issue, opened after its review: a pull
request with many heads is a fix reviewed after its push. request with many heads is a fix reviewed after its push.
- Its issue moves to `resolved` within the hour its delivery plan ends. - Its issue moves to `resolved` within the hour its delivery plan ends.
- A pull request with no review record at its head (the Approve's ask says so first), a fix to a module's - A pull request reviewed and green but unmarked
running pushed with no integration test of the module whole, or a review report for more than 15 minutes, a fix to a module's running marked ready with no integration test of the module whole, or a review report
asking a change outside the issue's done-when and acceptance criteria, is the failure of steps 4 to 6. asking a change outside the issue's done-when, is the failure of steps 4 to 6.
- A fixed code issue shows a note headed `Acceptance criteria` (`issue-tracker.get`) older than its first
commit, and each criterion's label is cited by a test in the pushed tree
(`git grep -n '<issue>/AC<n>'`); one without is step 1 skipped.
- An issue in `fixing` with nobody on it shows in `issue-tracker.list` with `flag: abandoned`; a stopped fix - An issue in `fixing` with nobody on it shows in `issue-tracker.list` with `flag: abandoned`; a stopped fix
moves it back to `open` with its note. moves it back to `open` with its note.
- The repository's own clone says `main` (`git -C <clone> branch --show-current`); another branch there is a
fix built outside a worktree (issue 1010).
-1
View File
@@ -36,7 +36,6 @@ one-for-one — `mesh-catalog`, `mesh-sdk` and `mesh-tools` exist where the tabl
| `mesh-surfaces` | 3 | tools, web, cli | | `mesh-surfaces` | 3 | tools, web, cli |
| `mesh-sdk` | — | the stable spine modules build against — the tool-serving SDK harness, the messaging/event framework, the contracts and core primitives. Holds nothing per-module and nothing volatile (ADR 0039). | | `mesh-sdk` | — | the stable spine modules build against — the tool-serving SDK harness, the messaging/event framework, the contracts and core primitives. Holds nothing per-module and nothing volatile (ADR 0039). |
| `mesh-lab` | — | **exists.** The lab — scenario lifecycle, networking, placement. Ships to nobody; runs on a workstation. | | `mesh-lab` | — | **exists.** The lab — scenario lifecycle, networking, placement. Ships to nobody; runs on a workstation. |
| `mesh-delivery` | — | the delivery domain: the parts that build, check and deliver a change by watching and writing keys on the bus, nine modules each whole in it, prefixed `mesh-delivery-` (ADR 0429). Made by the forge module from its config (ADR 0428). |
Tier 4's shape is open, and deliberately so: see ADR 0019 and Tier 4's shape is open, and deliberately so: see ADR 0019 and
research 005. research 005.
+47 -122
View File
@@ -1,144 +1,69 @@
--- ---
layer: as-is layer: as-is
status: implemented status: implemented
code: [mesh-tools, mesh-host, mesh-controller] code: [mesh-catalog modules/mesh-console, mesh-tools src/mesh.ts, mesh-tools src/http.ts, mesh-tools src/runtime.ts, mesh-controller internal/broker, mesh-controller cmd/mesh-controller/check.go, mesh-controller cmd/mesh-controller/seatverbs.go]
updated: 2026-10-17 updated: 2026-10-10
decisions: decisions:
- "ADR 0152" - "ADR 0152"
- "ADR 0175" - "ADR 0095"
- "ADR 0195" - "ADR 0037"
- "ADR 0197"
- "ADR 0367"
- "ADR 0154" - "ADR 0154"
--- ---
# The mesh MCP server, as it runs # The mesh MCP server, as it runs
**The `mesh-console` container no longer exists.** Read 2026-10-17 from `mesh-tools` (package **The mesh's tools reach a person through a module the mesh assigned to their machine.** Since
`node-tools`), `mesh-host` and `mesh-controller` on their forges' main, and from the live mesh 2026-09-30 a workstation that is a node can be assigned `mesh-console`; the mesh mints a bus account
through `mesh_overview`, `mesh_machine`, `mesh_runtimes` and `mesh_describe`. Checked: the `<node>.mesh-console`, seals its credential to the machine, and the container binds
catalogue (`mesh-catalog`) carries no module named `mesh-console` any more — the name survives only `127.0.0.1:<port>` with the port the mesh assigned for the manifest's declared one. An agent on the
in the glossary's retired-words list and in a test fixture — and none of the four nodes answers a machine is pointed at `http://127.0.0.1:<port>/mcp` and sees the mesh's tools; a person uses the same
module of that name. endpoint. Nothing on the machine holds a credential a person had to carry.
## What it is now
**The tool runner is one of the mesh's core components**, named `node-tools` beside
`mesh-controller`, `mesh-host` (the node-engine) and `nats` (the bus) — the mesh's own repositories'
check treats the four the same way, as the core whose health the mesh itself defines rather than a
module's own. `mesh-host` and `mesh-controller` supervise, compose and account for it as a core
process rather than as an ordinary assignment (`ComponentNodeTools`/`BusNodeTools` in both, the
witness and lease code that restarts and rolls it back, the account named `<node>.node-tools` on
the bus).
**It still carries a manifest, `node-tools/module.json` in `mesh-tools`,** and that manifest is
where its grants live: `invokes` names `*`, so its account may call every tool on the mesh;
`waits` names the five event families `mesh_wait` may subscribe to (the knowledge base's reviews,
mesh-delivery's transitions, the forge's status, the controller's checked event, and the operator
channel's decision for the agent module) — the bus refuses a subscription outside this list
(ADR 0367 §2); and it
`provides` one provision, the MCP endpoint, at node scope, and `listens` on the port it serves from,
opened only to the machine itself. Its code lives under `node-tools/` in the `mesh-tools`
repository (`internal/console`, `internal/announce`, `internal/bus`).
**One runtime per node, run by the node-engine as a sibling process, never a container**
(ADR 0175). It holds the node's one bus credential and may call every tool on the mesh; its account
publishes nothing else. It binds `127.0.0.1:<port>` and refuses to bind anything else, said before
binding rather than after (`node-tools/internal/console/http.go`, citing ADR 0152): whoever is on
the machine owns the mesh there, and nobody else may reach this port. An agent or a person on the
node is pointed at `http://127.0.0.1:<port>/mcp` once; the `serverInfo.name` the handshake answers
is `mesh`.
## What it answers ## What it answers
`initialize`, `notifications/initialized`, `ping`, `tools/list`, `tools/call`, over HTTP, one JSON `initialize`, `tools/list`, `tools/call`, over HTTP, one JSON body per request, no session and no event
body per request, no session and no event stream (`node-tools/internal/console/mcp.go`). stream. `tools/list` is what the running modules answered: every tool runner built on or after that day
serves a `tools` verb for its module, and the mesh MCP server asks the catalogue for the roster and each module
for its tools. A module that did not answer is named in the list's `_meta.notAnswering`. On the day it
shipped that was 36 of 51 modules — those that serve no tools at all, and those whose rebuilt runtime the
mesh records rather than rolls out — and 62 tools from the rest.
**It announces seven tools, not five** (ADR 0195, extended by ADR 0367): `mesh_overview`, `tools/call` reaches any tool by `<module>.<tool>`, listed or not. The mesh MCP server's grant is `*`, so what it
`mesh_machine`, `mesh_search`, `mesh_describe`, `mesh_call`, `mesh_runtimes` and `mesh_wait`. The may call is every tool on the mesh; its account may publish nothing else and subscribes nothing.
handshake's `instructions` text names all seven and the four address forms. `mesh_overview` is
where the mesh-wide count of modules and tools is read live — a number copied into this document
would drift; asked on 2026-10-17 it answered 103 modules and 645 tools across the whole mesh, with
two module instances (`route-proxy` on two of the nodes) named as not answering rather than silently
absent.
**Addresses, exactly as the handshake states them:** `<seat>.<verb>` for a seat held once for the No call is refused for want of a person's approval: a tool runner serves every call it is issued, whoever made
mesh (`mesh-controller.status`); `<node>/<seat>.<verb>` for a seat every machine holds it, and cannot tell an agent from the operator (ADR 0259 §6, issue 340). An act waits for the operator's warrant
(`<node>/node-packet-filter.rules`); `<node>/<module>.<tool>` for a module assigned to one machine; only where its module asks first, in its own code: the agent module's instruction proposals, and the
and `<module>.<tool>` also for a module whose instances are interchangeable. A tool not listed can controller's settings proposals and the actions it offers on a condition.
still be called by address — listing is discovery, calling is the grant — and a module that is not
interchangeable is refused with the machines it runs on rather than guessed at.
**What exists is what announced itself** (ADR 0197). Every node's tool runner answers the NATS services
protocol's `$SRV.PING`, `$SRV.INFO` and `$SRV.STATS`, one service per process, naming in each
endpoint's metadata which module, seat, scope and machine serves it. `mesh_overview` and
`mesh_machine` gather one request's answers: at least 750ms so a runtime that only just answered
`$SRV.PING` is still waited for, up to 5s for one that said it is there but has not yet sent its
full announcement — a fixed shorter window lost the largest runtime's listing once a live
measurement found how slow its round trip over the bus could be. An announcement unchanged since
last asked (by its digest) is not re-fetched, which is why `mesh_runtimes` can show a runtime
answering in under 100ms for hundreds of kilobytes of discovery.
## mesh_wait
**The seventh tool, added by ADR 0367**, lets a session wait on the bus for an event instead of
polling: a call names one or more granted event families and an optional filter, and either answers
the first match or says it timed out. The tool runner's own bus account hears only the families its
manifest's `waits` list names; an event it hears is never taken as the state — a session reads the
state from the verb that owns it once woken. It is meant to be called in a loop from a background
command, not inline, because one call blocks until it answers or times out.
## The mesh's own verbs ## The mesh's own verbs
**These already answer directly, not as a stated future.** Beside every assigned module's tools, *Since 2026-09-30 evening (ADR 0154).*
the tool runner asks the `mesh-controller` seat for its own verbs the same way (ADR 0154): measured The mesh MCP server asks the `mesh-controller` seat's `tools` verb beside the modules and lists every role's
live, the mesh-controller runtime is one instance, mesh-wide, and it answers 63 verbs for that one tools as `<seat>.<verb>` — `mesh-controller.status`, `mesh-controller.push` and the other ten. A call
seat (`status`, `push`, `assign`, `build`, `artifacts` and the rest), and the `mesh-controller` to `<prefix>.<name>` reaches the seat when the prefix is a seat declaring that verb, and the module
*module* answers none of its own — it serves no module tools, only its seat's, exactly as design 33 otherwise; `seat:<seat>.<verb>` says so outright. When the controller does not answer, the list
described while it was still only designed. The 56-to-93 seat-verb counts each node's tool runner names `mesh-controller (seat)` as not answering and carries the modules' tools regardless. The
answers (below) are a different thing: the tool runner serves the verbs of whichever node-scoped `mesh-controller` *module* is always named as not answering: it serves no module tools, only its seat's.
seats are held on that node (`node-backup`, `node-build-agent`, `node-service-manager` among them),
on behalf of the modules holding them, not the mesh-controller seat's — that one is served by the
mesh-controller runtime itself, a separate process, and the tool runner only asks it and relays what
it answers.
## Where it runs ## Around it
**Every node, not a per-operator assignment.** Measured live: all four of the mesh's machines - **`invokes`** in a manifest is the grant. It is composed into the bus's user list exactly as a
answer a `node-tools` runtime, each with its own module and tool count (between 294 and 312 tools, person's account is; the mesh MCP server is the only module that declares it.
56 to 93 seat verbs, in this reading). This is a fact that shipped past the design it came from: the - **`module check <file|dir>…`** on the controller's binary judges a manifest with no mesh: the strict
to-be document still describes it as assigned to "whichever machines an operator sits at"; what parse, every per-manifest problem, and the rules between the manifests given. It prints what it cannot
actually runs is the opposite — every node the mesh holds carries one, because it is core judge without a store rather than refusing. The mesh MCP server's own manifest was the first thing checked
infrastructure the mesh itself depends on (discovery, the controller's own verbs, every module's with it, and the whole catalogue passes.
tools), not a convenience assigned to a workstation. There is accordingly no assign or unassign verb - **The person's client remains.** `operator issue` and `mesh tools|call|mcp` with a credential file
for it: removing it from a node is not an operation the controller or the catalogue exposes. still work, for a machine that is not a node and for a mesh not yet able to assign anything.
`mesh tools --console <url>` goes through a running mesh MCP server with no credential; it is covered by the
runtime repository's tests and was not exercised on the live mesh.
## What this repository's prior text got wrong ## What shipped bent
The document this replaces described `mesh-console` as a catalogue module served by a container and - A module registered by hand from the catalogue with `--source <url> --path modules/<m>` records a URL,
assigned only to workstations. That module and its container are gone; the manifest is not — it not a place on the git seat: `--self` takes the forge path form (`<owner>/<repository>`), which the
moved to `node-tools/module.json`, still declares `invokes: ["*"]`, and gained `waits` and the operator did not pass. The rebuild-on-merge matched the URL anyway.
`mcp-endpoint` provision, described above. What also moved is which language builds it: the - Modules whose upgrade policy is *record* — the forge among them — answered `tools` only once
manifest's `build.artifacts` names a Go binary built from `cmd/node-tools`, and that is what a node something pushed their rebuilt runtime; until then they are listed as not answering while still
runs. The repository still carries the earlier TypeScript sources (`node-tools/src/mesh.ts`, callable. That is the policy doing what it says, not a fault of the mesh MCP server.
`http.ts`, `runtime.ts`, `announce.ts`, `broker-nats.ts`, `client.ts`, `launch.ts`, `main.ts`,
`mcp.ts`) on main, but the manifest does not build from them. The quirks the prior text recorded
from the `mesh-console` era (a module registered by hand recording its source as a URL; a
*record*-policy module staying listed as not-answering until its rebuilt runtime is pushed) are not
re-checked here: they were about the `mesh-console` container's registration path, which no longer
exists.
## How it is checked
- `mesh_overview` and `mesh_machine <node>`: the live tool and seat-verb counts, and which modules
are named not-answering.
- `mesh_runtimes`: which runtimes answered discovery, how long they took, how large their
announcement was, and whether it was shortened to fit the bus — read this when an address is said
to be missing or on another machine.
- `mesh_describe <address>` for one tool's schema; a call through `mesh_call` reaches the module or
seat over the bus and the answer is its own, unshaped.
- The code: `mesh-tools` (`node-tools/internal/console`, `internal/announce`, `internal/bus`),
`mesh-host` and `mesh-controller`'s core-component handling of `node-tools` (witness, lease,
bus-user naming). Their unit tests hold the loopback-only refusal, the seven-tool handshake, the
discovery gather window and `mesh_wait`'s event-then-read-state rule.
- `knowledge-base.get` with `kind: decision` and number 152, 175, 195, 197, 367 or 154.
@@ -1,116 +0,0 @@
---
layer: as-is
status: implemented
code: [mesh-controller, mesh-catalog modules/claude-code, mesh-catalog modules/gitea, mesh-catalog modules/ssh-client]
updated: 2026-10-17
decisions:
- "ADR 0266"
- "ADR 0399"
- "ADR 0409"
- "ADR 0410"
---
# Accounts on a machine, as they run
**A node record names at most two logins: the operator account, always, and an agent account, on one node of
four. Where a node names no agent account, agents run as the operator account. Nothing ties an identity to a
login, and a module that needs one declares it itself.** Read on 2026-10-17 from the controller's and the
catalogue's code on main (both local clones were a few commits behind the forge, so each claim below was
also checked against a live read) and from the live node records. The change planned in ADR 0411 to 0414
replaces this document when it is built.
## The node record
- **The operator account is stated at the controller's terminal** with `node account <node> <account> [home]`.
It is the person's login on that machine. The controller refuses to set it to the node's agent account.
- **The agent account is stated at the controller's terminal only** with `node agent-account <node> <account>
[home]`, and cleared with `--clear`. It is no verb and no setting, so an agent cannot name itself one
(ADR 0266 §1). The controller refuses root, a malformed login, a well-known service account and the
operator account.
- **Five machine facts come from the record.** `account` and `account-home` are the operator's, absent when
the node has no operator account. `agent-account` is the agent account, or the operator account where none is
named; `agent-home` is its home (`/home/<account>` when no home is stated); `agent-root` is `never` only where
the node names an agent account of its own, otherwise empty. Modules read them as `${machine:...}` words.
- **One node of four names an agent account today**, the control node. The workstation, the laptop and the
home server name none: `mesh-controller.node` says there "agents run as the operator account; no agent
account is named", and the plan for the workstation declares the `claude-code` account resource with the
operator's name and no `root` field. The tool runner also receives `MESH_AGENT_ACCOUNT` and `MESH_AGENT_HOME`.
- **Not verified:** whether the mesh-config snapshot carries either account. The controller's code reads both
from its own node table (columns for account, account home, agent account and agent home); no second place
that states them was found.
## What claude-code does with the agent account
- **It declares a `user` resource** named `${machine:agent-account}` with home `${machine:agent-home}` and
`root: ${machine:agent-root}`, and a `~/.claude` directory under that home owned by it, mode 0700. On the
control node the plan holds the resource with `root: never`; elsewhere it holds the operator's account.
Where the account is new, the mesh creates it with a home, its own group and no password (ADR 0266 §2).
- **`claude-agent`** is a file the module writes at `/usr/local/bin/claude-agent`. Run as the agent account
it starts `claude`; run as any other account it runs `sudo -iu <agent account> claude` through that
account's own sudo, carrying the operator's guard override only if it is set in that shell. The file
`/etc/claude-code/agent-account` holds the account's name.
- **The guard of ADR 0266 §28** is the module's hook on every tool call. On a node with an agent account
of its own it refuses each call of a session that runs as another account, with no override, and tells the
session to start with `claude-agent`. Its record goes to the machine's journal. The same guard also refuses
ssh to a mesh machine and writes to `/etc/hosts` and the resolver file (ADR 0245).
- **Not verified live:** this rests on the guard's code and README, not on a refusal seen on a running machine.
## The judgement that the agent account cannot become root
- **The node-engine judges `root: never` on every look**, by reading only: uid 0, a group that grants root,
a sudo, doas or polkit rule, a container runtime's socket or API port, a secret the mesh placed for another
account, a setuid program no package owns, and kernel link protection off (ADR 0266 §12 to §16). A way found,
or a question unanswered, makes the account resource unhealthy. It does not prevent any way to root.
- **The controller's self-check holds** that a node naming an agent account has it judged, in a verdict heard
within 15 minutes, as unable to become root (ADR 0266 §21 to §22). Otherwise it raises the urgent condition
`agent-can-become-root`. `mesh-controller.node` shows the verdict: for the control node it reads "cannot
become root without a person", judged the same day.
- **On the three nodes without an agent account** the check does not apply. The controller instead holds an
urgent condition `root-not-free` on each machine that runs reads or the mail notifier. Two of the three are
silenced until 2026-10-23 on the operator's decision; the home server's is open and not silenced.
## The accounts agents push as on the forge
- **Today the forge has a user per machine**, made by the gitea module's `forge-account` provision and
holding that machine's key, plus a write token the module still mints for each (ADR 0398, ADR 0400). ADR 0410
decides one forge user, `nox-agent`, in the agents' write team, each machine's key titled by machine. No tool
reads the forge's user list, so whether `nox-agent` exists yet is **not verified**.
- **The forge key is meant for the agent account** (ADR 0409 §2), but a module cannot run as that account:
the controller refuses it (issue 989, open). So the `ssh-client` module's key keeper still runs as the
operator account.
- **An ordering deadlock** between `ssh-client`, which wants `forge-account`, and `gitea`, which provides it,
stopped every node's declaration from composing on 2026-10-17 at 00:35 until gitea was registered three
minutes later (issue 986). The four urgent conditions it raised were not observed again but stay raised,
because the controller's declaration check times out and never clears them (issue 992).
Git over ssh was proven from the laptop only; the proof on the other three, and retiring the forge token the
module still places, wait on issues 989 and 991 (the latter in `fixing`).
- **Main stays closed**: a push to the main branch is refused, and a merge waits for a person (ADR 0398).
## Logins on the bus
Not here: see [`16-logins-on-the-bus.md`](16-logins-on-the-bus.md).
## What it cannot do
- **No identity carries a login.** An agent, a person and a module are not tied to an account on a machine;
the node record names a login per node and nothing else.
- **A module declares a login itself.** Each module that needs an account writes a `user` resource of its own,
and nothing checks that two modules agree or that a login exists on every machine that needs it.
- **Agent and operator share a login on three of four machines.** There the agent runs as the operator account,
which may become root; the guard's account rule does not apply, and the exposure is a held condition, not a fix.
- **No per-agent bus password.** Every session on a machine calls through that machine's tool-runner login
(document 16), so one agent's calls cannot be told from another's.
- **Detects, does not prevent.** The judgement above lists the ways to root it looks for; a way it does not list
is not seen (ADR 0266 §17).
## How it is checked
- `mesh-controller.node` with a node name: "agents run as" line, the verdict and when it was judged, the open
conditions (`agent-can-become-root`, `root-not-free`, `uncomposable`).
- `mesh-controller.plan` with a node name: the `claude-code.agent-account` resource and its `root` field; with
`files` set, the files given, including `claude-agent` and `agent-account`.
- `mesh-controller.conditions` for the held conditions; `claude_code_guard` for the guard's record, `claude-code`
tools for its status.
- `knowledge-base.get` with `kind` decision and number 266, 409 or 410; `issue-tracker.get` with 989 or 991.
- The code: the controller's node-record and agent-account files, and the `claude-code` module's manifest and
guard in the catalogue. Their unit tests hold the node record's refusals and the guard's account rule.
-1
View File
@@ -25,7 +25,6 @@ Where the two disagree, the implementation wins and the disagreement is stated.
| [`14-a-modules-state.md`](14-a-modules-state.md) | A module's current state on the bus: what the controller creates, the runtime serves, and the first live use showed | | [`14-a-modules-state.md`](14-a-modules-state.md) | A module's current state on the bus: what the controller creates, the runtime serves, and the first live use showed |
| [`15-the-agent-and-its-licences.md`](15-the-agent-and-its-licences.md) | The operator's agent on every machine and the licence manager that keeps its licences | | [`15-the-agent-and-its-licences.md`](15-the-agent-and-its-licences.md) | The operator's agent on every machine and the licence manager that keeps its licences |
| [`16-logins-on-the-bus.md`](16-logins-on-the-bus.md) | The bus's one list of logins, a login per module assignment, persons limited to their tools, sessions calling through the tool runner's login, and no effect on any tool | | [`16-logins-on-the-bus.md`](16-logins-on-the-bus.md) | The bus's one list of logins, a login per module assignment, persons limited to their tools, sessions calling through the tool runner's login, and no effect on any tool |
| [`17-accounts-on-a-machine.md`](17-accounts-on-a-machine.md) | The operator and agent accounts on the node record, claude-code's own login and guard, the judgement that the agent account cannot become root, and the forge accounts agents push as |
## What these documents are not ## What these documents are not
+3 -5
View File
@@ -1,11 +1,11 @@
--- ---
layer: to-be layer: to-be
status: in-progress status: proposed
code: code:
- mesh-controller cmd/mesh-builder - mesh-controller cmd/mesh-builder
- mesh-controller internal/builder - mesh-controller internal/builder
- mesh-controller modules/build-agent - mesh-catalog modules/build-agent
updated: 2026-10-17 updated: 2026-10-16
decisions: decisions:
- "ADR 0405" - "ADR 0405"
- "ADR 0262" - "ADR 0262"
@@ -31,8 +31,6 @@ decisions:
# Building a module # Building a module
**Where it stands, 2026-10-17** (the records cleanup, issue 1072; playbook 07, "The design's state is written back"). Built on main: mesh-controller's builder (internal/builder, cmd/mesh-builder) implements the recipe kinds described here, and the sections marked built and proven live match the code. Left: not broken down into milestones; issue 1072 found it saying proposed.
[`12-a-module-repository`](12-a-module-repository.md) says what a module may build — an image, an [`12-a-module-repository`](12-a-module-repository.md) says what a module may build — an image, an
archive, an upstream mirror — and where the result goes. This says **how a build is modelled**, and archive, an upstream mirror — and where the result goes. This says **how a build is modelled**, and
why the current model does not fit what a module is. why the current model does not fit what a module is.
+2 -4
View File
@@ -1,13 +1,13 @@
--- ---
layer: to-be layer: to-be
status: in-progress status: proposed
code: code:
- mesh-sdk src - mesh-sdk src
- mesh-tools src/broker-nats.ts (and broker-amqp.ts until the rollout) - mesh-tools src/broker-nats.ts (and broker-amqp.ts until the rollout)
- mesh-controller internal/link - mesh-controller internal/link
- mesh-controller cmd/mesh-controller (status) - mesh-controller cmd/mesh-controller (status)
- mesh-catalog modules/postgres, modules/keycloak (the Go provisioner loop) - mesh-catalog modules/postgres, modules/keycloak (the Go provisioner loop)
updated: 2026-10-17 updated: 2026-10-06
decisions: decisions:
- "ADR 0224" - "ADR 0224"
- "ADR 0230" - "ADR 0230"
@@ -24,8 +24,6 @@ decisions:
# The module protocol # The module protocol
**Where it stands, 2026-10-17** (the records cleanup, issue 1072; playbook 07, "The design's state is written back"). Built on main: the wire this design describes is in mesh-sdk and mesh-controller's internal/link, and the design's own checks against the code (2026-09-16) agree. Left: not broken down into milestones; issue 1072 found it saying proposed.
**What a module's code and the mesh say to each other.** An SDK is an implementation of this in one **What a module's code and the mesh say to each other.** An SDK is an implementation of this in one
language and nothing more (ADR 0074). language and nothing more (ADR 0074).
@@ -1,11 +1,11 @@
--- ---
layer: to-be layer: to-be
status: in-progress status: proposed
code: code:
- mesh-host internal/bootstrap - mesh-host internal/bootstrap
- mesh-host cmd/mesh-bootstrap - mesh-host cmd/mesh-bootstrap
- mesh-lab test/integration/one-node-mesh.test.ts - mesh-lab test/integration/one-node-mesh.test.ts
updated: 2026-10-17 updated: 2026-10-06
decisions: decisions:
- "ADR 0226" - "ADR 0226"
- "ADR 0067" - "ADR 0067"
@@ -16,8 +16,6 @@ decisions:
# The installation, in full # The installation, in full
**Where it stands, 2026-10-17** (the records cleanup, issue 1072; playbook 07, "The design's state is written back"). Built on main: the node-engine's bootstrap (`mesh-host` internal/bootstrap) carries phases 2 and 3 of the installation, so the steps this text calls manual are partly automated. Left: not broken down into milestones; the text is not yet corrected against the code.
Every step from a machine with nothing to a mesh that maintains itself. Written out explicitly Every step from a machine with nothing to a mesh that maintains itself. Written out explicitly
because it is the procedure everything else depends on, and because the parts that do not exist because it is the procedure everything else depends on, and because the parts that do not exist
yet are easier to see beside the parts that do. yet are easier to see beside the parts that do.
+1 -3
View File
@@ -6,7 +6,7 @@ code:
- mesh-controller - mesh-controller
- mesh-catalog - mesh-catalog
- mesh-lab - mesh-lab
updated: 2026-10-17 updated: 2026-09-15
decisions: decisions:
- "ADR 0067" - "ADR 0067"
- "ADR 0074" - "ADR 0074"
@@ -17,8 +17,6 @@ decisions:
# The work ahead # The work ahead
**Where it stands, 2026-10-17** (the records cleanup, issue 1072; playbook 07, "The design's state is written back"). Phase 2's blocker (issue 053) is resolved and delivered, and the forge's module declares the package registry in full, Phase 2's done-when. Whether every phase holds is checked under issue 1073, which marks it implemented or names what is left.
Everything decided this cycle and not yet built, in the order its dependencies allow. Each phase Everything decided this cycle and not yet built, in the order its dependencies allow. Each phase
ends at something provable on a running mesh, because a phase that ends at a claim is a phase that ends at something provable on a running mesh, because a phase that ends at a claim is a phase that
went missing without anything complaining. went missing without anything complaining.
+3 -5
View File
@@ -1,8 +1,8 @@
--- ---
layer: to-be layer: to-be
status: in-progress status: designed
code: [mesh-controller] code: []
updated: 2026-10-17 updated: 2026-09-25
decisions: decisions:
- "ADR 0084" - "ADR 0084"
- "ADR 0027" - "ADR 0027"
@@ -11,8 +11,6 @@ decisions:
# 23 — Choosing a provider # 23 — Choosing a provider
**Where it stands, 2026-10-17** (the records cleanup, issue 1072; playbook 07, "The design's state is written back"). Built on main: mesh-controller's internal/catalogue/resolve.go implements the model described here, and designs 26, 27 and 29 rely on it. Not yet checked against this text clause by clause, nor given an as-is: issue 1074.
A provision is named for what the consumer's code is coupled to A provision is named for what the consumer's code is coupled to
(ADR 0027): (ADR 0027):
`postgres-database`, not `database`. That decides *what kind* of provider satisfies a requirement. `postgres-database`, not `database`. That decides *what kind* of provider satisfies a requirement.
@@ -5,7 +5,7 @@ code:
- mesh-controller internal/inventory - mesh-controller internal/inventory
- mesh-controller internal/catalogue - mesh-controller internal/catalogue
- mesh-controller cmd/mesh-controller - mesh-controller cmd/mesh-controller
updated: 2026-10-17 updated: 2026-10-16
decisions: decisions:
- "ADR 0181" - "ADR 0181"
- "ADR 0182" - "ADR 0182"
@@ -16,11 +16,6 @@ decisions:
- "ADR 0117" - "ADR 0117"
- "ADR 0399" - "ADR 0399"
- "ADR 0400" - "ADR 0400"
- "ADR 0412"
- "ADR 0415"
- "ADR 0416"
- "ADR 0417"
- "ADR 0422"
--- ---
# 29 — A node has operator accounts, and the mesh owns what lives under a home # 29 — A node has operator accounts, and the mesh owns what lives under a home
@@ -63,15 +58,6 @@ These are a **family**, not one module: an `ssh-client` module, a shell module,
module, each a *universal-tier* consumer of the account fact — assigned wherever a person logs in, module, each a *universal-tier* consumer of the account fact — assigned wherever a person logs in,
which is every node, unlike the graphical stack that a capability gates. which is every node, unlike the graphical stack that a capability gates.
**A folder a module owns says how it treats a file it did not write** (ADR 0416, ADR 0422). Declaring a
directory under a home owns it (ADR 0182 §1), and the owner alone writes there and reacts to every file
that enters it, never losing what the file held. The manifest says how, on the directory itself:
`unknown` is `move-out` (the node-engine moves every path the manifest does not declare and `knows` does
not name into `~/.local/state/mesh-moved/<module>/`, with a list), `module` (the module's own process
reacts, as ssh-client does in `~/.ssh`) or `program` (everything in it is the program's, as in a
service's data directory). `move-out` is only for a folder under a home, the home of the account the
assignment runs for (ADR 0412 §5). A folder many modules write in is not declared at all.
## 3. The whole of `~/.ssh` is the mesh's — with one boundary drawn inside it ## 3. The whole of `~/.ssh` is the mesh's — with one boundary drawn inside it
The predecessor owned a single file (`~/.ssh/config`) and left the rest alone; it drifted, because The predecessor owned a single file (`~/.ssh/config`) and left the rest alone; it drifted, because
@@ -98,9 +84,6 @@ ADR 0117 draws for the uplink and the sshd
module draws for the firewall: **the mesh must never be able to arrange the one failure that severs module draws for the firewall: **the mesh must never be able to arrange the one failure that severs
its own way back in.** The carve-out is not a convenience; it is that rule, in `~/.ssh`. its own way back in.** The carve-out is not a convenience; it is that rule, in `~/.ssh`.
Since ADR 0415 and ADR 0417 the found class no longer holds in `~/.ssh`: ssh-client moves out what it
does not know into a kept folder, and never moves a key or `authorized_keys`, which keeps this boundary.
## 4. Keys are the mesh's to generate — through a CA, and existing keys are adopted, not replaced ## 4. Keys are the mesh's to generate — through a CA, and existing keys are adopted, not replaced
Key *generation* is the mesh's, not each node's improvising its own. The clean form is an **SSH Key *generation* is the mesh's, not each node's improvising its own. The clean form is an **SSH
@@ -1,8 +1,8 @@
--- ---
layer: to-be layer: to-be
status: in-progress status: proposed
code: [mesh-controller] code: []
updated: 2026-10-17 updated: 2026-10-06
decisions: decisions:
- "ADR 0238" - "ADR 0238"
- "ADR 0221" - "ADR 0221"
@@ -15,8 +15,6 @@ decisions:
# 30 — The mesh updates itself on a push # 30 — The mesh updates itself on a push
**Where it stands, 2026-10-17** (the records cleanup, issue 1072; playbook 07, "The design's state is written back"). Built on main: mesh-controller's delivery plans, merge gate and merge window (plan.go, release_plan.go, merge_gate.go, merge_window_bus.go, changeplan.go, with their migrations), which design 47 builds on. Not yet checked against this text nor given an as-is: issue 1075.
**Today the mesh does not update itself; a person drives the pipeline by hand, and one class of **Today the mesh does not update itself; a person drives the pipeline by hand, and one class of
change freezes it.** A code change lands in `mesh-controller` or `mesh-catalog`, and getting it onto change freezes it.** A code change lands in `mesh-controller` or `mesh-catalog`, and getting it onto
the machines is a sequence somebody types. The predecessor's pipelines rebuilt and redeployed on a the machines is a sequence somebody types. The predecessor's pipelines rebuilt and redeployed on a
+8 -15
View File
@@ -1,13 +1,11 @@
--- ---
layer: to-be layer: to-be
status: implemented status: designed
code: [mesh-tools, mesh-host, mesh-controller] code: [mesh-catalog, mesh-tools, mesh-controller]
updated: 2026-10-17 updated: 2026-10-03
decisions: decisions:
- "ADR 0197" - "ADR 0197"
- "ADR 0195" - "ADR 0195"
- "ADR 0175"
- "ADR 0367"
- "ADR 0160" - "ADR 0160"
- "ADR 0159" - "ADR 0159"
- "ADR 0152" - "ADR 0152"
@@ -165,16 +163,11 @@ matched it by URL.
## What this does not settle ## What this does not settle
- **The mesh's own verbs on the bus.** Design 33's third family: these now answer directly, through - Narrowing a mesh MCP server's grant per assignment. ADR 0046 makes it a setting; nothing reads one yet.
the `mesh-controller` seat, under ADR 0154 — settled, not open. [As-is 13](../00-as-is/13-the-console.md) - The mesh's own verbs on the bus. Design 33's third family; this document only says where they appear
records it live. once they exist.
- **Narrowing the tool runner's grant per assignment.** ADR 0046 makes it a setting; nothing reads - A person's identity behind the mesh MCP server. The mesh sees the mesh MCP server's account; design 15 keeps the
one yet. Owned by [design 58](58-agent-work-as-the-meshs-own.md) milestone 6 (issue 883): an question open.
identity asked for each assignment is one of that milestone's parts.
- **A person's identity behind the mesh MCP server.** The mesh still sees only the tool runner's own
account, never the caller's; design 15 raised the question and leaves it open. Owned by the same
milestone (design 58 milestone 6, issue 883): its outcome is that every person, agent and module
becomes an identity with a role, and a call carries which one made it.
## References ## References
@@ -2,10 +2,8 @@
layer: to-be layer: to-be
status: in-progress status: in-progress
code: [mesh-catalog modules/claude-code] code: [mesh-catalog modules/claude-code]
updated: 2026-10-17 updated: 2026-10-13
decisions: decisions:
- "ADR 0411"
- "ADR 0412"
- "ADR 0343" - "ADR 0343"
- "ADR 0274" - "ADR 0274"
- "ADR 0273" - "ADR 0273"
@@ -308,27 +306,21 @@ operator's word — and, for the operator's own home, only at the terminal or on
## 9. The account the agent runs as ## 9. The account the agent runs as
*Added 2026-10-08 by ADR 0266; changed 2026-10-17 by ADR 0411 and ADR 0412.* *Added 2026-10-08 by ADR 0266.*
Everywhere above, "the operator's home" and "the operator account" mean **the account the agent runs as**: Everywhere above, "the operator's home" and "the operator account" mean **the account the agent runs as**.
the machine account of the identity the module runs for (ADR 0412). claude-code declares that it can run for That is the operator account, unless the node names an **agent account** of its own. The control node does.
an agent identity, in its assignment or when a session is started: started for `nox` it runs with the agent's
home; started for the operator, it is the default run, with the operator's configuration. On a
machine where no agent is marked as the agent account, its assignment is the default run, as every module's is.
**The fact.** The machine accounts come from the identity seat (ADR 0411); the operator no longer states the **The fact.** The operator states the agent account at the controller's terminal. No verb and no setting
agent account at the controller's terminal. A machine's agent account is the machine account of the agent the writes it. The controller resolves the machine facts `agent-account`, `agent-home` and `agent-root`, and gives
identity seat marks as the agent account there, on the operator's Approve: `nox` on the control node. There this module's the tool runner the account and its home. Where the node names none, the facts name the operator account and
assignment is run for that agent, and the controller refuses one that is not. The controller resolves the `agent-root` is empty, so nothing changes there.
machine facts `agent-account`, `agent-home` and `agent-root` from it, and gives the tool runner the account and
its home. Where no agent is marked (every other machine today), the facts name the operator account and `agent-root`
is empty, so nothing changes there, though a login `nox` exists for the modules run for it.
**What the module declares.** **What the module declares.**
- No login. The mesh makes the agent's machine account (home, own group, no password) from the identity, and the - A `user` named by `agent-account`, with `root` set from `agent-root`. Where the account is the agent's own,
node-engine judges on every look that it cannot become root without a person, by the ways ADR 0266 §12 to §17 list. the mesh creates it (home, own group, no password) and the node-engine judges on every look that it cannot
It detects those ways and no others; it prevents nothing. The `user` resource named by `agent-account`, become root without a person, by the ways ADR 0266 §3 lists. It detects those ways and no others; it
which this module declared until ADR 0411, goes. prevents nothing.
- `~/.claude` under that account's home, owned by it. - `~/.claude` under that account's home, owned by it.
- `claude-agent`, a command that starts the agent as that account through the operator's own sudo, or - `claude-agent`, a command that starts the agent as that account through the operator's own sudo, or
directly when already run as it. directly when already run as it.
@@ -1,16 +1,9 @@
--- ---
layer: to-be layer: to-be
status: in-progress status: in-progress
code: [mesh-controller, mesh-catalog modules/mesh-delivery, mesh-delivery, mesh-host, mesh-sdk] code: [mesh-controller, mesh-catalog modules/mesh-delivery]
updated: 2026-10-18 updated: 2026-10-16
decisions: decisions:
- "ADR 0429"
- "ADR 0428"
- "ADR 0426"
- "ADR 0421"
- "ADR 0420"
- "ADR 0419"
- "ADR 0418"
- "ADR 0408" - "ADR 0408"
- "ADR 0407" - "ADR 0407"
- "ADR 0406" - "ADR 0406"
@@ -37,6 +30,7 @@ decisions:
- "ADR 0270" - "ADR 0270"
- "ADR 0267" - "ADR 0267"
- "ADR 0250" - "ADR 0250"
- "ADR 0249"
- "ADR 0239" - "ADR 0239"
- "ADR 0238" - "ADR 0238"
- "ADR 0233" - "ADR 0233"
@@ -46,15 +40,14 @@ decisions:
# 47 — Delivery: from a commit to delivered # 47 — Delivery: from a commit to delivered
**A change reaches the machines in one of two ways, both owned by mesh-delivery: a hotfix (one pull request, **A change reaches the machines in one of three ways, all owned by mesh-delivery: a hotfix (one pull request,
delivered at its merge) or a feature (one branch name across its repositories: in two or more, one delivery delivered at its merge), a feature (one branch name across its repositories: in two or more, one delivery
group merged in one pass on the operator's one Approve and delivered by one delivery plan; in one, a lone group merged together and delivered member by member in its order; in one, a lone delivery), or an epic (a run of milestones, each delivered as a feature). The trunk is what runs; there
delivery). A design's milestones are features delivered one after another. The trunk is what runs; there is no is no "release branch"** (ADR 0401).
"release branch"** (ADR 0401, ADR 0418 §1, ADR 0421 §2).
**Every step starts on an event, and the state lives on the bus.** The forge's events, the controller's **Every step starts on an event, and the state lives on the bus.** The forge's events, the controller's
events and the nodes' reports move a delivery; the bus's own rules keep the queues, the merge window and the events and the nodes' reports move a delivery; the bus's own rules keep the queues, the merge window and the
order of a plan's tiers, which only the module graph gives (ADR 0419); and one chain, from module to commit to build to rollout to assignment, is kept in the bus's order; and one chain, from module to commit to build to rollout to assignment, is kept in the bus's
key-value store as its only source (ADR 0406, ADR 0407, ADR 0408). A running delivery never touches merging: key-value store as its only source (ADR 0406, ADR 0407, ADR 0408). A running delivery never touches merging:
every merge is recorded at once for the next delivery plan of what it touches. every merge is recorded at once for the next delivery plan of what it touches.
@@ -64,42 +57,6 @@ commit's journey, in what order the steps are allowed, and where each fact of it
2026-10-16 from the decisions of research 055, which replaced the "release branches", the toolchain image, the 2026-10-16 from the decisions of research 055, which replaced the "release branches", the toolchain image, the
controller's delivery tables and the timers that polled them. controller's delivery tables and the timers that polled them.
## Where it goes: parts that watch and write keys
ADR 0429 (accepted on 2026-10-18, from research 063) takes building and delivering out of the controller. They
become independent **parts**, each a process with its own bus credential that watches keys and writes keys:
no part calls another, there is no plan object and no orchestrator, and each key has one writer. A delivered
source revision is derived from the keys: every **want** equals its module's **fingerprint**, and every
**have** equals its want and is healthy.
- **The forge bridge** turns the forge's events into stream messages and is the only part that calls the
forge.
- **The indexer** computes each module's fingerprint at the merge preview and writes the candidates of each
group and the modules of the trunk.
- **The builder** claims a build by creating its key, once per fingerprint, and builds it; build-agent becomes
this part.
- **The checker** runs the merge check on built artifacts.
- **The gate** merges a group's pull requests once check, review and the operator's Approve agree on the same
heads, and then moves the mesh's source revision in one write.
- **The teller** carries conditions and asks to the operator's phone and is the only writer of an ask's answer.
- **The assigner** writes which fingerprint each node should run, by six rules: the build is done, the
fingerprint is not held, the node is the module's **canary** or the canary runs it healthy, at most `step`
other nodes are ahead when `step` is above zero, every provider runs healthy, and the module's credential for that fingerprint exists.
- **The node agent**, the node-engine's own process, applies its wants one module at a time, backs up a data
module first, and writes its haves.
- **The watchman** turns the bus's connections into presence and mirrors every key whose next move a machine
owes into a **pulse**, whose expiry is a stall.
- **The status view** is the operator's command `mesh-delivery-cli` and a web view; it reads every bucket and
writes only its own answers to asks.
Nine of these are modules of the repository `mesh-delivery`, prefixed `mesh-delivery-` (ADR 0429 §2); the
repository is made by the forge module from its config (ADR 0428; issue 1128 makes the config value reach the
node at once). The reviewer stays the review agent's session.
It lands in six releases, each leaving the mesh working, each one milestone below (ADR 0429 §7). Until the
release that replaces it is delivered, each section from here to *The bootstrap* stays this design and its rule
(ADR 0429 §21); each is rewritten when the release that replaces it is prepared (issue 1130).
## The parts ## The parts
``` ```
@@ -111,7 +68,7 @@ release that replaces it is delivered, each section from here to *The bootstrap*
(deliveries, groups, a key per │ (deliveries, groups, a key per │
pull request, the state table) ▼ pull request, the state table) ▼
node-engine on every node node-engine on every node
builders (build seat) ◄── work queues: delivery builds, approve-asked checks, other checks ───────────────── (reports; backups first) builders (build seat) ◄── work queues: delivery builds, ready checks, other checks ───────────────────────── (reports; backups first)
``` ```
- **mesh-delivery** (the seat `mesh-delivery`) owns hotfixes, features and milestones as deliveries and delivery - **mesh-delivery** (the seat `mesh-delivery`) owns hotfixes, features and milestones as deliveries and delivery
@@ -125,37 +82,34 @@ release that replaces it is delivered, each section from here to *The bootstrap*
the build seat's own account, and take their jobs from the bus's work queues, delivery builds first the build seat's own account, and take their jobs from the bus's work queues, delivery builds first
(ADR 0405, ADR 0406). (ADR 0405, ADR 0406).
- **The forge module** (`gitea`) publishes every forge event on the bus through the forge's system webhook - **The forge module** (`gitea`) publishes every forge event on the bus through the forge's system webhook
(issue 729): pull requests updated, synchronized, merged and closed, and commit statuses; no label or comment (issue 729): pull requests updated, synchronized, labeled, unlabeled, commented, merged and closed, and
moves a delivery, and mesh-delivery subscribes to none (ADR 0421 §11). It sets statuses, keeps the delivery's view as one comment on the pull request, and appends commit statuses. It sets statuses, keeps the delivery's view as one comment on the pull request, and appends
to the commit's note under `refs/notes/mesh-plan`. to the commit's note under `refs/notes/mesh-plan`.
- **Each node's backup seat** (`node-backup`) backs up the data a delivery plan moves there before the plan - **Each node's backup seat** (`node-backup`) backs up the data a delivery plan moves there before the plan
sends anything (ADR 0404). sends anything (ADR 0404).
## Two ways a change reaches the machines ## Three ways a change reaches the machines
*ADR 0401.* *ADR 0401.*
| Way | What it is | How it is delivered | | Way | What it is | How it is delivered |
|---|---|---| |---|---|---|
| **hotfix** | one pull request in one repository | at its merge, as a lone delivery; a lone merge is cut at once (ADR 0282 §2, as ADR 0304 §1 changed it) | | **hotfix** | one pull request in one repository | at its merge, as a lone delivery; a lone merge is cut at once (ADR 0282 §2, as ADR 0304 §1 changed it) |
| **feature** | one branch name across its repositories; in two or more, its pull requests are one delivery group; in one, a single pull request | a group is merged in one pass once its composed check passed at every member's head and the operator gave the group's one Approve (ADR 0418 §4, ADR 0421 §2); its merges are one batch and one delivery plan, each module built once and sent once (ADR 0418 §1); in one repository, as a lone delivery like a hotfix | | **feature** | one branch name across its repositories; in two or more, its pull requests are one delivery group; in one, a single pull request | a group is merged together once its composed check passes and every member is marked, reviewed and approved, then delivered member by member in its order (ADR 0239 §16); in one repository, as a lone delivery like a hotfix |
| **epic** | a run of milestones in one design (playbook 04) | each milestone is a feature, delivered when its own group is ready; the next starts when the previous one's delivery plan is done |
- **The trunk is what runs.** Every delivery is cut from a repository's trunk. A merge into any other branch - **The trunk is what runs.** Every delivery is cut from a repository's trunk. A merge into any other branch
starts no delivery, and the controller says so for that merge (issue 889). Holding merged work back, on a starts no delivery, and the controller says so for that merge (issue 889). Holding merged work back, on a
branch beside the trunk or per module, was dropped: from 2026-10-13 23:52 to 2026-10-16 09:32 feature 57's branch beside the trunk or per module, was dropped: from 2026-10-13 23:52 to 2026-10-16 09:32 feature 57's
branches held the controller for 55.7 h, and 28 of 68 other merges touched a held module (research 055, branches held the controller for 55.7 h, and 28 of 68 other merges touched a held module (research 055,
finding 3). finding 3).
- **A design's milestones are features, one after another.** Each milestone is delivered as a feature when its
own group is ready; the next starts when the previous one's delivery plan is done (playbook 04).
- **A milestone is functional, its delivery shaped technically.** A milestone is named by what the operator or - **A milestone is functional, its delivery shaped technically.** A milestone is named by what the operator or
the mesh can do once it is delivered; how it ships is shaped by the delivery plan its changes would have, asked of the mesh can do once it is delivered; how it ships is shaped by the delivery plan its changes would have, asked of
the controller's planner through mesh-delivery's `what-if` (ADR 0239 §3): the modules it moves and their fan-out. the controller's planner through mesh-delivery's `what-if` (ADR 0239 §3): the modules it moves and their fan-out.
- **What a merge waits for.** After its checks, a merge waits for one act of a person: the operator's Approve, - **What a merge waits for.** mesh-delivery merges a group's members once the group's composed check passed at
asked as soon as both required checks passed at the head (ADR 0421 §1, §3). For a group the Approve is asked their heads, the independent reviewer marked each member ready (ADR 0364 §1, §2), each member carries the
once, when every member's checks and the group's composed check passed, and covers every member's head review ADR 0382 §6 asks, and the operator approved each merge into main (ADR 0398 §3). The operator's Approve is
(ADR 0421 §2); it is the review ADR 0382 §6 asks (ADR 0421 §9). No ready mark, label, comment, forge review or asked for every merge into main, a hotfix's too (the operator, 2026-10-16 23:40).
recheck stands before it. The operator's Approve is asked for every merge into main, a hotfix's too
(ADR 0398 §3; the operator, 2026-10-16 23:40).
## A delivery ## A delivery
@@ -167,12 +121,8 @@ Its id is `<owner>/<repository>@<twelve characters of its commit>`. It holds:
its diffset (ADR 0238 §2, as ADR 0323 §1 changed it); its diffset (ADR 0238 §2, as ADR 0323 §1 changed it);
- its **state**, its **transitions** (the last hundred, each with when, the event, from, to and why), and its - its **state**, its **transitions** (the last hundred, each with when, the event, from, to and why), and its
**machine steps** while delivering; **machine steps** while delivering;
- its **Approve**: the ask, the operator's answer with its warrant, and the review record the pull request was
opened with (ADR 0421 §5, §8);
- once merged, **the commit it landed on the trunk as**, and the delivery plan that delivers it: the plan of the - once merged, **the commit it landed on the trunk as**, and the delivery plan that delivers it: the plan of the
batch that answers its merge, which may carry a later commit of its repository that contains it. Every member batch that answers its merge, which may carry a later commit of its repository that contains it.
of a delivery group is delivered by the same plan, and its delivery ends with that plan's result
(ADR 0418 §1).
**One key per pull request.** Beside the delivery, mesh-delivery keeps one key per open pull request on the **One key per pull request.** Beside the delivery, mesh-delivery keeps one key per open pull request on the
bus: its head, the trunk commit its current check is asked against, and its verdict, written by bus: its head, the trunk commit its current check is asked against, and its verdict, written by
@@ -191,7 +141,7 @@ allows.
|---|---|---|---| |---|---|---|---|
| proposed | a head was announced and is being checked | 1 hour from when its check is asked; none while its verdict is out of date and no check is asked | nothing: the check's own watchdog (S6) speaks | | proposed | a head was announced and is being checked | 1 hour from when its check is asked; none while its verdict is out of date and no check is asked | nothing: the check's own watchdog (S6) speaks |
| checked | a verdict arrived | 1 minute | nothing: it is decided at once | | checked | a verdict arrived | 1 minute | nothing: it is decided at once |
| ready | it passed; its Approve is asked (a group member's once the group is ready), and it waits for it and the merge | none | — | | ready | it passed; it waits to be merged | none | — |
| rejected | it failed its check | none | — | | rejected | it failed its check | none | — |
| published | merged on its trunk, recorded in a batch; it waits for its plan to start | 30 minutes | `superseded` when a plan that does not answer its merge took over its plan; `go` when its plan started | | published | merged on its trunk, recorded in a batch; it waits for its plan to start | 30 minutes | `superseded` when a plan that does not answer its merge took over its plan; `go` when its plan started |
| held | it waits for a person: a failed backup (ADR 0404 §4), a policy of record; it ends by itself when it moves nothing or a later merge covers it (ADR 0270) | 1 day | nothing: it is the operator's | | held | it waits for a person: a failed backup (ADR 0404 §4), a policy of record; it ends by itself when it moves nothing or a later merge covers it (ADR 0270) | 1 day | nothing: it is the operator's |
@@ -202,8 +152,7 @@ allows.
when the builder clones it, and the verdict names both. When another merge moves the trunk, a `ready` when the builder clones it, and the verdict names both. When another merge moves the trunk, a `ready`
delivery goes back to `proposed` on the event `trunk moved`, and mesh-delivery asks the check of every active, delivery goes back to `proposed` on the event `trunk moved`, and mesh-delivery asks the check of every active,
cleanly merging open pull request into that trunk at once (ADR 0395, *A move of the trunk asks every check cleanly merging open pull request into that trunk at once (ADR 0395, *A move of the trunk asks every check
again* below). An Approve already asked stays; the merge waits for the fresh check, and the Approve is not asked again* below). The row from `proposed` to `checked` takes only a verdict that names the trunk's head.
again for a trunk move (ADR 0421 §1, §3). The row from `proposed` to `checked` takes only a verdict that names the trunk's head.
**What needs no person ends by itself** (ADR 0270): a merge that moves nothing is `delivered` at its merge; a **What needs no person ends by itself** (ADR 0270): a merge that moves nothing is `delivered` at its merge; a
merge a later delivered merge contains is `covered`, by the forge's word that the later merge commit contains merge a later delivered merge contains is `covered`, by the forge's word that the later merge commit contains
@@ -221,29 +170,23 @@ nodes' reports, never inferred from time.
## Order without a person ## Order without a person
*ADR 0419, ADR 0418 §2.* *ADR 0239 §11 and §12, ADR 0249, ADR 0402.*
A delivery group has no order among its members, and nobody writes one (ADR 0419 §1). Inside a plan only the A delivery group's order is a table of pairs, each with the rule that won (ADR 0249 §3). The controller's
module graph orders: the planner orders the build and deploy tiers by edges between modules, never between `delivery-order` infers it from the manifests and the module graph; a person writes an `after: <repository>`
repositories or members (ADR 0418 §2, ADR 0419 §3). Modules with no edge between them share a tier. line only where the rules cannot see the order.
| Edge | A module goes before | Read from | | Rank (highest first) | Rule | Read from |
|---|---|---| |---|---|---|
| *built by* and *stands on* | the modules built by it or standing on it | the module graph | | 1 | *declared*: an `after:` line | the member's description |
| *SDK before its users* | every module built on the SDK, `mesh-sdk` | the module graph | | 2 | *built by* and *version skew* | the module graph and the change |
| *verb before caller* | a module whose change adds to its invoked verbs a verb this module's change adds to a seat's served verbs | the manifests | | 2 | *verb before caller*: a member adding a verb to a seat's served verbs before a member adding that verb to an invoked verb | the manifests (ADR 0402) |
| *version skew* | (the controller, when the plan moves it) every module whose manifest the plan changes | the change | | 2 | *SDK before its users*: a member changing `mesh-sdk` before every member whose modules are built on it | the module graph (ADR 0402) |
| 3 | *engine before controller* | module names |
| 4 | the repositories' names | — |
- **Engine before controller is the one default.** When a plan moves both the node-engine and the controller and Rules of one rank in opposite directions reject the group, naming both members, both rules and the line that
no edge orders them, the node-engine's tier comes first; an edge either way wins over it (ADR 0419 §4). would settle it (ADR 0249 §4).
- **Each tier says the edge that placed it.** The plan, `delivery-order`, `show` and the plan's note name, for
each module placed after another, the edge and its rule, and where the default was overruled, the edge that
overruled it (ADR 0419 §5).
- **A cycle is refused before anything merges.** Edges that form a cycle fail the group's composed check, or a
lone pull request's check, naming every module and edge in it. The way out is in the change itself (a verb
promised optional before it is required, ADR 0246), never an order typed by a person (ADR 0419 §6).
- **No `after:` line is read.** mesh-delivery reads no `after:` line, no refusal asks for one, and a description
that holds one is told once on its pull request that the line is not read (ADR 0419 §2).
**The SDK by version.** A Go module names the SDK by its released version and fetches it from the mesh's forge, **The SDK by version.** A Go module names the SDK by its released version and fetches it from the mesh's forge,
checked against its recorded checksum; vendored copies go. Inside a group, a member may name the SDK member's checked against its recorded checksum; vendored copies go. Inside a group, a member may name the SDK member's
@@ -258,10 +201,10 @@ commit on the SDK's trunk or the head of an SDK pull request the forge holds, op
*ADR 0323, ADR 0350, ADR 0395, ADR 0406.* *ADR 0323, ADR 0350, ADR 0395, ADR 0406.*
- **Three work queues, taken in order.** Delivery builds go on a work-queue stream of their own; checks of pull - **Three work queues, taken in order.** Delivery builds go on a work-queue stream of their own; checks of pull
requests whose Approve is asked or given on a second (ADR 0421 §10); every other check on a third. A builder takes from the first, then the requests marked ready on a second; every other check on a third. A builder takes from the first, then the
second, then the third. Each check stream has one subject per pull request and keeps only the newest request: second, then the third. Each check stream has one subject per pull request and keeps only the newest request:
a newer request replaces one still waiting, by the stream's rule. The fresh check of such a pull request, asked a newer request replaces one still waiting, by the stream's rule. A ready mark publishes its pull request's
on the merge path, is published on the second stream; a request left behind on the third for a head and trunk commit already check request on the ready stream; a request left behind on the third for a head and trunk commit already
judged or running is acknowledged and dropped when taken, by reading the pull request's key. Nothing pauses the judged or running is acknowledged and dropped when taken, by reading the pull request's key. Nothing pauses the
worker or deletes and republishes a request (issue 558). worker or deletes and republishes a request (issue 558).
- **A running check stops itself.** It watches its pull request's key and stops when the head or the trunk - **A running check stops itself.** It watches its pull request's key and stops when the head or the trunk
@@ -279,15 +222,15 @@ commit on the SDK's trunk or the head of an SDK pull request the forge holds, op
- A trunk move asks the check of every active open pull request into that trunk that merges cleanly, against the - A trunk move asks the check of every active open pull request into that trunk that merges cleanly, against the
new head; a conflict is told once on the pull request, not checked. new head; a conflict is told once on the pull request, not checked.
- A pull request with no push for 7 days and no Approve asked or given is left out; its statuses say out of date - A pull request with no push and no ready mark for 7 days is left out; a push, a ready mark or a recheck asks
and that a push or a recheck asks its check (ADR 0421 §10). its check.
- For a group, a move asks each member's own check, and the composed check runs again through those checks; the - For a group, a move asks each member's own check, and the composed check runs again through those rechecks; a
group's own merges, made in one pass on its Approve, ask its members nothing again (ADR 0418 §4). move made by a member merging in the group's order asks the other members nothing.
- Its cost is at most one check per active, cleanly merging pull request per trunk move; issue 944 measures it. - Its cost is at most one check per active, cleanly merging pull request per trunk move; issue 944 measures it.
### A delivery group's checks ### A delivery group's checks
*ADR 0239 §14, ADR 0323 §6, ADR 0382, ADR 0403, ADR 0418 §4, ADR 0421 §2, §9.* *ADR 0239 §14, ADR 0323 §6, ADR 0382, ADR 0403.*
- **The composed check is each member's gate.** The group's composed check composes every machine with all - **The composed check is each member's gate.** The group's composed check composes every machine with all
members' heads, each merged into its own trunk's head first, judged by the controller built from the group's members' heads, each merged into its own trunk's head first, judged by the controller built from the group's
@@ -299,39 +242,20 @@ commit on the SDK's trunk or the head of an SDK pull request the forge holds, op
heads exists. A member's new head, close or base move puts the composed verdict out of date, and with it every heads exists. A member's new head, close or base move puts the composed verdict out of date, and with it every
member's gate. When mesh-delivery cannot say the groups, the pull request is checked alone and its verdict member's gate. When mesh-delivery cannot say the groups, the pull request is checked alone and its verdict
says so (ADR 0382 §1). says so (ADR 0382 §1).
- **A group merges in one pass, with no order.** Once the composed check passed at every member's head and the - **A member merges only with its group passed and reviewed** (ADR 0382 §6), and in the group's order
operator's one Approve covers those heads, mesh-delivery merges every member in one pass, no member waiting for (ADR 0364 §8).
another's merge or delivery (ADR 0418 §4). The Approve is the review ADR 0382 §6 asks: the merge tool reads its
warrant for those heads in place of an approving forge review (ADR 0421 §9). A member the forge refuses is
merged again on its fresh check while the group's batch stays open for it.
## The Approve and the merge ## Ready marks and the merge
*ADR 0421, ADR 0398 §3, ADR 0406 §1.* *ADR 0364, ADR 0406 §1.*
- **Asked as soon as the checks pass.** When a pull request's delivery reaches `ready` (both `mesh/merge-gate` and The independent reviewer marks a pull request ready at the reviewed head with the label `mesh/ready` and a
`mesh/repo-check` passed at its head against its trunk's head), mesh-delivery asks the operator's Approve of its comment `ready at <full sha>`, through `mesh-delivery.mark-ready`. The forge's `labeled` and `commented` events
merge into main at once, on a channel that proves who answered (ADR 0274; Telegram today). The ask names the reach mesh-delivery in seconds; once both required checks passed against the trunk's head, it asks the operator's
pull request, its issues, the reviewer's verdict, the delivery plan and the trunk commit the checks judged. A Approve for a merge into main, with the pull request, its issues, the reviewer's verdict and the delivery plan, and
verdict that names a trunk commit the trunk had already left raises no ask: mesh-delivery asks that head's check merges the marked head through the forge's merge tool on that Approve (ADR 0398 §3). When the trunk moved, the merge tool asks the fresh check, and the event
against the trunk's head, and its passing verdict raises the ask (ADR 0421 §1). of its passing verdict asks the merge again; no timer asks it. A push clears the mark (the `synchronized`
- **A group is asked once.** One ask names every member and its head, once every member is `ready` and the event), and a failed check is said once and listed by `stalled`.
composed check passed at those heads; one Approve covers them all (ADR 0421 §2).
- **The reviewer's verdict is attached, not a gate.** The review finishes before the push (playbook 07), and the
session that opens a pull request gives its review record: who reviewed it, the commit the review approved and
the verdict. The ask carries it; with none, or one naming another commit, the ask says so in its first line and
the operator decides (ADR 0421 §5).
- **The merge.** mesh-delivery merges through the forge's merge tool at the heads the Approve names, only while
they are still the heads and both checks passed against the trunk's head. When the trunk moved, the merge tool
asks the fresh check, and the event of its passing verdict asks the merge again; no timer asks it, and no person
rechecks (ADR 0421 §3, §12).
- **A push or a Decline ends the Approve.** A new head ends its ask or its Approve, for a group every member's, and
is said once on the pull request; the new head is asked again once its checks pass. A Decline merges nothing and
is said once (ADR 0421 §6).
- **A failure is said, never merged past.** An approved head whose check failed is said once on the pull request
and listed by `stalled`; so is a merge the merge tool kept refusing for an hour (ADR 0421 §7).
- **Recorded.** The ask, the answer with its warrant and the review record are kept on the delivery, shown by
`show` and said in mesh-delivery's log; the merge's transition names the Approve (ADR 0421 §8).
## The merge window and the batch ## The merge window and the batch
@@ -345,19 +269,12 @@ commit on the SDK's trunk or the head of an SDK pull request the forge holds, op
refreshed by each merge into the batch, beside a key set once when the batch opens whose time to live is refreshed by each merge into the batch, beside a key set once when the batch opens whose time to live is
`merge-window-at-most` (10 minutes). The first to expire closes the batch, heard by a watcher; no cutter `merge-window-at-most` (10 minutes). The first to expire closes the batch, heard by a watcher; no cutter
polls. A stop that records merges again into the open batch closes its window too (ADR 0299 §3). polls. A stop that records merges again into the open batch closes its window too (ADR 0299 §3).
- **A group's merges are one batch.** A batch holding a merge of a delivery group with a member not yet merged
does not close when either key expires; once the group's last member merged, the window runs again from that
merge. A group whose last member has not merged ten minutes (`merge-window-at-most`) after its first raises a
condition naming the member and why, and nothing of the group is delivered while it waits (ADR 0418 §3).
- **A lone merge is cut at once**: no batch it shares a repository, module, graph edge or delivery group member - **A lone merge is cut at once**: no batch it shares a repository, module, graph edge or delivery group member
with is assembling (ADR 0282 §2, ADR 0304 §1, ADR 0306). with is assembling (ADR 0282 §2, ADR 0304 §1, ADR 0306).
- **The plan.** A closed batch becomes one delivery plan: every commit in it, tiered along the module graph - **The plan.** A closed batch becomes one delivery plan: every commit in it, tiered along the module graph
across repositories, at most one commit per repository, the latest of its trunk, each module it moves built once across repositories, at most one commit per repository, the latest of its trunk. It answers every merge in the
and sent once to each node that runs it. It answers every merge in the batch, its own commits and the earlier batch, its own commits and the earlier merges a later commit contains (ADR 0276 §2, as ADR 0299 §7 changed it).
merges a later commit contains (ADR 0276 §2, as ADR 0299 §7 changed it; ADR 0418 §1). No member of a group has - **A plan fixes its commits when it starts.** A plan starts at its first send, after mesh-delivery's word, and
a turn: only the module graph orders its tiers (ADR 0418 §2).
- **A plan fixes its commits when it starts.** A plan starts at its first send, after mesh-delivery's word, given once for a
group's plan when every member is merged (ADR 0418 §2), and
from then nothing that merges changes it. A plan still waiting for its word is folded into the next batch from then nothing that merges changes it. A plan still waiting for its word is folded into the next batch
(ADR 0276 §4). (ADR 0276 §4).
- **Plans that share something run one after another.** A batch that shares a repository, a module or a graph - **Plans that share something run one after another.** A batch that shares a repository, a module or a graph
@@ -365,9 +282,7 @@ commit on the SDK's trunk or the head of an SDK pull request the forge holds, op
that shares none runs beside the open ones, so a hotfix is never held by an unrelated feature's delivery. that shares none runs beside the open ones, so a hotfix is never held by an unrelated feature's delivery.
- **The ends.** A plan done ends every delivery it answers: `delivered` for its own commits, `covered` for an - **The ends.** A plan done ends every delivery it answers: `delivered` for its own commits, `covered` for an
earlier merge it carried by a later commit. A plan that failed fails its own commits' deliveries; each earlier earlier merge it carried by a later commit. A plan that failed fails its own commits' deliveries; each earlier
merge it answered is then delivered on its own commit, the newest first, until one is delivered (ADR 0276 §3). A merge it answered is then delivered on its own commit, the newest first, until one is delivered (ADR 0276 §3).
group's plan fails as one: no member is stopped because another failed, what earlier tiers delivered stays, and
the fix is a new change (ADR 0418 §5).
- **Shown.** `plans` lists the open batch first, with its window's time left and its merges, and a batch waiting - **Shown.** `plans` lists the open batch first, with its window's time left and its merges, and a batch waiting
behind an open plan names that plan; the controller says the batch as `plan-moved` in the state `assembling`. behind an open plan names that plan; the controller says the batch as `plan-moved` in the state `assembling`.
@@ -410,9 +325,9 @@ nothing, and the plan says so for that node.
One plan carries every merge of its batch, so a stop ends only the change it is for: One plan carries every merge of its batch, so a stop ends only the change it is for:
- **No stop by member.** A group's plan fails as one (ADR 0418 §5), so mesh-delivery asks no stop of a member - **A group's stop.** When a member after a failed one is stopped and its plan is open, mesh-delivery asks
after a failed one. The mechanism stands for a person's stop with the way `again`: the controller ends the plan `delivery-stop` for that plan with the stopped members' merges to leave out. The controller ends the plan and
and records every other merge it answered again in the open batch of its kind, whose window the stop closes. records every other merge it answered again in the open batch of its kind, whose window the stop closes.
- **What is left out with it.** A later merge of the stopped merge's repository, and a merge that moves a - **What is left out with it.** A later merge of the stopped merge's repository, and a merge that moves a
module a left-out merge moves, would deliver the stopped change; each is left out too, its delivery `stopped`, module a left-out merge moves, would deliver the stopped change; each is left out too, its delivery `stopped`,
naming why. naming why.
@@ -511,29 +426,27 @@ forge module's files of what it announced (ADR 0406 §6).
| Verb | Answers or does | | Verb | Answers or does |
|---|---| |---|---|
| `deliveries` | every delivery not final, the final ones of the last day, and every one `stalled` still lists | | `deliveries` | every delivery not final, the final ones of the last day, and every one `stalled` still lists |
| `show` | one delivery or group whole: plan, the edge that placed each tier, the Approve, transitions, machine steps, the chain's links | | `show` | one delivery or group whole: plan, transitions, machine steps, order, the chain's links |
| `groups` | every group with its members and its derived state | | `groups` | every group with its members in order and its derived state |
| `what-if` | the delivery plan a diffset would have, asked of the controller's planner | | `what-if` | the delivery plan a diffset would have, asked of the controller's planner |
| `table` | the state table and the machine-step table, with bounds and H2's repairs | | `table` | the state table and the machine-step table, with bounds and H2's repairs |
| `stalled` | every delivery held past its bound, with the transition H2 may take | | `stalled` | every delivery held past its bound, with the transition H2 may take |
| `recheck` | an exception verb, no step of the flow: a check asked again for a proposed delivery nothing else asks again, an inactive one or a failed verdict gone out of date (ADR 0395 §6, ADR 0421 §12) | | `mark-ready` | a reviewer's mark at a head: the label and the `ready at` comment, by the mesh's own forge account |
| `recheck` | a check asked again for a delivery whose verdict is out of date (ADR 0395 §6) |
| `release` | a held delivery goes on, with why; for a failed backup it asks the backups again | | `release` | a held delivery goes on, with why; for a failed backup it asks the backups again |
| `stop` | a delivery to stopped, with why; on a shared plan it first answers what it would take down (ADR 0299) | | `stop` | a delivery to stopped, with why; on a shared plan it first answers what it would take down (ADR 0299) |
| `close` | H2's only verb: the transition the table names for a stalled delivery | | `close` | H2's only verb: the transition the table names for a stalled delivery |
| `retire-history` | held history to retired (ADR 0250) | | `retire-history` | held history to retired (ADR 0250) |
| `times` | each class's budget, the week's median and worst delivery time, and the deliveries over budget | | `times` | each class's budget, the week's median and worst delivery time, and the deliveries over budget |
**The controller's verbs for it**: `delivery-plan`, `delivery-order` (the edges that order a plan's tiers, **The controller's verbs for it**: `delivery-plan`, `delivery-order`, `delivery-check`, `deliver`,
ADR 0419 §2), `delivery-check`, `deliver`,
`delivery-stop`, `delivery-walks`, and `plans` (the open batch, the open plans, and a what-if for a repository `delivery-stop`, `delivery-walks`, and `plans` (the open batch, the open plans, and a what-if for a repository
and its files). A person has `plans go <plan> --why`. and its files). A person has `plans go <plan> --why`.
## The bootstrap ## The bootstrap
- A plan that moves the controller, the node-engine, the tool runner, the bus or mesh-delivery is the - A plan that moves the controller, the node-engine, the tool runner, the bus or mesh-delivery is the
controller's own, started by the merge; mesh-delivery records it (ADR 0239 §31). Such a merge shares one batch controller's own, started by the merge; mesh-delivery records it (ADR 0239 §31).
and one plan with the other merges of its delivery group, and that plan is started by the controller when the
group's batch closes, not by its own merge (ADR 0418 §6).
- Any other plan waits for `deliver` while the seat has a holder on record; with none, the controller starts it. - Any other plan waits for `deliver` while the seat has a holder on record; with none, the controller starts it.
- A replaced controller or mesh-delivery carries on from the bus: the keys, the queues and the durable - A replaced controller or mesh-delivery carries on from the bus: the keys, the queues and the durable
consumers hold every step, so nothing is lost or repeated (ADR 0406 §6). consumers hold every step, so nothing is lost or repeated (ADR 0406 §6).
@@ -543,17 +456,14 @@ and its files). A person has `plans go <plan> --why`.
What the build proves before this design is implemented (research 055's direction): What the build proves before this design is implemented (research 055's direction):
1. A feature across the controller and the catalogue with a new seat verb is delivered as one group, with no 1. A feature across the controller and the catalogue with a new seat verb is delivered as one group, with no
`after:` line, one composed check, one Approve and one delivery plan: its merges are one batch, each module is `after:` line, no two steps and one composed check.
built once and sent once, its commits are carried by that one plan, and the plan names the edge that placed
each tier.
2. An SDK change and a Go module using it are delivered as one group. 2. An SDK change and a Go module using it are delivered as one group.
3. A data module is backed up before a delivery moves it, and a failed backup holds the delivery. 3. A data module is backed up before a delivery moves it, and a failed backup holds the delivery.
4. A build on any builder uses the installed toolchain with no container, and a toolchain update rebuilds only 4. A build on any builder uses the installed toolchain with no container, and a toolchain update rebuilds only
modules of that language when its version changes. modules of that language when its version changes.
5. Two merges into one repository within a minute leave each open pull request with one queued check, by the 5. Two merges into one repository within a minute leave each open pull request with one queued check, by the
stream's rule. stream's rule.
6. The operator's Approve is asked within seconds of the event of a pull request's checks passing, with no ready 6. A ready mark is acted on within seconds of its event.
mark, label or comment written by anyone.
7. A controller replaced in the middle of a rollout resumes it with no step lost or repeated. 7. A controller replaced in the middle of a rollout resumes it with no step lost or repeated.
8. The merge window closes by its key's expiry. 8. The merge window closes by its key's expiry.
9. A hotfix to a module the running feature's delivery does not touch reaches the machines in minutes, beside it. 9. A hotfix to a module the running feature's delivery does not touch reaches the machines in minutes, beside it.
@@ -563,25 +473,6 @@ What the build proves before this design is implemented (research 055's directio
with no person's release and nothing held in mesh-delivery. with no person's release and nothing held in mesh-delivery.
12. No table in the controller's store holds builds, rollouts or assignments. 12. No table in the controller's store holds builds, rollouts or assignments.
From ADR 0429 (research 063's done-when, and the live proof each of its releases names):
13. The status view shows every module on every node from the have keys, and agrees with the controller's own
read of the nodes' reports.
14. A part stopped by hand shows stuck within two minutes, and no part holds a timer.
15. A group of two repositories merges as one source revision, and every module's fingerprint the indexer
writes equals the controller's source fingerprint.
16. A merge builds nothing its check built, and a hotfix's build waits for no other build.
17. A day of deliveries shows no difference between the assigner's wants and what the controller sent that the
six rules do not explain.
18. On the live mesh a milestone of three repositories reaches every node through the keys alone (research 063's
scenario A), and the assigner replaced in the middle of it resumes with nothing lost or repeated (scenario
D, once).
19. Plans, batches, the merge window, tiers, the plans hold and node declaration holds, the push of whole
declarations, their retries, takeovers and healers, and the node-engine's handling of whole declarations are
deleted from the code, and no verb that starts, stops, retries or closes a plan remains.
20. Each release's pull request reports the controller's code lines against research 063's baseline (111,386
lines on 2026-10-18).
## What retires ## What retires
- The "release branches" and the controller's judge of them (ADR 0358, superseded by ADR 0401). - The "release branches" and the controller's judge of them (ADR 0358, superseded by ADR 0401).
@@ -589,48 +480,24 @@ From ADR 0429 (research 063's done-when, and the live proof each of its releases
- The window cutter, the merge catch-up timer, the rollout ticker, mesh-delivery's tick, reconcile and - The window cutter, the merge catch-up timer, the rollout ticker, mesh-delivery's tick, reconcile and
ready-mark poll, the forge's poll as a trigger, the queue reordering, the forge-asking cancel, and the forge ready-mark poll, the forge's poll as a trigger, the queue reordering, the forge-asking cancel, and the forge
module's announcement files (ADR 0406, ADR 0407). module's announcement files (ADR 0406, ADR 0407).
- The ready mark: the label `mesh/ready`, the `ready at` comment, `mark-ready`, the forge module's
`ready-authors` setting, and mesh-delivery's subscriptions to labeled, unlabeled and commented events
(ADR 0421 §4, §11); a recheck by hand as a step of the flow (ADR 0421 §12).
- A group's order: `after:` lines, the ranked rules and the order of the repositories' names (ADR 0419, which
supersedes ADR 0249).
- Delivery member by member, a stop of a member after a failed one, and the own path's separate batch for a
group's merges (ADR 0418 §1, §5, §6).
- The controller's tables of builds, build requests, rollouts and their tiers, batches and merges, and - The controller's tables of builds, build requests, rollouts and their tiers, batches and merges, and
assignments; the record mirrors, probe D17's audit and healer H6 for those kinds (ADR 0408). assignments; the record mirrors, probe D17's audit and healer H6 for those kinds (ADR 0408).
## Milestones ## Milestones
Milestones 1 to 6 were confirmed by the operator on 2026-10-17 (playbook 04), and 5b on 2026-10-18. Releases 1 Confirmed by the operator on 2026-10-17 (playbook 04). Each is one feature step of playbook 07; the next starts
to 6 of ADR 0429 follow milestone 5b in its order, releases 1 and 2 as one milestone (ADR 0429 §7 allows it: when the previous one's delivery is done. Milestones 5 and 6 are split into steps when they come up.
they need nothing of each other), and milestone 6 folded into release 4: confirmed by the operator on 2026-10-18
(playbook 04 step 5). Release 1-2's issues are opened; each later release's are opened when it is prepared. Each milestone is one release of playbook 07; the next starts when the
previous one's delivery is done, and a release's clauses of ADR 0429 hold from its delivery (§21). Every
release reports the controller's code lines against research 063's baseline (acceptance test 20).
| # | Outcome | Acceptance tests | Repositories | Issues | | # | Outcome | Acceptance tests | Repositories | Issues |
|---|---|---|---|---| |---|---|---|---|---|
| 1 | A merge during a running delivery is never dropped and needs no person's release; the merge window lives on the bus; a rollout that shares nothing with an open one runs beside it | 8, 9, 11 | mesh-controller | 973 (with the rest of 525) | | 1 | A merge during a running delivery is never dropped and needs no person's release; the merge window lives on the bus; a rollout that shares nothing with an open one runs beside it | 8, 9, 11 | mesh-controller | 973 (with the rest of 525) |
| 2 | A ready mark is acted on within seconds, and each pull request has at most one waiting check | 5, 6, 7 | mesh-controller, mesh-catalog (mesh-delivery, gitea) | 974, 975, 976 | | 2 | A ready mark is acted on within seconds, and each pull request has at most one waiting check | 5, 6, 7 | mesh-controller, mesh-catalog (mesh-delivery, gitea) | 974, 975, 976 |
| 2b | build-agent and route-proxy live whole in mesh-controller (ADR 0420, accepted 2026-10-17): the controller accepts a reviewed move, mesh-delivery announces each merge with its Approve, and the two modules move; built as two releases before the rule that a milestone is one release | — | mesh-controller, mesh-catalog (mesh-delivery, build-agent, route-proxy) | release 1: 1025; release 2: 1019 | | 3 | A feature with a new seat verb across the controller and the catalogue is delivered as one group: no `after:` line, one composed check, no "release branches" | 1 | mesh-controller, mesh-catalog (mesh-delivery) | 977, 978 |
| 3 | A feature with a new seat verb across the controller and the catalogue is one group, one batch, one delivery plan and one Approve: no order lines, no ready mark, each module built and sent once | 1, 6 | mesh-controller, mesh-catalog (mesh-delivery, gitea) | 977, 978, 1022, 895 (with 1046 folded into it: unit and integration tests split, each level within its bound); 605 (1021 closed as its duplicate) is resolved by this build (ADR 0419 §7) | | 4 | A delivery backs up the data it moves first, and a failed backup holds it and is said | 3 | mesh-controller, mesh-catalog (mesh-delivery, the backup seat's holder) | 980 |
| 4 | A delivery backs up the data it moves first, and a failed backup holds it and is said | 3 | mesh-controller, mesh-catalog (mesh-delivery, the backup seat's holder) | 980, split into 1065, 1066, 1067, 1068 |
| 3 fix | A group approved together merges in one pass and goes out in one delivery plan, also when its members' tests read each other's repositories | 1 | mesh-catalog (mesh-delivery, gitea) | 1082, 1049, 1047, 1083, 1079 (1084 closed as its duplicate) |
| 5 | One read of a module on a node names the commit, build and rollout that put it there | 10, 12 | mesh-controller, mesh-catalog (mesh-config, nats) | 981 | | 5 | One read of a module on a node names the commit, build and rollout that put it there | 10, 12 | mesh-controller, mesh-catalog (mesh-config, nats) | 981 |
| 5b | Decided: building and delivering leave the controller for parts that watch and write keys in the repository `mesh-delivery`, landing in six releases (ADR 0429); the forge module makes that repository from its config, protected before any team reaches it (ADR 0428) | — (ADR 0429 names them: 13 to 20) | hq, mesh-catalog (gitea) | 1117, 1124, 1128 | | 6 | Builds use installed toolchains with no container, builders fetch over SSH with their own key, and an SDK change ships in one group with its users | 2, 4 | mesh-catalog (build modules, build-agent, ssh-client, gitea), mesh-controller, mesh-sdk | 979, 971 |
| 6 | Folded into release 4 by ADR 0429 §10: installed toolchains, builders fetching over SSH with their own key, and an SDK change shipping with its users are built in the builder's new home, so the builder is changed once | (2, 4: proved by release 4) | — | 979, 971 (moved to release 4) |
| R1–2 | The operator sees every module on every node, with its fingerprint, phase and health, in `mesh-delivery-cli` and its web view, and a part that stops moving is raised as a condition within two minutes without a timer | 13, 14 | mesh-delivery (view, watchman), the node-engine (`mesh-host`: have keys; its alive message deleted), mesh-sdk (key layouts), mesh-controller (its silence timers deleted), mesh-catalog (zsh's `nox-delivery`) | 1131 (key layouts), 1132 (have keys), 1136 (credentials), 1134 (the view), 1135 (the watchman), 1137 (the alias) |
| R3 | A group reaches the mesh as one source revision written by the gate, the operator's asks come through the teller and can be answered in the view, but for the merge's Approve, answered on the phone until its way in proves who answered (issue 953), and today's module mesh-delivery is split into its parts and ends | 15 | mesh-delivery (forge bridge, gate, teller, indexer), mesh-catalog (mesh-delivery split), mesh-sdk | opened when it is prepared |
| R4 | The controller asks no build: a build is keyed by its fingerprint, so a merge builds nothing its check built; with milestone 6, toolchains are installed, builders fetch over SSH, and an SDK change ships with its users | 16, 2, 4 | mesh-delivery (builder, checker), mesh-controller (build dispatch deleted; build-agent leaves), mesh-catalog (build modules, ssh-client, gitea), mesh-sdk | 979, 971; the rest opened when it is prepared |
| R5 | The assigner writes what each node should run by its six rules, compared with what the controller sends, and the view shows every difference | 17 | mesh-delivery (assigner), mesh-controller (grants keys) | opened when it is prepared |
| R6 | Each node applies from its wants, a new version reaches the module's canary first and the rest once it is healthy there, and the plans are deleted from the code | 18, 19 | the node-engine (`mesh-host`: the node agent), mesh-controller (plans deleted), mesh-delivery (the gate's delivery states go) | opened when it is prepared |
The issues are linked `depends-on` in this order; 976 depends on 975, and 979 also on 971. In release 1-2, 1132 and 1136 depend on 1131, 1134 and 1135 on 1136, 1134 also on 1132 and 1128, and 1137 on 1134. Milestone 3's first live group (milestone 4's, on 2026-10-17) held one Approve but merged in two passes and went out in two delivery plans: a member's tests read the other member's repository, so the first merge made the second's verdict out of date, and a closed group never asked its composed verdict again (issues 1049, 1082). The operator put a fix of milestone 3 after milestone 4 and before milestone 5 on 2026-10-18; acceptance test 1 is proved again by it. The operator put milestone 5b after milestone 5 and before milestone 6 on 2026-10-18, as an issue rather than a research effort; where the planning goes is settled by a decision record before it is built (issue 1117), and milestone 6's build changes are read against it. Milestone 2b was The issues are linked `depends-on` in this order; 976 depends on 975, and 979 also on 971.
put before milestone 3 by the operator on 2026-10-17. Milestone 3 builds ADR 0418, ADR 0419 and ADR 0421
(issues 977, 978 and 1022), and splits mesh-controller's unit tests from its integration tests (issue 895, the
operator on 2026-10-17), so the unit level holds its bound of ADR 0391; the integration level's sixty seconds
is issue 1055. Acceptance test 6 spoke of a ready mark when milestone 2 was delivered; restated for
the Approve, it is proved again by milestone 3.
## What is not decided here ## What is not decided here
+5 -6
View File
@@ -2,9 +2,8 @@
layer: to-be layer: to-be
status: in-progress status: in-progress
code: [hq 00-META/checks/words.py, mesh-catalog merge-check.sh] code: [hq 00-META/checks/words.py, mesh-catalog merge-check.sh]
updated: 2026-10-18 updated: 2026-10-11
decisions: decisions:
- "ADR 0429"
- "ADR 0244" - "ADR 0244"
- "ADR 0006" - "ADR 0006"
- "ADR 0008" - "ADR 0008"
@@ -32,7 +31,7 @@ shape — what each is for, where its records live, and how they depend on one a
| **Placement** | to decide, send and apply what each node runs, and say why | the controller's `inventory` database | | **Placement** | to decide, send and apply what each node runs, and say why | the controller's `inventory` database |
| **Provisioning** | to resolve which module serves a provision for which consumer, and wire the two | the controller | | **Provisioning** | to resolve which module serves a provision for which consumer, and wire the two | the controller |
| **Identity and access** | to issue, hold, rotate and check credentials and grants | the controller's `identity` and `licences` databases, and the vault | | **Identity and access** | to issue, hold, rotate and check credentials and grants | the controller's `identity` and `licences` databases, and the vault |
| **Delivery** | to take one commit from its pull request to every node that should run it, and put it back when it fails | the `mesh-delivery` module and the controller's planner and rollout today; from each release of ADR 0429 on, the parts of the repository `mesh-delivery` it builds | | **Change and delivery** | to take one commit from its pull request to every node that should run it, and put it back when it fails | the `mesh-delivery` module, and the controller's planner and rollout |
| **Health and repair** | to notice what is wrong, say it once, repair what may be repaired unattended, and record what was done by hand | the controller's condition store | | **Health and repair** | to notice what is wrong, say it once, repair what may be repaired unattended, and record what was done by hand | the controller's condition store |
| **Data** | to know every item of data a module holds, how precious it is, and keep it recoverable | the manifests' data declarations, and every node's `node-backup` seat | | **Data** | to know every item of data a module holds, how precious it is, and keep it recoverable | the manifests' data declarations, and every node's `node-backup` seat |
| **Connectivity** | to make every node and module reachable by name where it should be, and unreachable where it should not | the controller | | **Connectivity** | to make every node and module reachable by name where it should be, and unreachable where it should not | the controller |
@@ -62,7 +61,7 @@ a change of meaning upstream ripples down, never up. Arrows point downstream.
+-------------> Health and repair <-------------------+ +-------------> Health and repair <-------------------+
| | | |
v v v v
Delivery Operator and conversation Change and delivery Operator and conversation
| ^ | ^
+-------------------+ +-------------------+
(a held delivery waits for a person's word) (a held delivery waits for a person's word)
@@ -71,7 +70,7 @@ a change of meaning upstream ripples down, never up. Arrows point downstream.
Media stands beside the diagram, below Module alone: its words are about what a person watches, not Media stands beside the diagram, below Module alone: its words are about what a person watches, not
about the mesh's machinery, and no other domain reads them. about the mesh's machinery, and no other domain reads them.
Delivery sits low because it reads health and data; in time it runs first, and asks Placement Change and delivery sits low because it reads health and data; in time it runs first, and asks Placement
to send. An authorising answer acts in whichever domain asked, through that domain's own verb: the to send. An authorising answer acts in whichever domain asked, through that domain's own verb: the
conversation owns the asking, never the act. conversation owns the asking, never the act.
@@ -85,7 +84,7 @@ ADR 0006 named seven contexts of the controller. Each is a domain now, under the
| config | split: settings to Module and Placement, secrets to Identity and access | | config | split: settings to Module and Placement, secrets to Identity and access |
| connectivity | Connectivity | | connectivity | Connectivity |
| provisioning | Provisioning | | provisioning | Provisioning |
| delivery | Delivery — owned by the `mesh-delivery` module, not by the controller (ADR 0239); named so by ADR 0429 §2, its parts living in the repository `mesh-delivery` | | delivery | Change and delivery — owned by the `mesh-delivery` module, not by the controller (ADR 0239) |
| observability | Health and repair: the mesh built conditions, not "alerts", and half of the domain is repair | | observability | Health and repair: the mesh built conditions, not "alerts", and half of the domain is repair |
| identity | Identity and access, with the licences store the seven did not name | | identity | Identity and access, with the licences store the seven did not name |
@@ -1,8 +1,8 @@
--- ---
layer: to-be layer: to-be
status: in-progress status: designed
code: [mesh-controller] code: []
updated: 2026-10-17 updated: 2026-10-10
decisions: decisions:
- "ADR 0289" - "ADR 0289"
- "ADR 0201" - "ADR 0201"
@@ -12,8 +12,6 @@ decisions:
# 54 — The controller's records on the bus # 54 — The controller's records on the bus
**Where it stands, 2026-10-17** (the records cleanup, issue 1072; playbook 07, "The design's state is written back"). Built on main: mesh-controller's record mirrors (internal/recordmirror, cmd/mesh-controller/record_mirrors.go, internal/inventory's record mirrors), the mechanism specified here. Not yet checked mirror by mirror; not broken down into milestones.
**What each node runs, how far each rollout has got, what each build is doing and what each setting holds **What each node runs, how far each rollout has got, what each build is doing and what each setting holds
can be watched on the bus, and what is watched never says something the controller's store does not** can be watched on the bus, and what is watched never says something the controller's store does not**
(ADR 0289, (ADR 0289,
+10 -118
View File
@@ -1,19 +1,15 @@
--- ---
layer: to-be layer: to-be
status: in-progress status: designed
code: [mesh-catalog, hq] code: []
updated: 2026-10-18 updated: 2026-10-12
decisions: decisions:
- "ADR 0290" - "ADR 0290"
- "ADR 0321" - "ADR 0321"
- "ADR 0423"
- "ADR 0424"
--- ---
# 55 — The knowledge base # 55 — The knowledge base
**Where it stands, 2026-10-17** (the records cleanup, issue 1072; playbook 07, "The design's state is written back"). Built and live since 2026-10-11: the knowledge base is the module mesh-knowledge in mesh-catalog, holding every decision record and research effort (issues 425 and 426, resolved and delivered). Left: the later slices, issues 428 and 429.
**The mesh keeps its decision records and research efforts whole in a module of its own, `mesh-knowledge`, one **The mesh keeps its decision records and research efforts whole in a module of its own, `mesh-knowledge`, one
instance on the control-node, holding the seat `knowledge-base`: each record's number, status, topic, typed links, instance on the control-node, holding the seat `knowledge-base`: each record's number, status, topic, typed links,
its clauses with their own status, and its reasoning, with every version kept. A record is proposed, checked when it its clauses with their own status, and its reasoning, with every version kept. A record is proposed, checked when it
@@ -30,8 +26,7 @@ gets, in one call under 4 KiB, the clauses in force, the records they come from,
read the reasoning behind each. read the reasoning behind each.
This document designs the first slice in detail, tracked by issue 425. The slices after it are in This document designs the first slice in detail, tracked by issue 425. The slices after it are in
[§ 9](#9-the-slices-after-the-first), which also designs the third: designs and the glossary's entries become records, [§ 9](#9-the-slices-after-the-first).
and the knowledge base works out each design's state (ADR 0423, ADR 0424).
## 1. The parts ## 1. The parts
@@ -44,12 +39,9 @@ and the knowledge base works out each design's state (ADR 0423, ADR 0424).
├ verbs ask, get, holds, links, history, reserve, propose, import, return ├ verbs ask, get, holds, links, history, reserve, propose, import, return
│ served by the control-node's tool runner │ served by the control-node's tool runner
├ events proposed, reviewed, accepted, changed, imported, drafted, confirmed, returned ├ events proposed, reviewed, accepted, changed, imported, drafted, confirmed, returned
├ consumes the router's warrants: the operator's Approve for a record that needs it (§ 5) └ consumes the router's warrants: the operator's Approve for a record that needs it (§ 5)
└ consumes the delivery module's delivery-done announcement: a design's state (§ 9)
mesh-issues (control-node) the counters for decision and research numbers (§ 6); mesh-issues (control-node) the counters for decision and research numbers (§ 6)
the issues' design links, read for a design's state (§ 9)
mesh-delivery (its seat) announces each delivery done, on the bus (§ 9)
claude-code (same node) claude_code_judge: review a proposal (§ 5), draft and confirm clauses (§ 7) claude-code (same node) claude_code_judge: review a proposal (§ 5), draft and confirm clauses (§ 7)
records (any node) records_read at a commit: the import's source (§ 8) records (any node) records_read at a commit: the import's source (§ 8)
messenger (control-node) the router: the operator's ask and its warrant messenger (control-node) the router: the operator's ask and its warrant
@@ -63,11 +55,9 @@ the records module, as the issue tracker's import does.
## 2. What one record holds ## 2. What one record holds
- `number` and `kind` (decision or research; with the third slice also design and glossary entry, § 9), its key - `number` and `kind` (decision or research), its key together; `title`, `topic`, `status`, `date`, `deciders`;
together; `title`, `topic`, `status`, `date`, `deciders`;
- its **reasoning**, in sections: for a decision, context, the options weighed, consequences, how it is checked, - its **reasoning**, in sections: for a decision, context, the options weighed, consequences, how it is checked,
references; for a research effort, its overview and each of its documents by name; for a design, its `to-be` references; for a research effort, its overview and each of its documents by name; markdown, as written;
section and, where something of it runs, its `as-is` section (§ 9); markdown, as written;
- for a decision, its **clauses** (§ 3); - for a decision, its **clauses** (§ 3);
- its **links**, typed, each stored once and read from both ends; - its **links**, typed, each stored once and read from both ends;
- its **versions**: every change is a new version with who made it, when, and why in one line; `history` lists them - its **versions**: every change is a new version with who made it, when, and why in one line; `history` lists them
@@ -200,89 +190,11 @@ field by field and section by section, and the result is written on issue 426.
|---|---|---|---| |---|---|---|---|
| 1 | this document: the module, the import, clauses, answers, `propose`, its own review and acceptance | the acceptance sentence above, live, and a record proposed, reviewed and accepted without a pull request | issue 425 | | 1 | this document: the module, the import, clauses, answers, `propose`, its own review and acceptance | the acceptance sentence above, live, and a record proposed, reviewed and accepted without a pull request | issue 425 |
| 2 | the decision records and research efforts leave this repository; path mentions become "ADR NNNN" and "research NNN"; playbooks 01 and 02 changed | this repository holds no decision record, and `get` answers every one | issue 426 | | 2 | the decision records and research efforts leave this repository; path mentions become "ADR NNNN" and "research NNN"; playbooks 01 and 02 changed | this repository holds no decision record, and `get` answers every one | issue 426 |
| 3 | designs and the glossary's entries as records, moved in one delivery, and each design's state worked out by the knowledge base (§ 9.1 to § 9.3; ADR 0423, ADR 0424) | `get` answers design 47 with its sections and links, a word's entry answers from the knowledge base, and a design's state follows its issues' deliveries without anyone writing it | issue 428 | | 3 | designs, playbooks and glossary entries as kinds | `holds` answers for a design's decisions and a glossary word | issue 428 |
| 4 | meeting notes, drawings and blog posts | not decided; needs its own decision | issue 429 | | 4 | meeting notes, drawings and blog posts | not decided; needs its own decision | issue 429 |
Numbers from the issue tracker, which `reserve` needs, are the tracker's own slice, tracked by issue 424. This Numbers from the issue tracker, which `reserve` needs, are the tracker's own slice, tracked by issue 424. This
repository keeps no copy of the records, generated or otherwise (issue 435, answered by the operator). Playbooks repository keeps no copy of the records, generated or otherwise (issue 435, answered by the operator).
stay in this repository as files: they are how work is done here, not records of what was decided (research 061).
### 9.1 Designs and glossary entries as records
**A design** is a record of the kind `design`, named "design NN" and keeping its number (ADR 0423). Its text is its
sections: the `to-be` section, what is being built, and the `as-is` section, what runs. Each of the as-is documents
becomes the as-is section of the design it describes, never a record of its own: the import matches 3 of the 18 by
name and 15 by topic, and shows the operator the list before it runs. Its owning repositories are its field `code`;
its decisions, and every design it names, are `cites` links. It holds no state written by anyone (§ 9.2). A new
design takes its number from `reserve` and is written with `propose`, and is reviewed as every record is (§ 5).
**A glossary entry** is a record of the kind `glossary entry`, one per entry of the glossary page, kept whole: its
head word or words (an entry may name several with one meaning, as research effort, graduation, hand-off and playbook
do), its domain, its meaning, its *Not:* line with each retired word's scope, and its identifier line. Each row of
the page's homonym table becomes an entry of its own, naming the qualified form each domain uses. The entries replace
the page as the authority on words (ADR 0423, changing ADR 0244).
**The readers change with them.** Everything that read the page or the design folders reads the records instead:
| Reader | Reads, after the move |
|---|---|
| the knowledge base's own rule on a retired word (§ 4) | the *Not:* and identifier lines of the entries, no longer the page through the records module; it judges a design when it is proposed, as it judges a decision |
| hq's word check | the entries' *Not:* and identifier lines, and its uniqueness check (a head word heading two entries, or also retired) over the entries; it covers what stays in this repository, and designs leave its coverage |
| the catalogue's repository check | the retired words of no scope or *(tools)* from the entries; its copied list is retired, so hq has no copy to compare |
| hq's cycle check | the design records, for every design naming its decisions, not files under `03-DESIGN/` |
Each of the four fails, saying so, when the knowledge base does not answer, and never passes on nothing.
**Links become links between records, and the old paths go.** At the import every file link inside a design becomes
a link between records, which the rule on a link to nothing then checks (§ 4). The links from documents that stay in
this repository are rewritten to name the design ("design 47"). No page stays at an old path: afterwards this
repository holds no file under `03-DESIGN/` and no `00-META/glossary.md`.
**It moves in one delivery**, as the records did: the import, the two kinds, the four readers, the rewritten links,
the playbooks' steps that write a design, and the instruction text sessions read, which names the glossary's place.
A reader left behind would pass on nothing, so none moves alone.
### 9.2 A design's state
**The knowledge base says a design's state, and nobody writes it** (ADR 0424): proposed, designed, in progress or
implemented, kept matching what runs. No person, session or document sets it, and `propose` refuses a design that
carries one.
It reads two things. The issues are those the issue tracker holds with a `design` link to the design. Whether one
is done comes from its deliveries: an issue is done when it is resolved and the delivery its fix went out in reached
`delivered`, whether a hotfix alone or a delivery group. The delivery module announces each delivery done on the
bus, and on each announcement the knowledge base works the state of every design that issue links out again.
```
issue tracker ── issues with a design link ──┐
├──▶ knowledge base ── works out ──▶ the design's state
mesh-delivery ── "delivery done", bus ───────┘ (served by get, ask)
```
**The rules.**
| Case | State |
|---|---|
| a decision the design names is not accepted | proposed |
| no linked issue done, or no linked issue at all | designed |
| some linked issues done | in progress |
| every linked issue done | implemented |
Three kinds of issue count apart: an issue closed won't fix is left out; an issue resolved with nothing to deliver,
a fix of documents only, is done when resolved; and a new issue linked to an implemented design makes it in progress
again, until that issue is done. Milestones are not read: a milestone is a group of a design's issues delivered
together, and the state depends only on the issues and their deliveries.
**Imported designs say what they lack.** A design imported with no linked issue shows `no issues linked yet` beside
designed, until an issue is linked to it; the import names each such design by number. Linking their issues is
tracked by issue 428.
### 9.3 Its place beside design 47
The delivery-done announcement this slice reads is design 47's
([47 — Delivery](47-delivery-from-commit-to-delivered.md)): its hotfixes and delivery groups, and each delivery's
end at `delivered`. This slice is an extension after design 47's last milestone. It changes none of design 47's
milestones and nothing of how a delivery runs; it only listens for the announcement.
## 10. How it is checked ## 10. How it is checked
@@ -300,26 +212,6 @@ milestones and nothing of how a delivery runs; it only listens for the announcem
| A record written in this repository meanwhile writes its clauses | `records.py`, check `clauses`, on every hq pull request, until the records left this repository (issue 426); since then `records.py`, check `no-records`, refuses any record written here | | A record written in this repository meanwhile writes its clauses | `records.py`, check `clauses`, on every hq pull request, until the records left this repository (issue 426); since then `records.py`, check `no-records`, refuses any record written here |
| Live | the acceptance sentence, asked from a session outside this repository | | Live | the acceptance sentence, asked from a session outside this repository |
**The third slice's acceptance tests** (§ 9.1 to § 9.3; research 062's direction), what its build proves before it
is done:
1. After the import, `knowledge-base.get` of design 47 returns its sections and its links, and every link from a
design resolves to a record: none is refused by the rule on a link to nothing.
2. This repository's main holds no file under `03-DESIGN/` and no `00-META/glossary.md`, and its link check passes
with the 76 links from documents that stay here rewritten.
3. hq's cycle check and word check read the knowledge base, and fail, saying so, when it does not answer.
4. A proposal holding a retired word is refused after the move, the word read from the entries; the entries are
175 and hold 183 head words and 29 retired words, 0 unparsed, both counts checked.
5. In the module's tests, each state rule of § 9.2 gives its state from fixture issues; live, an issue linked to a
design whose hotfix is delivered turns the design implemented within a minute of the delivery being done, and
a new issue linked to it turns it in progress.
6. The import reports every design with no linked issue as `no issues linked yet`, by number.
7. The instruction text sessions read names the glossary's new place.
8. Every entry of the glossary's Config section is filed under the Config domain, and the domains the entries name
are the twelve of ADR 0354.
9. Tests 1 to 8 pass together after the one delivery, run against main and the live knowledge base, with nothing
in this repository, the catalogue or the instruction text pointing at a removed path.
## What it leaves out ## What it leaves out
Semantic or vector search; a browser view; line comments on a proposal (the judgement's findings name the section and Semantic or vector search; a browser view; line comments on a proposal (the judgement's findings name the section and
@@ -1,8 +1,8 @@
--- ---
layer: to-be layer: to-be
status: in-progress status: designed
code: [mesh-catalog, mesh-controller] code: []
updated: 2026-10-17 updated: 2026-10-13
decisions: decisions:
- "ADR 0316" - "ADR 0316"
- "ADR 0317" - "ADR 0317"
@@ -12,8 +12,6 @@ decisions:
# 56 — The mail client, set up by the mesh # 56 — The mail client, set up by the mesh
**Where it stands, 2026-10-17** (the records cleanup, issue 1072; playbook 07, "The design's state is written back"). Built on main: issue 586, this design's core, is resolved and delivered (hq#336, mesh-catalog#262, mesh-controller#286), and mesh-catalog's betterbird module has every piece named here. Not yet proven live end to end, nor given an as-is: issue 1076.
**On every node that holds a display server, the `betterbird` module installs Betterbird from a pinned **On every node that holds a display server, the `betterbird` module installs Betterbird from a pinned
upstream release, ships the Owl add-on with it through Betterbird's enterprise policy, and declares the upstream release, ships the Owl add-on with it through Betterbird's enterprise policy, and declares the
operator's mail accounts without their credentials. A password or token never passes through the mesh: each operator's mail accounts without their credentials. A password or token never passes through the mesh: each
@@ -1,8 +1,8 @@
--- ---
layer: to-be layer: to-be
status: in-progress status: designed
code: [mesh-controller, mesh-host, mesh-sdk, mesh-catalog] code: [mesh-controller, mesh-host, mesh-sdk, mesh-catalog]
updated: 2026-10-17 updated: 2026-10-14
decisions: decisions:
- "ADR 0348" - "ADR 0348"
- "ADR 0349" - "ADR 0349"
@@ -61,10 +61,6 @@ both, by the form of its manifest, and the old forms stay accepted until the las
Inside the release this still holds: the media catalogue and the applications kept in their own Inside the release this still holds: the media catalogue and the applications kept in their own
repositories move after it, so the old forms stay accepted until they have (R1). repositories move after it, so the old forms stay accepted until they have (R1).
**Where it stands, 2026-10-17** (the Milestones section below). The packages' branches were merged into
main of mesh-controller, the node-engine (`mesh-host`) and mesh-catalog on 2026-10-16. T6 moved after them, as
planned above, and is resolved. B3 was left out as well, so B3, R1 and R2 are what is left.
## What exists already, measured ## What exists already, measured
Counted 2026-10-14 at mesh-controller `1601cb1`, the node-engine (`mesh-host`) `1a26778`, mesh-sdk `a2d2af0`, mesh-catalog Counted 2026-10-14 at mesh-controller `1601cb1`, the node-engine (`mesh-host`) `1a26778`, mesh-sdk `a2d2af0`, mesh-catalog
@@ -337,17 +333,6 @@ removed code.
amended, the 67 documents that still say "setting" in running prose are reworded so the glossary can amended, the 67 documents that still say "setting" in running prose are reworded so the glossary can
retire the word (F4 left it off the *Not:* line for them), design 49 lists the Config domain, ADR 0348's and 0349's built status is said, and this document's status becomes `implemented`. retire the word (F4 left it off the *Not:* line for them), design 49 lists the Config domain, ADR 0348's and 0349's built status is said, and this document's status becomes `implemented`.
## Milestones
The packages above were built as one delivery, not milestone by milestone; the milestones group them by what
each makes true. Whether a milestone's issues are delivered is read from the issue tracker (playbook 07).
| # | Outcome | Packages | Issues |
|---|---|---|---|
| 1 | Config, provisioning, secrets, bindings, collections and runtime asks run the new way; the old forms are still accepted | every package but T6, B3, R1, R2 | 647, 650 to 679 but 659, 681, 684, 685 |
| 2 | The media catalogue and the applications carry their file text as templates | T6 | 659 |
| 3 | The other providers answer their consumers; the old forms are refused; the record says what runs | B3, R1, R2 | 680, 682, 683 |
## What is deliberately not here ## What is deliberately not here
- **Secrets in a module's own code** wait on issue 569 (a module's tools run as the tool runner's - **Secrets in a module's own code** wait on issue 569 (a module's tools run as the tool runner's
@@ -2,7 +2,7 @@
layer: to-be layer: to-be
status: designed status: designed
code: [mesh-catalog, mesh-media-catalog, mesh-sdk, mesh-tools, mesh-controller, mesh-host, mesh-lab] code: [mesh-catalog, mesh-media-catalog, mesh-sdk, mesh-tools, mesh-controller, mesh-host, mesh-lab]
updated: 2026-10-17 updated: 2026-10-16
decisions: decisions:
- "ADR 0383" - "ADR 0383"
- "ADR 0384" - "ADR 0384"
@@ -12,10 +12,6 @@ decisions:
- "ADR 0388" - "ADR 0388"
- "ADR 0389" - "ADR 0389"
- "ADR 0390" - "ADR 0390"
- "ADR 0411"
- "ADR 0412"
- "ADR 0413"
- "ADR 0414"
--- ---
# 58 — Agent work as the mesh's own # 58 — Agent work as the mesh's own
@@ -25,10 +21,8 @@ bus by a thread seat; a thread handler on an always-on node runs the agent harne
of its own, with the thread's own branch on the forge as its workspace; the agent works through a workspace toolkit of its own, with the thread's own branch on the forge as its workspace; the agent works through a workspace toolkit
and named tools, never a general command runner. Every person, agent and module is an identity held by one identity and named tools, never a general command runner. Every person, agent and module is an identity held by one identity
seat, with a role made of grants; the bus checks each login by its signature, and the tool runner checks each call seat, with a role made of grants; the bus checks each login by its signature, and the tool runner checks each call
against the role, tool by tool, by the effect every tool carries. An identity is also its logins: the mesh makes each against the role, tool by tool, by the effect every tool carries.**
person's and agent's machine account on every machine it is present on, gives each its own bus password, and runs a (ADR 0383 to ADR 0390, from research 053.)
module for an identity only where the module is set to; everything else runs as the operator.**
(ADR 0383 to ADR 0390, from research 053; ADR 0411 to ADR 0414, from research 057.)
**Acceptance.** The operator starts a thread from the terminal client, an agent on an always-on node works it in a **Acceptance.** The operator starts a thread from the terminal client, an agent on an always-on node works it in a
sandbox, asks a question the operator answers in place, and the thread moves to another node and carries on with sandbox, asks a question the operator answers in place, and the thread moves to another node and carries on with
@@ -112,95 +106,6 @@ agent carries a token the identity seat signed for that agent, and the tool runn
refusal names the missing grant; every call is recorded with its identity and thread. A session on an operator's refusal names the missing grant; every call is recorded with its identity and thread. A session on an operator's
machine calls as the operator's person identity or as an agent, never through the machine's tool-runner login. machine calls as the operator's person identity or as an agent, never through the machine's tool-runner login.
### 5.1 An identity on the machines (ADR 0411, ADR 0414)
A person or agent identity also holds a **login name**, the **machines** it is present on and, for a person, the
login name kept on a machine where it differs. The seat starts with two: the operator's person identity and `nox`, the one
agent; every piece of agent work is a thread of `nox` (§2), and a second agent identity is made only when a rule needs
another role. The agent is `nox` everywhere: its login, its forge account (the forge account `nox-agent` renamed,
keys and team kept), Keycloak, mail and the bus. A login or service account the mesh makes for the operator from now on carries the operator's identity name;
the operator's existing logins and forge account are kept as they are. On a machine where agents ran as the
operator's login, that login stays the operator's and `nox` gets a login of its own; nox's bus account is `agent.nox`. `nox` is also the
operator's terminal command (`mesh-cli`), and prose says which is meant.
An identity's login on one machine is its **machine account**. The controller composes the machine account of every
identity present on a machine into that machine's declaration, and the node-engine applies it with its `user`
resource. A login that exists is adopted under its name, uid and home; the mesh never renames or deletes one. An
agent's machine account has its own home and group, no password, no root group and no sudo rule, and the node-engine
judges on every look that it cannot become root without a person, by the ways ADR 0266 §12 to §17 list. No module declares a login for a
person or an agent: claude-code's `agent-account` resource goes, and the catalogue's check refuses a manifest that
declares one. The node record's operator account and agent account are read from the identities, no longer stated
at the controller's terminal or in the mesh-config snapshot; the machine facts `account`, `agent-account` and their
homes keep their names. A machine names an agent account only where the identity seat marks an agent as its agent
account: the control node, marked `nox`, where the guard and the licence stay as they are. The other machines are
marked with none, so the guard stays off there and the operator's sessions keep the mesh's tools, while the login `nox` serves the
modules run for `nox`. Where a person or an agent is present, its kept logins and the agent account mark change only
through a verb of the identity seat that only the operator calls, on an Approve; an assignment never sets the mark,
because an agent may call the verb that changes an assignment (ADR 0266 §4). The verb, and the controller at composition,
refuse an agent whose login on a machine would be `root`, a person's login there or a well-known service account
(ADR 0266 §5). The self-check judges every agent login,
marked or not. The seed: the operator on every machine, with the operator's existing login kept where it differs from the
identity's name; `nox` on every machine, the agent account on the control node.
```
identity seat controller each machine (node-engine)
───────────── ────────── ──────────────────────────
person operator, machines, composes, per machine: the operator's login (adopted)
the login kept on each the machine accounts present nox, root: never (adopted on the control node,
agent nox, its machines the facts account, agent-account made elsewhere)
each identity's bus password ──► its secret, owned by its login, 0600
```
### 5.2 Run for an identity (ADR 0412)
Every module runs as the operator account of its machine, as today, with no setting; running for the operator is that
default run. A manifest may declare that its module can **run for** an agent identity, at assignment, at start or both: serve one identity with that identity's machine account, home and bus password.
An assignment of such a module may name the identity it runs for, and the module may let the one who starts it
choose: claude-code started for the operator is the default run with the operator's configuration, and started for `nox`
it runs for `nox` with the agent's home. A start
goes down only: a module is started for an identity by a process already running as that identity's login, or by the
operator's own login for an agent; an agent never starts one for a person, and the module refuses it, naming who asked
and for whom. A module may be assigned to one machine once per identity it runs for there, each its own process under
its own login, and the controller keys an assignment by machine, module and identity. The first two to declare it are
claude-code and ssh-client. On a machine the identity seat marks with an agent account, claude-code's assignment is
run for that agent (`nox` on the control node), so its licence and home scope stay in the agent's home, and the controller refuses
one there that is not. ssh-client keeps its default assignment for
the operator wherever `nox` is present and gains a second one run for `nox`, which makes the agent's forge key in the
agent's home and closes issue 989; gitea's provider registers that key on the forge account `nox`. A module run for an identity calls as that
identity and gets no module identity of its own (this narrows ADR 0386 §4, which holds for every other assignment). A module with an account of its
own (`runs-as`, ADR 0259 §8) cannot declare it, and a run for an identity with no machine account on that machine is
refused at composition and at start. ADR 0266's guard
stands: on the control node, a session started for the operator gets the operator's configuration and its tool calls are still
refused.
### 5.3 A bus password per identity (ADR 0413)
Each person and agent identity has a bus account of its own (`person.<name>` for the operator, `agent.nox`), whose memberships are made
from its role, and its password is a secret owned by its machine account at mode 0600 on each machine it is present
on, which no other account but root can read. A person's password is placed only on a machine that names an agent
account of its own judged unable to become root, by a verdict at most fifteen minutes old; on the other machines, where agents can still run as the operator,
it is not placed, and the controller says so. A module run for an identity reaches the bus with that password, can do
only what the identity's role allows, and every call is recorded as that identity's. Nox's role never holds an approval
grant, a trusted party's grant or a dangerous tool named in it (ADR 0387 §4 does not apply to it), and the identity seat
refuses one; each dangerous act of nox's is asked one by one. The break-glass agent (§6) keeps its own rules and gets
no login and no bus password from this. This changes ADR 0259 §8, which kept every bus credential out of an agent's account; the rest of §8 stands.
The password lives in today's list of bus accounts and is replaced by a signed login for the same role at the bus
switch (§7, §8).
### 5.4 Traced to GENESIS
Checked on 2026-10-17 (playbook 02, step 1); no conflict was found.
- [Mission](../../00-META/mission.md): agents "hold identity", and there is one kind of participant, some of them
human; persons and agents are held alike, with the same fields, and differ in what their role allows, which the
mission calls modality. "The mesh brokers credentials": the bus password per identity and the service accounts made
from it. "The mesh owns the truth": logins are derived from identities, no longer stated per node. "Dogfood": logins
go through the node-engine's existing resource.
- [Context](../../00-META/context.md): "Identity is mesh-internal", with no directory and no SSO, so the identity seat
holds it and Keycloak only receives accounts; "Nodes are personal" and the mesh is a guest, so existing logins are
adopted, never renamed; human agents are few, often one, so the seat holds two identities and one agent.
- [Effect](../../00-META/effect.md): a human agent's environment is part of the mesh, its login included.
## 6. Effects, asks and break-glass (ADR 0387) ## 6. Effects, asks and break-glass (ADR 0387)
Every seat verb and module tool is one permission and carries one **effect**, set in its module's manifest: Every seat verb and module tool is one permission and carries one **effect**, set in its module's manifest:
@@ -251,36 +156,12 @@ carry them:
`research/053-signed-bus-logins`, loses nothing and leaves every identity on the new bus. `research/053-signed-bus-logins`, loses nothing and leaves every identity on the new bus.
5. The acceptance sentence at the top, end to end. 5. The acceptance sentence at the top, end to end.
From research 057 (ADR 0411 to ADR 0414):
6. The identity seat lists the operator and `nox` with their machines, kept logins and the control node's agent account mark; a
create or a presence change by any caller but the operator's Approve is refused.
7. On every other machine a login `nox` exists; on the control node the login `nox` is adopted with its uid and home unchanged;
on every machine the node-engine judges `nox` unable to become root; a login `nox` given a sudo rule in a test is left
unaltered and the self-check raises `agent-can-become-root` for it, on a machine that marks no agent account too;
the operator's existing logins are
unchanged.
8. No manifest in the catalogue declares a login for a person or an agent; the catalogue's check refuses one.
9. For every machine, the declaration before and after differs, for the modules not set to run for an identity, only
in the machine accounts' `user` resources (now the controller's, and `nox` added on the other machines); claude-code on the control node, run for `nox`, places the same
files in the same home; on the other machines the guard stays off and the operator's sessions keep the mesh's tools.
10. A session started for `nox` on the control node gets the agent's home configuration and one started for the operator the
operator's, with no tool call through; a session as `nox` that starts claude-code for the operator is refused; with
ssh-client run for `nox`, nox's key is on the forge account `nox` and the session pushes a branch over SSH as `nox`.
11. Nox's bus password is owned by `nox` at mode 0600 on every machine nox is present on; a call with it outside
nox's role is refused; a grant test finds no approval grant, trusted party's grant or dangerous tool in nox's
role; the operator's password is placed only on a machine with an agent account judged root-free within fifteen
minutes, and the controller says so for every other.
12. The forge account `nox-agent` is renamed `nox`, keeps every machine's key, stays in the agents' write team, and
every `forge-account` entry names `nox`.
## 11. Milestones ## 11. Milestones
Each milestone is one feature step (playbook 07), usable as soon as its delivery plan is done, and starts when Each milestone is one feature step (playbook 07), usable as soon as its delivery plan is done, and starts when
the previous one's is. What it replaces is in the as-is documents the previous one's is. What it replaces is in the as-is documents
[15 — The agent and its licences](../00-as-is/15-the-agent-and-its-licences.md) and [15 — The agent and its licences](../00-as-is/15-the-agent-and-its-licences.md) and
[16 — Logins on the bus](../00-as-is/16-logins-on-the-bus.md), and for milestones 11 to 14 [16 — Logins on the bus](../00-as-is/16-logins-on-the-bus.md). The issues are opened once the operator confirms
[17 — Accounts on a machine](../00-as-is/17-accounts-on-a-machine.md). The issues are opened once the operator confirms
the milestones, and their numbers written here. the milestones, and their numbers written here.
**Confirmed by the operator on 2026-10-16.** One issue per milestone: 1 is issue 878, 2 is 879, 3 is 880, 4 is **Confirmed by the operator on 2026-10-16.** One issue per milestone: 1 is issue 878, 2 is 879, 3 is 880, 4 is
@@ -305,18 +186,3 @@ and the issue tracker takes only registered owners. The building waits until the
The front ends' later parts, the inference seat and the other agent harnesses are tracked by issue 850; the The front ends' later parts, the inference seat and the other agent harnesses are tracked by issue 850; the
seat-sense "role" in older designs by issue 872. seat-sense "role" in older designs by issue 872.
**Milestones 11 to 14, from research 057, confirmed by the operator on 2026-10-17.** One issue per repository
part, opened at the confirmation, so a fixing agent per repository can start when its step does. Milestone 11
waits for nothing; 12 waits for milestone 6.
| # | Outcome, in one sentence | Proves | Repositories and their parts (issues) | Depends on |
|---|---|---|---|---|
| 11 | The agent's forge account is `nox`, with its keys and its place in the agents' write team kept | test 12 | mesh-catalog gitea: the rename, keys registered on `nox`, every `forge-account` entry (993) | — |
| 12 | The mesh makes the operator's logins and nox's on every machine from the identities, and judges nox unable to become root everywhere | tests 6 to 9 | identity module: logins, machines, kept logins, the agent account mark, the operator's verb (994); the node-engine (`mesh-host`): existing logins adopted, every agent login judged (995); mesh-controller: machine accounts composed, node accounts read from the identities, the terminal lines retired (996); mesh-catalog: claude-code declares no login, the catalogue's check refuses one (997) | 6 |
| 13 | The operator and nox each reach the bus with their own password, held to their role; the operator's only where no agent runs as the operator | test 11 | mesh-controller: the operator's and nox's bus accounts, secrets at 0600, the fifteen-minute verdict (999); identity module: nox's role refuses approvals and dangerous tools (998); until milestone 7 builds the asks, a dangerous act of nox's is refused, not asked | 12 |
| 14 | The operator starts a claude-code session for nox or for themself, and nox pushes over SSH with its own key | test 10 | mesh-controller: run for, assignments keyed by identity, the refusals (1001); mesh-sdk: calls as the identity, a start goes down only (1002); claude-code: started for nox or for the operator (1003); ssh-client and gitea: nox's own key, closing issue 989 (1004) | 11, 13 |
The agent's and the operator's accounts in Keycloak and mail (ADR 0414 §2 and §3) follow milestone 13 and are
tracked by issue 1000, which needs a design section first. A second agent identity is made only when a rule
needs another role (ADR 0414 §1); no milestone makes one.