Compare commits

...
Author SHA1 Message Date
mesh-tools b29008ad36 Merge pull request 'Playbook 02: one design per thing' (#387) from process/02-one-design-per-thing into main 2026-10-16 17:41:22 +00:00
jschoubben 101d7f30f7 Playbook 02: one design per thing; a graduation rewrites the design that covers it instead of writing a second (the operator, 2026-10-16)
mesh/merge-gate head 101d7f30f7e2 merged into trunk 225eee14d0bf — pass: the change touches no module of the mesh's graph
mesh/repo-check head 101d7f30f7e2 merged into trunk 225eee14d0bf — pass: its merge-check.sh passed
mesh/delivery delivered
2026-10-16 19:40:48 +02:00
mesh-tools 225eee14d0 Merge pull request 'Design 47: a move of the trunk asks every active check again (ADR 0395)' (#381) from design/47-recheck-on-trunk-move into main 2026-10-16 17:35:52 +00:00
jschoubben 12e75cdfa2 Design 47: the transition paragraph, the recheck verb and the section's bullets say ADR 0395 whole (review)
mesh/merge-gate head 12e75cdfa215 merged into trunk f1081c4f9768 — pass: the change touches no module of the mesh's graph
mesh/repo-check head 12e75cdfa215 merged into trunk f1081c4f9768 — pass: its merge-check.sh passed
mesh/delivery delivered
2026-10-16 19:27:52 +02:00
jschoubben 377fa3f88a Design 47: say ADR 0395 as accepted — a conflict is asked again at every move, an ended check's builds are not reused, a group's composed check reruns through its members, and the cost bound 2026-10-16 19:26:47 +02:00
jschoubben 11e32ce5be Merge remote-tracking branch 'origin/main' into design/47-recheck-on-trunk-move 2026-10-16 19:26:35 +02:00
mesh-tools f1081c4f97 Merge pull request 'Playbook 08: a second fault the delivery shows joins the issue only in the same files' (#386) from fix/pb08-second-fault into main 2026-10-16 17:09:53 +00:00
mesh-tools 152cc0c0e7 Playbook 08: a second fault the delivery shows joins the issue only in the same files
mesh/merge-gate head 152cc0c0e77c merged into trunk a8b951f3b488 — pass: the change touches no module of the mesh's graph
mesh/repo-check head 152cc0c0e77c merged into trunk a8b951f3b488 — pass: its merge-check.sh passed
mesh/delivery delivered
Taken from issue 937, where mesh-catalog #335 was taken into the issue
and issue 943 was opened for a fault in other code.
2026-10-16 17:06:25 +00:00
mesh-tools a8b951f3b4 Merge pull request 'Playbook 08: needing a decision keeps a fix small' (#385) from process/08-a-decision-keeps-a-fix-small into main 2026-10-16 15:58:01 +00:00
jschoubben 0eadc27fc7 Playbook 08: needing a decision keeps a fix small; a feature is several modules with research or a design before it (the operator, 2026-10-16)
mesh/merge-gate head 0eadc27fc722 merged into trunk 4e437eb1488f — pass: the change touches no module of the mesh's graph
mesh/repo-check head 0eadc27fc722 merged into trunk 4e437eb1488f — pass: its merge-check.sh passed
mesh/delivery delivered
2026-10-16 17:46:23 +02:00
mesh-tools 4e437eb148 Merge pull request 'Design 58: agent work as the mesh's own (graduation of research 053)' (#372) from design/58-agent-work-as-the-meshs-own into main 2026-10-16 14:09:25 +00:00
jschoubben 0e7a115183 Merge pull request 'Playbooks 07 and 08: mark ready with mesh-delivery.mark-ready (issue 913)' (#383) from feat/913-mark-ready into main
Reviewed-on: #383
2026-10-16 13:47:15 +00:00
jschoubben 18428baf4b Merge remote-tracking branch 'origin/main' into design/58-agent-work-as-the-meshs-own
mesh/merge-gate head 18428baf4b23 merged into trunk 0e7a115183a9 — pass: the change touches no module of the mesh's graph
mesh/repo-check head 18428baf4b23 merged into trunk 0e7a115183a9 — pass: its merge-check.sh passed
mesh/delivery delivered
2026-10-16 14:53:11 +02:00
jschoubben 7b5f3582be Playbooks 07 and 08: mark ready with mesh-delivery.mark-ready, label then comment in the order the forge counts (issue 913)
mesh/merge-gate head 7b5f3582be90 merged into trunk 6dcfa2226e63 beside mesh-controller#357@114c99e0 +1 more — pass: the change touches no module of the me…
mesh/repo-check head 7b5f3582be90 merged into trunk 6dcfa2226e63 beside mesh-controller#357@114c99e0 +1 more — pass: its merge-check.sh passed
mesh/delivery delivered
mesh/delivery-group group feat/913-mark-ready delivered: every member is delivered
2026-10-16 14:31:49 +02:00
jschoubben 6dcfa2226e Merge pull request 'Playbooks 01 and 02: place research in the domains; its words reach the glossary at graduation' (#382) from playbooks/01-place-it-in-the-domains into main
Reviewed-on: #382
2026-10-16 12:24:26 +00:00
jschoubben 1ddc2f5cbf Merge pull request 'Design 41: every file on a node is managed through node-files (WP9, ADR 0394)' (#380) from design/41-node-files into main
Reviewed-on: #380
2026-10-16 12:24:18 +00:00
jschoubben 06998cc8d9 Merge pull request 'Playbooks 07 and 08: the five rules from the delivery-time audit' (#375) from playbooks/07-08-time-sinks into main
Reviewed-on: #375
2026-10-16 12:24:07 +00:00
jschoubben 353bfdcef3 Merge remote-tracking branch 'origin/main' into design/58-agent-work-as-the-meshs-own-tmp
mesh/merge-gate out of date: judged against trunk b9af4e373dc0, the trunk is now 6dcfa2226e63; judged again on a recheck or when it is merged
mesh/repo-check out of date: judged against trunk b9af4e373dc0, the trunk is now 6dcfa2226e63; judged again on a recheck or when it is merged
mesh/delivery superseded: a newer head of the same pull request
2026-10-16 14:00:12 +02:00
jschoubben 91f82cd38e Design 47: a group's own merge in order asks nothing again (ADR 0395 §1, review)
mesh/merge-gate out of date: judged against trunk b9af4e373dc0, the trunk is now 6dcfa2226e63; judged again on a recheck or when it is merged
mesh/repo-check out of date: judged against trunk b9af4e373dc0, the trunk is now 6dcfa2226e63; judged again on a recheck or when it is merged
mesh/delivery superseded: a newer head of the same pull request
2026-10-16 13:32:39 +02:00
jschoubben 300be92814 Design 41 WP9: readlink and the account and group lookup are node-files verbs too (ADR 0394 §1; review)
mesh/merge-gate head 300be928144e merged into trunk b9af4e373dc0 — pass: the change touches no module of the mesh's graph
mesh/repo-check head 300be928144e merged into trunk b9af4e373dc0 — pass: its merge-check.sh passed
mesh/delivery delivered
2026-10-16 13:31:45 +02:00
jschoubben 427fe4c9c5 Playbooks 01 and 02: place research in the domains before its direction, and carry its words into the glossary at graduation (ADR 0244)
mesh/merge-gate head 427fe4c9c58b merged into trunk b9af4e373dc0 — pass: the change touches no module of the mesh's graph
mesh/repo-check head 427fe4c9c58b merged into trunk b9af4e373dc0 — pass: its merge-check.sh passed
mesh/delivery delivered
The operator, 2026-10-16: research defines the modules a feature lands in and the domain extensions and
reductions that come with it.
2026-10-16 12:38:59 +02:00
jschoubben a65e1076a2 Design 47: a pull request that no longer merges cleanly is told, not checked (ADR 0395 §2)
mesh/merge-gate head a65e1076a2e5 merged into trunk b9af4e373dc0 — pass: the change touches no module of the mesh's graph
mesh/repo-check head a65e1076a2e5 merged into trunk b9af4e373dc0 — pass: its merge-check.sh passed
mesh/delivery superseded: a newer head of the same pull request
2026-10-16 11:42:03 +02:00
jschoubben 09ec150bd6 Design 47: a move of the trunk asks every active check again, one at a time per pull request (ADR 0395, issue 888)
mesh/delivery superseded: a newer head of the same pull request
mesh/merge-gate error: the check could not run: killed by hand
mesh/repo-check error: the check could not run: killed by hand
2026-10-16 11:40:14 +02:00
jschoubben 1efa37957d Playbooks 07 and 08: is the coast clear? Hold a push while a release branch in flight shares its files; the glossary's release branch
mesh/merge-gate head 1efa37957db2 merged into trunk b9af4e373dc0 siblings unknown — pass: the change touches no module of the mesh's graph
mesh/repo-check head 1efa37957db2 merged into trunk b9af4e373dc0 siblings unknown — pass: its merge-check.sh passed
mesh/delivery delivered
The operator, 2026-10-16: the file module waited for feature 57's release branch, which changes four of the
same controller files.
2026-10-16 11:28:32 +02:00
jschoubben 26073f30b0 Design 41: node-reads goes in the delivery after node-files is held on every node (ADR 0394 §9)
mesh/merge-gate head 26073f30b0de merged into trunk b9af4e373dc0 — pass: the change touches no module of the mesh's graph
mesh/repo-check head 26073f30b0de merged into trunk b9af4e373dc0 — pass: its merge-check.sh passed
mesh/delivery superseded: a newer head of the same pull request
2026-10-16 11:14:01 +02:00
jschoubben 1f5a12e07e Design 41: WP8's node-reads is replaced by WP9's node-files (ADR 0394 supersedes ADR 0310)
mesh/merge-gate head 1f5a12e07e42 merged into trunk b9af4e373dc0 — pass: the change touches no module of the mesh's graph
mesh/repo-check head 1f5a12e07e42 merged into trunk b9af4e373dc0 — pass: its merge-check.sh passed
mesh/delivery superseded: a newer head of the same pull request
2026-10-16 11:11:53 +02:00
jschoubben 7ac5194bcb Design 41 WP9: mesh-files runs as the operator account with sudo where needed, and replaces node-reads (ADR 0394)
mesh/merge-gate head 7ac5194bcbdc merged into trunk b9af4e373dc0 — pass: the change touches no module of the mesh's graph
mesh/repo-check head 7ac5194bcbdc merged into trunk b9af4e373dc0 — pass: its merge-check.sh passed
mesh/delivery superseded: a newer head of the same pull request
2026-10-16 11:06:32 +02:00
jschoubben 330d341618 Design 41 WP9: node-files is held by a module of its own, mesh-files, beside mesh-reads
mesh/merge-gate waiting in the build queue since 11:01
mesh/delivery superseded: a newer head of the same pull request
2026-10-16 11:01:43 +02:00
jschoubben cf56649845 Design 41: every file on a node is managed through node-files (WP9, ADR 0394, issue 900)
mesh/merge-gate waiting in the build queue since 11:01
mesh/delivery superseded: a newer head of the same pull request
2026-10-16 11:00:46 +02:00
mesh-tools b9af4e373d Merge pull request 'Feature 57 (config and provisioning): release hq's record' (#378) from release/57-config-and-provisioning into main 2026-10-16 08:41:58 +00:00
mesh-tools e7417cf124 Feature 57 (config and provisioning): release hq's record (glossary words, release notes) on top of main
mesh/merge-gate head e7417cf1248c merged into trunk c22b58143b74 beside mesh-host#116@bff1ac14 — pass: the change touches no module of the mesh's graph
mesh/repo-check head e7417cf1248c merged into trunk c22b58143b74 beside mesh-host#116@bff1ac14 — pass: its merge-check.sh passed
mesh/delivery delivered
mesh/delivery-group group release/57-config-and-provisioning delivered: every member is delivered
2026-10-16 08:24:14 +00:00
jschoubben 47f7321c05 Playbooks 07 and 08: integration tests only in the merge check, no try before it, no lab and no end-to-end tests yet (the operator, 2026-10-16)
mesh/merge-gate out of date: judged against trunk c22b58143b74, the trunk is now b9af4e373dc0; judged again on a recheck or when it is merged
mesh/repo-check out of date: judged against trunk c22b58143b74, the trunk is now b9af4e373dc0; judged again on a recheck or when it is merged
mesh/delivery superseded: a newer head of the same pull request
2026-10-16 10:15:31 +02:00
jschoubben f95043e379 Playbooks 07 and 08: a module's acceptance is an integration test the forge runs, and try runs on the builder (ADR 0391)
mesh/merge-gate head f95043e3797e merged into trunk c22b58143b74 — pass: the change touches no module of the mesh's graph
mesh/repo-check head f95043e3797e merged into trunk c22b58143b74 — pass: its merge-check.sh passed
mesh/delivery superseded: a newer head of the same pull request
2026-10-16 10:14:42 +02:00
jschoubben cb98a08384 Playbooks 07 and 08: prove a module whole on a throwaway bus and store, not in the lab while its runs are too slow (the operator, 2026-10-16) 2026-10-16 10:14:25 +02:00
jschoubben 4e822c56a0 Playbooks 07 and 08: checks for every new rule, the ready mark's bound in both, the lead marks an unreviewed edit (review round 1) 2026-10-16 10:14:25 +02:00
jschoubben c5fa1de264 Playbooks 07 and 08: review asks only what makes a change wrong, try before opening, mark ready on approval, prove a module whole first, hand back stopped issues
From the delivery-time audit of 2026-10-16: seven review rounds on issue 704, a feature branch's first
check failing on 29 problems, mesh-catalog #300 waiting 12 h unmarked, the mail modules failing live,
98 issues held in fixing.
2026-10-16 10:14:25 +02:00
jschoubben ea359ea4e6 Try a small fix's branch before its pull request, since local runs miss the check's toolchain (playbook 08) 2026-10-16 10:14:25 +02:00
mesh-tools c22b58143b Merge pull request 'Test levels and time budgets in how-we-build and playbooks 07 and 08 (issue 891, ADR 0391)' (#374) from fix/891-test-levels-and-budgets into main 2026-10-16 08:13:26 +00:00
mesh-tools d9dd8a63d8 Say ADR 0391's test levels and time budgets in how-we-build and playbooks 07 and 08 (issue 891)
mesh/merge-gate head d9dd8a63d83b merged into trunk 08d6476e5287 — pass: the change touches no module of the mesh's graph
mesh/repo-check head d9dd8a63d83b merged into trunk 08d6476e5287 — pass: its merge-check.sh passed
mesh/delivery delivered
2026-10-16 08:09:33 +00:00
jschoubben 43c3f2c098 Design 58: the operator confirmed the milestones; issues 878 to 887, building after design 57's delivery (playbook 04)
mesh/merge-gate out of date: judged against trunk 08d6476e5287, the trunk is now c22b58143b74; judged again on a recheck or when it is merged
mesh/repo-check out of date: judged against trunk 08d6476e5287, the trunk is now c22b58143b74; judged again on a recheck or when it is merged
mesh/delivery superseded: a newer head of the same pull request
2026-10-16 09:55:11 +02:00
jschoubben 45e2d7e972 Design 58: milestones for the operator to confirm, owners named, and the as-is of logins on the bus (playbook 04)
mesh/merge-gate head 45e2d7e9722d merged into trunk 08d6476e5287 — pass: the change touches no module of the mesh's graph
mesh/repo-check head 45e2d7e9722d merged into trunk 08d6476e5287 — pass: its merge-check.sh passed
mesh/delivery superseded: a newer head of the same pull request
2026-10-16 03:03:21 +02:00
jschoubben 9844020589 Glossary: Uses lines, the thread's two records, the grant from ADR 0386 §4, the provisioner's harness, issue 872 for the seat-sense role (review round 1)
mesh/merge-gate head 9844020589df merged into trunk 08d6476e5287 — pass: the change touches no module of the mesh's graph
mesh/repo-check head 9844020589df merged into trunk 08d6476e5287 — pass: its merge-check.sh passed
mesh/delivery superseded: a newer head of the same pull request
2026-10-16 02:49:22 +02:00
jschoubben 535fd8bb54 Glossary: the words of research 053's graduation, and harness and role as homonyms
mesh/merge-gate head 535fd8bb5498 merged into trunk 08d6476e5287 — pass: the change touches no module of the mesh's graph
mesh/repo-check head 535fd8bb5498 merged into trunk 08d6476e5287 — pass: its merge-check.sh passed
mesh/delivery superseded: a newer head of the same pull request
Playbook 02 adds a graduated effort's words; design 58 now says agent harness, never the bare word.
2026-10-16 02:47:48 +02:00
jschoubben cb4c18cedc Design 58: the identity store is read by grant, and sessions never call through the tool runner's login (review round 1)
mesh/merge-gate head cb4c18cedcf0 merged into trunk 08d6476e5287 — pass: the change touches no module of the mesh's graph
mesh/repo-check head cb4c18cedcf0 merged into trunk 08d6476e5287 — pass: its merge-check.sh passed
mesh/delivery superseded: a newer head of the same pull request
2026-10-16 02:45:58 +02:00
jschoubben 8f8f1b4337 Design 58: agent work as the mesh's own, graduated from research 053 with ADR 0383 to ADR 0390
Threads on the bus, a headless thread handler in a sandbox per session, a workspace toolkit with no
general command runner, one identity seat, effects on every tool, signed bus logins and the switch
to them; names the build's acceptance tests for the milestones.
2026-10-16 02:44:15 +02:00
mesh-tools 08d6476e52 Merge pull request 'Playbooks 01, 02 and 04: settle decisions before versions, test at the cheapest level, acceptance tests through to milestones' (#371) from playbooks/01-settle-before-versions into main 2026-10-16 00:15:00 +00:00
jschoubben 80b647a2ed Name the acceptance tests in the milestone table and say what the review returns (review round 2)
mesh/merge-gate head 80b647a2ed66 merged into trunk f8c0676b3a1d — pass: the change touches no module of the mesh's graph
mesh/repo-check head 80b647a2ed66 merged into trunk f8c0676b3a1d — pass: its merge-check.sh passed
mesh/delivery delivered
2026-10-16 02:12:17 +02:00
jschoubben a4c5ef69bb Give the build's acceptance tests a home in the design and milestones, and answer review round 1
The research playbook named acceptance tests the milestones carry, which
playbooks 02 and 04 did not know; its pre-check also disagreed with the
knowledge base's deferral rule.
2026-10-16 02:11:27 +02:00
jschoubben ada703e790 Write a graduation's records side by side, from the confirmed direction (playbook 02)
Proposing records one after another serialises their reviews and the
operator's asks; retyping the effort's findings into each record makes them
long and gives the review more to send back.
2026-10-16 02:09:57 +02:00
jschoubben 2a61c57258 Settle research decisions before proposing versions, and test on the cheapest bed (playbook 01)
Research 053's identity part took six versions and about two hours: options
that broke the mesh's own rules were offered and argued back, a version was
proposed after every answer, and reviews returned findings a pre-check
would have caught. A lab run was planned where an in-process test answered.
2026-10-16 02:09:22 +02:00
mesh-tools f8c0676b3a Merge pull request 'Cite ADR 0382 in the delivery design (issue 867)' (#370) from fix/867-group-siblings into main 2026-10-15 23:52:50 +00:00
mesh-toolsandClaude e4a55051cc Cite ADR 0382 in the delivery design: a group's member is checked beside its siblings (issue 867)
mesh/merge-gate head e4a55051cc3b merged into trunk 9bfd57174fab — pass: the change touches no module of the mesh's graph
mesh/repo-check head e4a55051cc3b merged into trunk 9bfd57174fab — pass: its merge-check.sh passed
mesh/delivery delivered
mesh/delivery-group group fix/867-group-siblings delivered: every member is delivered
Co-Authored-By: Claude <noreply@anthropic.com>
2026-10-15 23:27:14 +00:00
15 changed files with 680 additions and 77 deletions
+148 -36
View File
@@ -69,7 +69,8 @@ is listed under [Homonyms](#homonyms), with the qualified form each domain uses.
**Recorded in** the catalogue, one manifest per module. **Upstream of** every other domain; its words **Recorded in** the catalogue, one manifest per module. **Upstream of** every other domain; its words
are a published vocabulary, fixed by the manifest's schema. **Decided by** ADR 0040, 0126, 0174, 0188, are a published vocabulary, fixed by the manifest's schema. **Decided by** ADR 0040, 0126, 0174, 0188,
0207, 0236, 0262. 0207, 0236, 0262.
**Uses:** module, seat, provision (Provisioning), data class (Data), health (Health and repair). **Uses:** module, seat, config value (Config), provision (Provisioning), data class (Data), health (Health
and repair).
- **manifest** — the file a module is defined by (`module.json`): what it is, what it provides and - **manifest** — the file a module is defined by (`module.json`): what it is, what it provides and
requires, the seats it claims, the resources it declares, its tools, its data and its health. Say requires, the seats it claims, the resources it declares, its tools, its data and its health. Say
@@ -83,26 +84,17 @@ are a published vocabulary, fixed by the manifest's schema. **Decided by** ADR 0
it guards: the `mesh-controller`, `postgres` and `nats` modules claim the `mesh-controller`, it guards: the `mesh-controller`, `postgres` and `nats` modules claim the `mesh-controller`,
`mesh-store` and `mesh-broker` seats (ADR 0079, `mesh-store` and `mesh-broker` seats (ADR 0079,
ADR 0116). ADR 0116).
- **setting** — a value a manifest declares and an assignment gives, one of the two ways a node varies
a module (ADR 0174).
What a "flavor" once varied is a setting or a separate module.
*Not:* ~~flavor~~
- **preference** — a setting whose manifest gives it a default, because its value is true of the
software and only tuned per node: a font size, a width
(ADR 0262).
A setting with no default is the operator's own value: a domain, an identity.
*Not:* ~~tunable~~ (hq)
- **kept region** — a marked block in a managed file the mesh writes *into*, where the operator's own - **kept region** — a marked block in a managed file the mesh writes *into*, where the operator's own
lines survive every send and are given back when the module goes (ADR 0174). The other way a node lines survive every send and are given back when the module goes (ADR 0174). The other way a node
varies a module. varies a module, besides a config value; a **managed** file's marked block is one (Provisioning).
- **bundle** — the artifact a module's own code is built into — its tools, a seat's implementation, a - **bundle** — the artifact a module's own code is built into — its tools, a seat's implementation, a
daemon — in any language the mesh has a toolchain for; never an image. A **tools bundle** speaks MCP to daemon — in any language the mesh has a toolchain for; never an image. A **tools bundle** speaks MCP to
the tool runner. One module may declare several the tool runner. One module may declare several
(ADR 0188). (ADR 0188).
- **tool** — one capability a module serves through the tool runner, addressed `<module>.<tool>`. A - **tool** — one capability a module serves through the tool runner, addressed `<module>.<tool>`. A
seat's **verb** is the same thing carried by a seat rather than a module. seat's **verb** is the same thing carried by a seat rather than a module.
- **mesh-sdk** — the library a module's own code is written against, including the harness that serves a - **mesh-sdk** — the library a module's own code is written against, including the SDK harness that serves a
tools bundle to the tool runner. The harness is part of it, not a library of its own. tools bundle to the tool runner. The SDK harness is part of it, not a library of its own.
*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.
@@ -111,6 +103,55 @@ are a published vocabulary, fixed by the manifest's schema. **Decided by** ADR 0
(ADR 0236). (ADR 0236).
The controller's verb `upgrade` shows it. The controller's verb `upgrade` shows it.
## Config — the values someone may change
**Purpose.** To hold, check and resolve every value a module reads that someone may change, and to say
where each resolved value came from. **Recorded by** the `mesh-config` module, in its buckets on the bus,
with its store as the history behind them. **Upstream of** Provisioning, which places the values;
downstream of Module and Placement. **Decided by** ADR 0262, 0277, 0348.
**Uses:** module, node, manifest (Module), assignment (Placement), provision (Provisioning), proposal
(Operator and conversation).
- **config value** — anything a module reads that someone may change: a font size, a domain, an address.
A module declares every one it reads under `config` in its manifest, with a type, a description,
validation, whether it is trusted, optionally the predefined key it falls back to, and a default or
`required`; it lands in a template file as `${config:<key>}` or is read by the module's code at runtime.
It resolves from four sources, the most specific winning: the default in the manifest; **mesh config**,
the value of the predefined key it falls back to for the whole mesh; **node config**, that key's value
on one node; and **module-assignment config**, the module's own value on one node. There is no module
value for the whole mesh. *Config* is the concept, the verb (`config`) and the manifest field
(ADR 0348). Mesh config is a source level, the value set for the whole mesh; `mesh-config` is the module
that keeps every level. The controller holds, mirrors and resolves no config value (ADR 0348 §9).
Older records and documents say "setting" for a config value and "settings" for the concept; new writing
says config value and config. The word goes on this entry's *Not:* line once the documents in force that
still use it in running prose are reworded, which is issue 683; until then it is checked by review. The
manifest field and the verb "settings" are kept as aliases until every module has moved (ADR 0348 §1,
issue 630), and are named on the line below with the rest once issue 683 is done.
*Identifier until renamed:* `takes-settings`, `${setting:}` — manifest forms kept as aliases until every
module has moved to their config names (ADR 0348 §1, issue 630).
What a "flavor" once varied is a config value or a separate module.
*Not:* ~~flavor~~
- **predefined key** — a key of the catalogue's `predefined.json`, the fixed list of keys for which mesh
config and node config hold values, each with its type, description, validation and whether it is
trusted, such as `font-size`. A config value may name the predefined key it falls back to, of its own
type; one that names none resolves from its default and the module's own value only. The list is the
catalogue build's: a change to it is validated with every manifest naming a key, and removing a key still
named or still holding a value is refused (ADR 0348).
- **trusted** — said of a config value that decides what runs or who can reach what. At every level it is
set only at the controller's terminal or through `config` with propose, on the operator's Approve; a
module value that falls back to a trusted predefined key is itself trusted (ADR 0277, ADR 0348 §8). Not
the trusted sender of an operator message (see [Homonyms](#homonyms)).
- **preference** — a config value whose manifest gives it a default, because its value is true of the
software and only tuned per node: a font size, a width
(ADR 0262).
A config value with no default is `required`: the operator's own value, such as a domain or an
identity, and while nothing sets it the module's files are not written (ADR 0348).
*Not:* ~~tunable~~ (hq)
- **`mesh-config`** — the module that owns every config value, and not the source level mesh config: it
holds the seat `mesh-config` once for the mesh, validates, takes a trusted value only from the
controller's terminal or the operator's Approve, keeps the history with who set it and why, and resolves
the four sources (ADR 0348).
## Core — the mesh's own machinery ## Core — the mesh's own machinery
**Purpose.** To keep the controller, the bus, the store and every node's engine running and agreed, so **Purpose.** To keep the controller, the bus, the store and every node's engine running and agreed, so
@@ -190,9 +231,10 @@ every other domain has something to run on. **Recorded by** the controller and t
**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. and Health and repair. **Decided by** ADR 0100, 0126, 0176, 0181, 0207, 0221, 0252, 0261.
**Uses:** node, machine, module, seat, setting (Module), manifest (Module). **Uses:** node, machine, module, seat, manifest (Module), config value (Config).
- **assignment** — a module put on a node, with the settings that node gives it. - **assignment** — a module put on a node. Its config values on that node (module-assignment config,
Config) are `mesh-config`'s, never part of what the controller records (ADR 0348 §9).
- **pending assignment** — an assignment of a module whose build is still running, kept, and made by the - **pending assignment** — an assignment of a module whose build is still running, kept, and made by the
controller when that build registers the module, or ended with why when it does not controller when that build registers the module, or ended with why when it does not
(ADR 0261). (ADR 0261).
@@ -227,7 +269,7 @@ and Health and repair. **Decided by** ADR 0100, 0126, 0176, 0181, 0207, 0221, 02
(ADR 0181). (ADR 0181).
Not a name a manifest carries. Not a name a manifest carries.
- **agent account** — the login every coding agent on a node runs as, when the node names one of its own; - **agent account** — the login every coding agent on a node runs as, when the node names one of its own;
stated on the node record at the controller's terminal, never by a verb or a setting. Empty means agents stated on the node record at the controller's terminal, never by a verb or a config value. Empty means agents
run as the operator account. It cannot become root without a person, and the node-engine judges that on run as the operator account. It cannot become root without a person, and the node-engine judges that on
every look (ADR 0266). 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
@@ -238,15 +280,21 @@ and Health and repair. **Decided by** ADR 0100, 0126, 0176, 0181, 0207, 0221, 02
## Provisioning — one module serving another ## Provisioning — one module serving another
**Purpose.** To resolve which module serves a provision for which consumer, and to wire the two. **Purpose.** To resolve which module serves a provision for which consumer, to wire the two, and to
**Recorded by** the controller. **Upstream of** Data; downstream of Module, Placement and Identity. place every value a module needs into its files on its node. **Recorded by** the controller and
**Decided by** ADR 0027, 0084, 0138, 0225, 0230, 0263. `mesh-provisioner`. **Upstream of** Data; downstream of Module, Placement, Config and Identity.
**Uses:** module, seat, credential (Identity and access). **Decided by** ADR 0027, 0084, 0138, 0225, 0230, 0263, 0349.
**Uses:** module, seat, node-engine (Core), declaration (Placement), config value (Config), credential
and vault (Identity and access).
- **provision** — a service one module `provides` and others `require`; the mesh resolves a provider - **provision** — a service one module `provides` and others `require`; the mesh resolves a provider
and wires the two with an endpoint and a credential. A provision is a service you offer, a seat is a and wires the two with an endpoint and a credential. A provision is a service you offer, a seat is a
role you occupy, and the two meet where a seat delivers a provision: occupying the seat is what makes role you occupy, and the two meet where a seat delivers a provision: occupying the seat is what makes
a module *the* provider of it. a module *the* provider of it.
Every value a module needs is a provision from the domain that owns it: placement from the controller,
config values from `mesh-config`, shared secrets from the vault, a consumer's values from the module
providing it, an item of a collection from its contributor. A module says only what it needs and where
it lands (ADR 0349).
- **provider** and **consumer** — the module that provides a provision, and the module that requires - **provider** and **consumer** — the module that provides a provision, and the module that requires
it. Which of several providers serves a consumer is resolved it. Which of several providers serves a consumer is resolved
(ADR 0084). (ADR 0084).
@@ -264,27 +312,65 @@ and Health and repair. **Decided by** ADR 0100, 0126, 0176, 0181, 0207, 0221, 02
node seat `node-mounts`, as a mount unit and an automount unit, mounted on access (ADR 0263). node seat `node-mounts`, as a mount unit and an automount unit, mounted on access (ADR 0263).
- **occasional source** — a share on a device that is not a node and comes and goes, such as a USB disk; - **occasional source** — a share on a device that is not a node and comes and goes, such as a USB disk;
its absence is a state, *not present*, never a fault (ADR 0263). its absence is a state, *not present*, never a fault (ADR 0263).
- **mesh-provisioner** — the module that places provisions on the nodes: it holds the seat
`mesh-provisioner` once for the mesh, keeping which domain answers which kind of placeholder, what each
module on each node needs, what is applied where and the items of every collection, and the seat
`node-provisioner` on every node, which its provisioner client serves (ADR 0349).
- **provisioner client** — mesh-provisioner's part on one node. For each module there it reads the
placeholders of its template files, records the needs, watches each owner's values for the node, fills a
template only once every value is there, has the node-engine write the file through a narrow local verb,
makes the program take the change as the file declares (`restart-on`, `reload-on`, `read-on-use`), and
reports `prov.applied`. It runs as a service with a bus account of its own, never in the tool runner
(ADR 0349).
- **collection** — a provision with many providers. The **collector**, a module that takes additions from
others (a shell's snippets, a bar's blocks), requires it; each **contributor** ships its **item** as a
template in its own repository and provides it. The provisioner client fills the collector's template
from the items of the contributors placed on its node, and an item goes when its contributor leaves the
node (ADR 0349).
- **owned** — a placed file that is the mesh's whole: the provisioner writes it back whenever it differs.
It is one of three answers a manifest gives for each file it places (`owner-at-runtime`): **owned**,
**seed** or **managed**. Whichever it is, the file has one writer: the node-engine writes it only on the
provisioner client's word (ADR 0349).
- **seed** — a placed file the provisioner writes once; from then on it is the program's.
- **managed** — a placed file of which only its named keys or a marked block (a kept region, Module) are
the mesh's; the program keeps the rest.
## Identity and access — who may do what ## Identity and access — who may do what
**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, Change and delivery, and Operator and conversation. **Upstream of** Provisioning, Change and delivery, and Operator and conversation.
**Decided by** ADR 0085, 0113, 0183, 0225, 0234. **Decided by** ADR 0085, 0113, 0183, 0225, 0234, 0386, 0387, 0388.
**Uses:** module, node, operator, consumer (Provisioning). **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**
is the two ends of one credential, the consumer's and the provider's. is the two ends of one credential, the consumer's and the provider's.
- **secret** — a credential or other value the vault keeps sealed and the node-engine unseals into a - **secret** — a credential or other value the vault keeps sealed and the node-engine unseals into a
file at apply (ADR 0085). file at apply (ADR 0085).
- **secret family** — a module's own secrets declared as one name ending in `-*`; each member is the prefix - **secret family** — a module's own secrets declared as one name ending in `-*`; each member is the prefix
and a name (`smb-password-<source>`, one per source the module's settings name), given by its full name and a name (`smb-password-<source>`, one per source the module's config values name), given by its full name
and never made by the mesh and never made by the mesh
(ADR 0283). (ADR 0283).
- **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)).
- **grant** — what an identity may call or reach, bounded by the provisions it requires - **identity** — a person, an agent or a module as the mesh knows it: a name, a kind, a role, and whether it
(ADR 0225). is switched on. Every identity and role is held by the identity seat, in the bus's key-value store, and
changes only through its verbs (ADR 0386).
- **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)).
- **grant** — one line of a role: an effect everywhere, an effect on one seat, or one named tool, and may be
limited to named machines; a dangerous tool is granted only by name (ADR 0386). A module's role
is made from what its manifest declares it calls (ADR 0386 §4).
- **effect** — a tool's mark, set by its module in its manifest: **reads**, **acts** or **dangerous**; a
tool served without one counts as dangerous (ADR 0387).
- **break-glass agent** — the one agent that holds every tool and never asks: sealed until the operator, or
a condition the operator named, opens it, open until the operator closes it, and every act it takes told
at once (ADR 0387).
- **signed login** — a bus login signed by a key the bus trusts, carrying its expiry, which the bus checks by
its signature instead of reading a list (ADR 0388). The identity seat signs with one **signing key** per
role.
- **root key** — the key at the top of the signed logins, which vouches for the signing keys: sealed in the
vault and used only to replace them, on the operator's Approve (ADR 0388).
- **bus account** and **membership** — a module's or a node's account on the bus, and the subjects it - **bus account** and **membership** — a module's or a node's account on the bus, and the subjects it
is issued. The bus server calls its accounts *users*; the mesh says bus account. is issued. The bus server calls its accounts *users*; the mesh says bus account.
- **rotate** — replacing a credential without a consumer holding one the provider does not know. - **rotate** — replacing a credential without a consumer holding one the provider does not know.
@@ -364,6 +450,9 @@ planner, the rollout). **Downstream of** Module, Core, Health and repair and Dat
- **delivery group** — two or more deliveries sharing a pull request head branch name across the mesh's - **delivery group** — two or more deliveries sharing a pull request head branch name across the mesh's
repositories, delivered as one unit in an order declared (`after:`) or inferred by the planner. One repositories, delivered as one unit in an order declared (`after:`) or inferred by the planner. One
level: a group holds deliveries, never groups. Its state is derived from its members, never set. level: a group holds deliveries, never groups. Its state is derived from its members, never set.
- **release branch** — a branch other than `main`, shared by name across repositories, that collects a feature's
merged work and is brought into `main` as one delivery (design 57's `feat/57-config-and-provisioning`). While it
is in flight, a change that touches its files waits for it (playbook 07, *Is the coast clear?*).
- **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
@@ -422,7 +511,7 @@ becomes a message). **Decided by** ADR 0227, 0231, 0240, 0252, 0283.
running session, started before, does not have that group yet. Said by the node-engine as the module's running session, started before, does not have that group yet. Said by the node-engine as the module's
resource of kind `account`; it clears at the first look after a new login (ADR 0252). resource of kind `account`; it clears at the first look after a new login (ADR 0252).
- **waiting** — a resource's health when its module's tool check says it waits for the operator: a named own - **waiting** — a resource's health when its module's tool check says it waits for the operator: a named own
secret or setting of the module, not given yet, without which a part cannot work. Not a fault: the secret or config value of the module, not given yet, without which a part cannot work. Not a fault: the
controller checks it and says it as the condition `needs-operator`, naming the act, and the first-node gate controller checks it and says it as the condition `needs-operator`, naming the act, and the first-node gate
reads it as *waits for a person* reads it as *waits for a person*
(ADR 0283). (ADR 0283).
@@ -503,8 +592,8 @@ it should not. **Recorded by** the controller. **Upstream of** Provisioning and
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, Change and 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. asking, never the act. **Decided by** ADR 0152, 0175, 0234, 0259, 0272, 0383, 0384, 0385, 0390.
**Uses:** operator, person, proof (Identity and access), release (Change and 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`,
@@ -546,7 +635,7 @@ asking, never the act. **Decided by** ADR 0152, 0175, 0234, 0259, 0272.
published for that asker alone. The only thing an asker acts on (ADR 0259). published for that asker alone. The only thing an asker acts on (ADR 0259).
- **proposal** — a change an agent, or anyone the bus admits, puts forward through a verb and that takes - **proposal** — a change an agent, or anyone the bus admits, puts forward through a verb and that takes
effect only on the operator's warrant, bound to the exact thing shown: a shared instruction section (ADR effect only on the operator's warrant, bound to the exact thing shown: a shared instruction section (ADR
0274), a trusted setting's layer (ADR 0277). 0274), a trusted config value (ADR 0277, ADR 0348).
A proposal changes nothing by itself; declined, expired or refused, it is discarded. A proposal changes nothing by itself; declined, expired or refused, it is discarded.
- **approval mark** — a tool's or verb's declaration in its manifest (`needs-approval`) that each call to it runs - **approval mark** — a tool's or verb's declaration in its manifest (`needs-approval`) that each call to it runs
only on the operator's warrant for that one call, with its exact arguments; the tool runner that serves it, or only on the operator's warrant for that one call, with its exact arguments; the tool runner that serves it, or
@@ -559,11 +648,24 @@ asking, never the act. **Decided by** ADR 0152, 0175, 0234, 0259, 0272.
instructions. instructions.
- **reference** — an opaque token in a message's words standing for a detail the content rule keeps out - **reference** — an opaque token in a message's words standing for a detail the content rule keeps out
of them (a path, an address); opened with `detail` only on a `private` channel or through the mesh MCP server. of them (a path, an address); opened with `detail` only on a `private` channel or through the mesh MCP server.
- **agent** — a coding agent: the operator's, or one the mesh runs. Not the node-engine, and not the - **agent** — a coding agent: the operator's, or one the mesh runs; to the mesh, an identity of kind agent
(ADR 0386). Not the node-engine, and not the
build seat's holder, whose seat name `node-build-agent` is an identifier to rename. build seat's holder, whose seat name `node-build-agent` is an identifier to rename.
- **thread** — one line of agent work, owned by the mesh: its turns, tool calls and results, its workspace
and usage, kept whole for good on the bus by the thread seat; it outlives any session, node or front end
(ADR 0383, ADR 0384).
- **session** — one run of an agent harness on a thread.
- **agent harness** — the program that runs an agent's sessions, its context manager and loop: Claude Code
today (ADR 0384). Not the SDK harness (see [Homonyms](#homonyms)).
- **thread handler** — the node seat whose holder runs threads' sessions headless, on the always-on nodes
(ADR 0384).
- **workspace toolkit** — the tools a session works with on its thread's workspace, the same for every agent
harness: the node seat `workspace` (ADR 0385).
- **sandbox** — the small virtual machine one session runs in, with its thread's workspace mounted
(ADR 0390).
- **instruction section** — one named section of the managed instruction file, which every agent session on the - **instruction section** — one named section of the managed instruction file, which every agent session on the
nodes it is for reads first, as the mesh's word: set at the controller's terminal in the agent module's nodes it is for reads first, as the mesh's word: set at the controller's terminal in the agent module's
settings, or **proposed** by an agent and made active on each node only by the operator's warrant, which that node config, or **proposed** by an agent and made active on each node only by the operator's warrant, which that node
proves itself (ADR 0274). A hand-over between sessions is one. proves itself (ADR 0274). A hand-over between sessions is one.
- **personal rule** — one short text in the operator's own words about how the operator wants their agent - **personal rule** — one short text in the operator's own words about how the operator wants their agent
to work with them, registered for the operator (the **operator scope** of the agent module) and rendered to work with them, registered for the operator (the **operator scope** of the agent module) and rendered
@@ -578,10 +680,10 @@ asking, never the act. **Decided by** ADR 0152, 0175, 0234, 0259, 0272.
## Media — what a person watches, listens to and reads ## Media — what a person watches, listens to and reads
**Purpose.** To find, fetch and file copies of the works a person watches, listens to and reads, each in **Purpose.** To find, fetch and file copies of the works a person watches, listens to and reads, each in
the library its kind names. **Recorded by** the `media-importer` module's settings and its memory of what the library its kind names. **Recorded by** the `media-importer` module's config values and its memory of what
it was told, and the servarr apps' own libraries. **Upstream of** nothing. **Decided by** ADR 0295, 0296, it was told, and the servarr apps' own libraries. **Upstream of** nothing. **Decided by** ADR 0295, 0296,
0302. 0302.
**Uses:** module, seat, setting (Module). **Uses:** module, seat, config value (Config).
- **content** — a work a person watches, listens to or reads: a film, an album and its tracks, an - **content** — a work a person watches, listens to or reads: a film, an album and its tracks, an
episode, a book, a session of a sport event. It is known by an identity from a catalogue outside the episode, a book, a session of a sport event. It is known by an identity from a catalogue outside the
@@ -592,7 +694,7 @@ it was told, and the servarr apps' own libraries. **Upstream of** nothing. **Dec
- **media release** — one copy of a work an engine can fetch, such as an NZB or a video. Downloaded, it - **media release** — one copy of a work an engine can fetch, such as an NZB or a video. Downloaded, it
sits in the downloads folder, not yet filed. ADR 0302 says *release*. sits in the downloads folder, not yet filed. ADR 0302 says *release*.
- **library** — one folder of one kind, filed by one importer: the media-importer's, named in its - **library** — one folder of one kind, filed by one importer: the media-importer's, named in its
settings, or a servarr app's. Several libraries may hold one kind, as TV Shows and Anime both hold config, or a servarr app's. Several libraries may hold one kind, as TV Shows and Anime both hold
series. series.
- **media file** — a file on disk together with its metadata: its kind, the fields that identify the - **media file** — a file on disk together with its metadata: its kind, the fields that identify the
work, and what part of the work the file is. It is what the media-importer takes. work, and what part of the work the file is. It is what the media-importer takes.
@@ -624,7 +726,7 @@ Not a domain of the mesh but of the way it is built, listed because its words me
`changed`, `superseded`): a later record may change one clause and leave the rest `changed`, `superseded`): a later record may change one clause and leave the rest
(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** — the role that 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), takes them as proposals, has them reviewed and accepted, typed links, clauses and reasoning, with every version), 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
@@ -634,7 +736,7 @@ Not a domain of the mesh but of the way it is built, listed because its words me
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.
An **incident** is a past event that taught a rule. An **incident** is a past event that taught a rule.
- **issue tracker** — the role that holds the mesh's issues and hands out their numbers: the seat `issue-tracker`, - **issue tracker** — what holds the mesh's issues and hands out their numbers: the seat `issue-tracker`,
held by the module `mesh-issues` ([53 — The issue tracker](../03-DESIGN/01-to-be/53-the-issue-tracker.md)). held by the module `mesh-issues` ([53 — The issue tracker](../03-DESIGN/01-to-be/53-the-issue-tracker.md)).
Not hits's *item*; bare *ticket* is a homonym (see [Homonyms](#homonyms)). Not hits's *item*; bare *ticket* is a homonym (see [Homonyms](#homonyms)).
*Not:* ~~ticketing system~~ (hq), ~~ticket system~~ (hq) *Not:* ~~ticketing system~~ (hq), ~~ticket system~~ (hq)
@@ -682,12 +784,18 @@ renaming one sense, the old sense moves to a *Not:* line and becomes mechanical.
| release | a person letting a held delivery go on | **release** | Change and delivery | | release | a person letting a held delivery go on | **release** | Change and delivery |
| | 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 |
| | a branch brought into `main` as one delivery | **release branch** | Change and delivery |
| gate | the judgement on a delivery's first node | **first-node gate** | Change and delivery | | gate | the judgement on a delivery's first node | **first-node gate** | Change and delivery |
| | the pull request status | **merge gate** | Change and delivery | | | the pull request status | **merge gate** | Change and 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** | Change and 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 |
| | the program that runs an agent's sessions | **agent harness** | Operator and conversation |
| | the TypeScript `provisioner` helper for database providers | **`provisioner`**, renamed by design 57 | Provisioning |
| 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 |
| 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** | Change and 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 |
@@ -712,6 +820,10 @@ renaming one sense, the old sense moves to a *Not:* line and becomes mechanical.
| | 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** | Change and 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 |
| | 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* | — |
| trusted | a config value that decides what runs or who can reach what | **trusted** config value | Config |
| | a sender on the operator's own list | **trusted** sender, its message an **operator message** | Operator and conversation |
## How this page is kept ## How this page is kept
+26 -1
View File
@@ -1,9 +1,10 @@
--- ---
status: canonical status: canonical
updated: 2026-10-11 updated: 2026-10-16
derives: the constitution page derives: the constitution page
decisions: decisions:
- "ADR 0020" - "ADR 0020"
- "ADR 0391"
--- ---
# How we build # How we build
@@ -172,6 +173,30 @@ quietly stop being true, and nobody will learn that from a document.
Not test-driven development as a blanket rule — a test written first against undiscovered Not test-driven development as a blanket rule — a test written first against undiscovered
behaviour asserts a guess. The obligation is that every decision has a defender. behaviour asserts a guess. The obligation is that every decision has a defender.
### Tests run at the lowest level that proves them, within a time budget
Three levels, by how much real system a test uses (ADR 0391):
| Level | Uses | Budget | Runs |
|---|---|---|---|
| **Unit** | one piece of logic alone: no database, network, bus, running service or other process | well under a second per test | locally, the tests of what changed, while working; and in the forge's checks |
| **Integration** | the real thing beside it: a database, the bus, a catalogue tree | the repository's whole suite in seconds, never over sixty | only in the forge's checks, once per push |
| **End to end** | the whole mesh, or a lab mesh, from the outside | — | only when the operator asks |
- **Test each behaviour at the lowest level that can prove it.** A defect found higher up gets a test at the
lowest level that reproduces it, before its fix.
- **A test that needs longer than a second, or a database, the network or a service, is not a unit test.**
- **A suite over sixty seconds is a defect with its own issue**: it is made faster or split, not accepted.
- **Never run a merge check, a throwaway database or an integration suite locally.** The forge's checks do,
on the builder.
- **A report names its tests and how long they took**, so a test over its budget is seen when it is added.
### Tests within a time budget — how it was earned
On 2026-10-16 a feature's fixing agents ran the catalogue's whole merge check (about ten minutes), a
throwaway database and a locally built gate during local development. Each round cost tens of minutes and
caught neither real failure; the forge's checks found both.
### A report is read from the system, never from what asked for it ### A report is read from the system, never from what asked for it
The same rule as the two above, pointed at reporting rather than at verification. **Anything The same rule as the two above, pointed at reporting rather than at verification. **Anything
+43 -8
View File
@@ -30,6 +30,15 @@ An idea arrives in one of two forms, and the first step differs:
2. **Ask the operator to set it up.** A few questions, one at a time, each answerable with a choice and 2. **Ask the operator to set it up.** A few questions, one at a time, each answerable with a choice and
carrying a recommendation: what comes first, what is out of scope, which options must be tried, what carrying a recommendation: what comes first, what is out of scope, which options must be tried, what
done looks like. Three to five questions is usual; the answers are the effort's first findings. done looks like. Three to five questions is usual; the answers are the effort's first findings.
- **Check every option against the mesh's own rules before offering it.** An option that breaks one
(the core values in [`mission.md`](../mission.md), among them *the mesh owns the truth* and
*sovereignty: external dependencies are deliberate and few*; the rules in
[`how-we-build.md`](../how-we-build.md); and everything between modules over the bus, research 036) is
dropped, or offered only with the rule it breaks named. A recommendation the operator has to argue back
costs a version.
- **Explain each question with a concrete picture** in the operator's words: what they would see, do or
lose under each option. No internal names (a record number, a verb, a word the glossary lacks) without
what it is beside it.
3. **Take a number.** `knowledge-base.reserve` with `kind: research` and `for`: the effort's title. The number 3. **Take a number.** `knowledge-base.reserve` with `kind: research` and `for`: the effort's title. The number
is spent whether or not the effort is ever proposed; one never proposed is withdrawn after a week. Never is spent whether or not the effort is ever proposed; one never proposed is withdrawn after a week. Never
pick one by hand. pick one by hand.
@@ -48,10 +57,32 @@ An idea arrives in one of two forms, and the first step differs:
lists them and gives the difference between two. Until issue 844 is delivered, the knowledge base lists them and gives the difference between two. Until issue 844 is delivered, the knowledge base
takes a new version of an accepted, active effort only as a progressive insight: give `insight`, saying takes a new version of an accepted, active effort only as a progressive insight: give `insight`, saying
what the effort held before the new finding, and mark the finding in the text with its date. what the effort held before the new finding, and mark the finding in the text with its date.
6. **Settle the direction with the operator.** When the questions are answered, write `05-direction.md`: - **Propose a version per settled set, not per answer.** While the operator answers a run of questions,
the proposed decisions in a few lines each, the options rejected and why, what is left open. Ask the or while experiments run, keep the answers and findings in the session's save point and propose once
operator to confirm or change it, one point at a time. Then the effort graduates through playbook when the set is settled. Each version is reviewed and resent whole, so a version per answer multiplies
[02](02-graduation.md). the reviews.
- **Pre-check before proposing**, against what the review sends back most: every record, issue or design
the text names is in `links`; work put off names the issue that tracks it (an acceptance test is not
put-off work: it is what the build proves, step 7); each figure is given as measured (a range, not a
rounded claim); no word on a glossary *Not:* line.
- **Try at the cheapest level that answers the question.** A program's own behaviour (a library, a
server, a protocol) is tried in process, on the machine's loopback, in minutes. Lab runs (about 12 to
16 minutes fresh) are for the mesh's own code; before that code exists, a lab run tests nothing an
in-process test does not, and its lab scenario is written as the build's acceptance test instead.
6. **Place it in the domains.** Before the direction, write `03-domains.md`: the domain or domains the idea
belongs to (the glossary's sections, ADR 0244); the modules and seats it lands in, each new, changed or
retired; what it does to each domain's words, each word added (with its domain and a one-line meaning),
changed or retired, and none on a *Not:* line or already meaning something else (the glossary's homonyms);
and whether a domain grows or shrinks, a concept moving between domains included. The direction names its
decisions in those words. Asked when the idea is set up, too: an option that lands a concept in the wrong
domain is offered with that cost named, like one that breaks a rule.
7. **Settle the direction with the operator.** When the questions are answered, write `05-direction.md`:
the proposed decisions in a few lines each, the options rejected and why, and the build's acceptance
tests: what the build must prove before it is done, carried by its milestones (playbook
[04](04-build-handoff.md)). Nothing else is left open: each question closes by a test, by a decision the
effort records with its reason (a small design limit is the lead's), or by the operator. Ask the
operator to confirm or change the direction, one point at a time, then propose it once. Then the effort
graduates through playbook [02](02-graduation.md).
## Bounds and models ## Bounds and models
@@ -60,7 +91,8 @@ An idea arrives in one of two forms, and the first step differs:
| reading and the setup questions (1, 2) | 30 minutes | the session's own, larger | | reading and the setup questions (1, 2) | 30 minutes | the session's own, larger |
| proposing (3, 4) | 15 minutes | larger | | proposing (3, 4) | 15 minutes | larger |
| each experiment or reading task (5) | 30 minutes | smaller, one agent at a time unless the operator allows more | | each experiment or reading task (5) | 30 minutes | smaller, one agent at a time unless the operator allows more |
| the direction (6) | 30 minutes | larger | | placing it in the domains (6) | 15 minutes | larger |
| the direction (7) | 30 minutes | larger |
A step past its bound is said to the operator at once, with what is being done about it. The lead reports A step past its bound is said to the operator at once, with what is being done about it. The lead reports
as playbook 07 does: every ten minutes and at once when something is found, stops or needs the operator, as playbook 07 does: every ten minutes and at once when something is found, stops or needs the operator,
@@ -100,6 +132,9 @@ and why, is the more expensive half to rediscover.
The rules on an effort are the knowledge base's: `propose` refuses one that breaks a rule that judges one The rules on an effort are the knowledge base's: `propose` refuses one that breaks a rule that judges one
record (the module's table test holds one case per rule), and the rules that judge the whole record set run record (the module's table test holds one case per rule), and the rules that judge the whole record set run
after every acceptance and raise a condition when one fails. That this repository holds no effort is after every acceptance and raise a condition when one fails. The habits of steps 2, 5, 6 and 7 (options checked
`records.py`'s `no-records` check, on every pull request. An active effort with no new version for seven against the rules, one version per settled set, the cheapest level, nothing left open) are the lead's, read by
days is the smell of research left behind: its lead closes or continues it. the reviewer; of the pre-check, `propose` refuses a link to nothing and a retired word, and the review returns
a record named but not linked, an untracked deferral and a figure not as measured. That this repository holds
no effort is `records.py`'s `no-records` check, on every pull request. An active effort with no new version
for seven days is the smell of research left behind: its lead closes or continues it.
+25 -3
View File
@@ -13,7 +13,7 @@ clauses in force for a topic or a record).
## Graduating research ## Graduating research
The effort's `05-direction.md`, confirmed by the operator (playbook [01](01-research.md), step 6), is what The effort's `05-direction.md`, confirmed by the operator (playbook [01](01-research.md), step 7), is what
graduates. The lead writes the records and the design on the larger model; it is one sitting, bounded at graduates. The lead writes the records and the design on the larger model; it is one sitting, bounded at
about an hour, and said to the operator if it runs past. about an hour, and said to the operator if it runs past.
@@ -23,7 +23,20 @@ about an hour, and said to the operator if it runs past.
say which, in writing, before proceeding. say which, in writing, before proceeding.
2. **Record the decisions** (below, *Writing a decision record*): one record per choice that could be 2. **Record the decisions** (below, *Writing a decision record*): one record per choice that could be
reversed on its own, not one record for the whole effort. reversed on its own, not one record for the whole effort.
3. **Write the design.** Create the document under `03-DESIGN/01-to-be/` with frontmatter: - **Write them together, not one after another.** Reserve every number first, then propose each record
as soon as it is written, without waiting for the review of the one before; the reviews run side by
side, and the operator's asks for the records that need them arrive together. A record that
`rests-on` or `extends` a sibling is proposed once that sibling is accepted.
- **Take the clauses from the confirmed direction** and cite the effort (`cites`, `"research NNN"`) for
the evidence instead of retyping its findings: a record's Context holds the few facts its decision
rests on, with the effort named for the rest.
- **Pre-check each record before proposing it**, as for research (playbook [01](01-research.md), step
5): every record, issue or design it names is in `links`; each deferral names its issue; each figure
is given as measured; no word on a glossary *Not:* line.
3. **Write the design.** **One design per thing** (the operator, 2026-10-16): when a to-be design already
covers what the decisions change, they rewrite that design, as *Amending an existing design* below says,
and no second design is written beside it that refers to it. Only for something no design covers, create
the document under `03-DESIGN/01-to-be/` with frontmatter:
```yaml ```yaml
--- ---
@@ -36,6 +49,13 @@ about an hour, and said to the operator if it runs past.
--- ---
``` ```
The design lists the direction's acceptance tests: what the build proves before it is done.
The words of the effort's `03-domains.md` go into the glossary in the same change, each in its domain: a
word added gets its entry, a word whose meaning changed gets its entry rewritten, and a retired word moves to
its replacement's *Not:* line. A word that already means something else in another domain is named as a
homonym with the qualified form each domain uses.
4. **Hand it to the work breakdown.** The design goes on through playbook [04](04-build-handoff.md): 4. **Hand it to the work breakdown.** The design goes on through playbook [04](04-build-handoff.md):
milestones, issues, and each milestone built as one feature step (playbook [07](07-feature-branches.md)). milestones, issues, and each milestone built as one feature step (playbook [07](07-feature-branches.md)).
5. **Close the effort.** `knowledge-base.propose` the effort again, whole, with `status: graduated` and a 5. **Close the effort.** `knowledge-base.propose` the effort again, whole, with `status: graduated` and a
@@ -141,4 +161,6 @@ record (its module's table test holds one case per rule), and the four rules tha
changes, an accepted record extending a superseded one) run after every acceptance and raise a condition changes, an accepted record extending a superseded one) run after every acceptance and raise a condition
when one fails. In this repository, `records.py` checks that a design's `decisions:` entries and every when one fails. In this repository, `records.py` checks that a design's `decisions:` entries and every
"ADR NNNN" in the text are well formed, and that no record is written here; `index.py` checks the reading "ADR NNNN" in the text are well formed, and that no record is written here; `index.py` checks the reading
order's six topics, which the knowledge base reads. 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,
a deferral no issue tracks and a retired word, and the review returns a record named but not linked.
+7 -6
View File
@@ -28,10 +28,11 @@ A design becomes **milestones**, and each milestone becomes **issues**:
there; if it is not, write it before changing it. Building against an undocumented as-is is how a shipped there; if it is not, write it before changing it. Building against an undocumented as-is is how a shipped
behaviour gets lost. behaviour gets lost.
4. **Break it into milestones.** Write them into the design, in a section *Milestones*: for each, its number, 4. **Break it into milestones.** Write them into the design, in a section *Milestones*: for each, its number,
its one-sentence outcome, the repositories it touches, and what it depends on. Order them so each is its one-sentence outcome, the acceptance tests of the design it proves, the repositories it touches, and
usable as soon as it is delivered, the smallest useful one first. what it depends on. Order them so each is usable as soon as it is delivered, the smallest useful one
5. **Ask the operator to confirm the milestones**, in a short table: number, outcome, repositories, the first.
parts each repository builds. Change what they change. 5. **Ask the operator to confirm the milestones**, in a short table: number, outcome, the acceptance tests it
proves, repositories, the parts each repository builds. Change what they change.
6. **Open the issues.** For each confirmed milestone, `issue-tracker.open` one issue per repository part, its 6. **Open the issues.** For each confirmed milestone, `issue-tracker.open` one issue per repository part, its
title the symptom or capability in plain words, its report naming the design, the milestone and the title the symptom or capability in plain words, its report naming the design, the milestone and the
decisions it builds with; link the order with `depends-on`. Write the issue numbers into the milestone's decisions it builds with; link the order with `depends-on`. Write the issue numbers into the milestone's
@@ -61,5 +62,5 @@ Read by the lead and the reviewer today; a check in hq's merge-check is tracked
issues; an in-progress design without one is the smell of building without a breakdown. issues; an in-progress design without one is the smell of building without a breakdown.
- Every issue a milestone names exists in the tracker (`issue-tracker.get`), and an issue in `fixing` names - Every issue a milestone names exists in the tracker (`issue-tracker.get`), and an issue in `fixing` names
its milestone in its report. its milestone in its report.
- A milestone whose issues are all `resolved` has had its delivery plan done; its outcome sentence is proven on - A milestone whose issues are all `resolved` has had its delivery plan done; its outcome sentence and
the mesh before the next milestone starts. acceptance tests are proven on the mesh before the next milestone starts.
+73 -9
View File
@@ -21,6 +21,16 @@ A small fix (one repository, one issue, about an hour of work) follows
undelivered for a day: an issue done at 21:09 was still not on the machines at 15:00 the next day. undelivered for a day: an issue done at 21:09 was still not on the machines at 15:00 the next day.
- **Work that waits and nobody notices.** Most lost time was not work at all, but a check, a delivery plan - **Work that waits and nobody notices.** Most lost time was not work at all, but a check, a delivery plan
or an agent waiting on something that had stopped, unseen. or an agent waiting on something that had stopped, unseen.
- **Review that keeps hardening.** One fix went through seven review rounds in five and a half hours, each
asking for more hardening than its issue asked (issue 704, 2026-10-14). A round is for what makes the
change wrong; everything else is an issue of its own.
- **The first check after the whole branch is written.** A feature's catalogue branch, pushed once after
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.
- **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).
- **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.
## The shape ## The shape
@@ -61,7 +71,8 @@ never into the running one, unless it blocks the running one.
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`), a throwaway database server if the store tests need one. the home (`TMPDIR`), and the unit tests to run (never a database server: tests that need one are
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. **Move the issues to `fixing`**, `by` naming the feature's agent for that repository. 5. **Move the issues to `fixing`**, `by` naming the feature's agent for that repository.
@@ -72,7 +83,8 @@ never into the running one, unless it blocks the running one.
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 issue's first test fails before its change and passes after; `gofmt`, `go vet` - **Unit tests only, of what changed, each well under a second** (how-we-build §5, ADR 0391). Each issue's
first test fails before its change and passes after; `gofmt`, `go vet`
and the repository's unit tests stay green. The integration tests (`merge-check.sh` with the store and the and the repository's unit tests stay green. The integration tests (`merge-check.sh` with the store and the
bus) wait for phase 3. bus) wait for phase 3.
- **Nothing is pushed** and no pull request is opened in this phase. Never land on a red check: run the - **Nothing is pushed** and no pull request is opened in this phase. Never land on a red check: run the
@@ -102,13 +114,40 @@ never into the running one, unless it blocks the running one.
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. **The reviewer reads the answers and the new commits**, and approves or reports again. From round 2 it 4. **The reviewer reads the answers and the new commits**, and approves or reports again (step 5 says what a round
asks changes only for a finding that makes the feature wrong; smaller points become issues of their own. may ask). After round 3 the lead stops and brings the operator what is left, with a recommendation.
After round 3 the lead stops and brings the operator what is left, with a recommendation. 5. **A finding outside the issues' done-when 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
report, and the round does not wait for it. What a round asks is only what makes the change wrong against
its issues and its decisions.
## Is the coast clear?
Before the push of phase 3 (and of a small fix, playbook [08](08-small-fixes.md) step 5), the lead checks, for every
repository the change touches, whether a **release branch** is in flight there: a branch other than `main` that
collects a feature's merged work to bring it into `main` as one delivery.
1. **Look for one.** A pull request into `main` whose head is a release branch (`gitea.gitea_open_pulls`), a
release branch holding merged work that `main` does not have yet (`git log origin/main..origin/<branch>`), or
a delivery plan of that release branch still running (`mesh-controller.plans`).
2. **Compare the files.** `git diff --name-only origin/main...origin/<release branch>` against the files the
change touches.
3. **Overlap: hold.** If the release branch touches a file the change touches, nothing is pushed into that repository
until the release branch's delivery plan is done. Then the feature branch is brought up to the new `main`, the unit
tests run again, and the push follows. The hold is on the wait list (its bound: the release branch's delivery plan),
and is said to the operator with what the change waits for.
4. **No overlap: go on.** A release branch in another repository, or one that shares no file with the change, does not
hold it; its merge joins the merge window as any other (ADR 0276).
Merging beside a release branch still in flight would make its lead resolve someone else's conflicts in the
middle of it, and could send part of the change with its delivery (2026-10-16: the file module's
controller part touched four files feature 57's release branch also changed).
## Phase 3 — Check (no review) ## Phase 3 — Check (no review)
1. **Push every feature branch and open one pull request per repository, together.** Pull requests in two or 1. **Push every feature branch and open one pull request per repository, together, once the coast is clear**
(above). The integration tests
run only in the merge check (the operator, 2026-10-16), never before. 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. reviewer first confirms that commit's tree equals the reviewed one.
@@ -116,8 +155,15 @@ never into the running one, unless it blocks the running one.
second review: phase 2 reviewed this tree. second review: phase 2 reviewed this tree.
3. **A check that fails** goes back to the reviewer with the failure, for **one more cycle** of phase 2 with 3. **A check that fails** goes back to the reviewer with the failure, for **one more cycle** of phase 2 with
the same fixing agents; then push once again. the same fixing agents; then push once again.
4. **Mark each pull request ready** (`mesh/ready` and a comment `ready at <full sha>`) once the operator's word 4. **The step's acceptance is proven before it is marked ready.** A step that adds or changes a module
for the feature is given; mesh-delivery merges it when its checks pass (ADR 0364). carries an integration test that starts the module whole, its process against the real bus and store, and
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).
5. **The reviewer marks each pull request ready as soon as the acceptance is shown** with
`mesh-delivery.mark-ready` (the delivery of the head it approved, who reviewed it and why), which writes the
label `mesh/ready` and then `ready at <full sha>`, the order the forge counts (issue 913); mesh-delivery merges it when its checks pass
(ADR 0364). The operator's word is waited for only when the step 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.
**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;
@@ -125,7 +171,8 @@ then `mesh-delivery.recheck` the catalogue's and merge it on the new verdict. Pl
## Phase 4 — Deliver ## Phase 4 — Deliver
The merge starts the delivery plan: build, publish, send. The lead follows it to done 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
(`mesh-controller.plans` with `wait` and `until: done`), then resolves the feature's issues with `fixed-by` (`mesh-controller.plans` with `wait` and `until: done`), then resolves the feature's issues with `fixed-by`
naming its pull requests, and removes what is left: no `feat/<slug>` branch and no `.work/<slug>` worktree naming its pull requests, and removes what is left: no `feat/<slug>` branch and no `.work/<slug>` worktree
survive. survive.
@@ -154,6 +201,8 @@ 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 ready mark | 15 minutes after the acceptance is shown | the reviewer marks it, or the lead does |
| a release branch in flight that shares files | its delivery plan done | report it every ten minutes; bring the branch up to `main` after |
| a merge after ready | 5 minutes after the checks pass | 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` |
@@ -198,6 +247,13 @@ lead prepares a `/clear` as the mesh's instruction "prepare before clear" says:
feature's phase, 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.
## Issues that stop
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
`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.
## How it is checked ## How it is checked
- 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
@@ -207,6 +263,14 @@ next action; then the prompt the operator pastes. The context files make the fea
- 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.
- More than one open pull request in a repository that share no feature name, or a feature step whose issues - More than one open pull request in a repository that share no feature name, or a feature step 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, 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.
- A pull request reviewed and green but not marked ready for more than 15 minutes is a lead that did not
mark it.
- A pull request opened into a repository whose release branch was in flight and shared a file with it is the failure of
*Is the coast clear?*; the lead's report names the check it made before the push.
- A step that adds or changes a module marked ready with no integration test that starts the module whole is
the failure of phase 3's fourth step.
- 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.
+36 -8
View File
@@ -2,7 +2,10 @@
**Trigger.** One issue in one repository, small enough to build in about an hour: a bug with a known cause, **Trigger.** One issue in one repository, small enough to build in about an hour: a bug with a known cause,
a missing check, a wording, a playbook or design edit. Anything larger, or anything that needs a change in a a missing check, a wording, a playbook or design edit. Anything larger, or anything that needs a change in a
second repository first, is a feature and follows [playbook 07](07-feature-branches.md). second repository first, is a feature and follows [playbook 07](07-feature-branches.md). A feature is the
larger kind: it touches several modules and has a research effort or a design before it. **Needing a decision
does not make a fix a feature**: the decision is settled first (step 1) and the fix stays small (the operator,
2026-10-16).
**Related issues in one fix.** A small fix may cover two or three related issues when they are in the same **Related issues in one fix.** A small fix may cover two or three related issues when they are in the same
repository, mostly in the same module, and together still fit in about an hour: they touch the same files, or repository, mostly in the same module, and together still fit in about an hour: they touch the same files, or
@@ -23,29 +26,49 @@ one delivery**. Everything before the push is local and fast.
1. **Read the issue and the code it names** (the session's own model, or one read-only agent on the smaller 1. **Read the issue and the code it names** (the session's own model, or one read-only agent on the smaller
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 decision or a second repository stops here and becomes a feature. 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.
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`. Write the 3. **Build on a local branch** `fix/<issue>-<slug>`, in a worktree of its own branched from `main`. Write the
test that fails today first; make the change; keep `gofmt`, `go vet` and the repository's unit tests green. test that fails today first; make the change; keep `gofmt`, `go vet` and the unit tests of what changed green
(each 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, 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). The reviewer's findings go back to the same agent that wrote the fix, continued by model; at most 3 rounds, as playbook 07; from round 2 only what is wrong; a finding outside the issue's
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
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 word on the pull request is its review. operator's word on the pull request is its review, and the lead marks it ready on that word.
5. **Push once and open the pull request.** The forge's merge checks run the integration tests. A failed check 5. **Push once and open the pull request, once the coast is clear** (playbook [07](07-feature-branches.md), *Is
the coast clear?*: no release branch in flight in the repository that shares a file with the fix). The forge's merge
check runs the integration tests, the only
place they run (the operator, 2026-10-16). A failed 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. **Mark it ready** (`mesh/ready` and `ready at <full sha>`) once the operator's word is given; mesh-delivery 6. **The reviewer marks it ready at once** (for an edit that skipped the reviewer, the lead, on the
merges it when its checks pass. operator's word) with `mesh-delivery.mark-ready` (the delivery of the head it approved, who reviewed it and
why), which writes the label `mesh/ready` and then `ready at <full sha>`, the order the forge counts (issue 913);
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,
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
the mesh (the symptom gone, measured), and resolve the issue with `located-in` and `fixed-by` the same hour. the mesh (the symptom gone, measured), and resolve the issue with `located-in` and `fixed-by` the same hour.
A fix that stops before its delivery moves its issue back to `open` with a note saying how far it got
(branch, last commit, what is left), as playbook [07](07-feature-branches.md) says.
**A second fault found by the delivery.** When a fix's delivery shows a second fault that keeps the issue's
done-when from holding, and that fault is in the same files, it is taken into the same issue as a second pull
request with a review round of its own. A fault in other code is opened as an issue of its own, and the first
issue waits for it (issue 937: mesh-catalog #335 was taken in; issue 943 was opened).
## Bounds ## Bounds
| Waiting on | Bound | Past the bound | | Waiting on | Bound | Past the bound |
|---|---|---| |---|---|---|
| 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 merge after ready | 5 minutes after the checks pass | read `mesh-delivery.show` for the delivery | | the merge after ready | 5 minutes after the checks pass | 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` |
@@ -62,3 +85,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 reviewed and green but unmarked
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, is the failure of steps 4 to 6.
- 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.
+1 -1
View File
@@ -34,7 +34,7 @@ one-for-one — `mesh-catalog`, `mesh-sdk` and `mesh-tools` exist where the tabl
| `mesh-foundation` | 1 | the four pinned services, as declarations | | `mesh-foundation` | 1 | the four pinned services, as declarations |
| `mesh-controller` | 2 | **exists.** The controller and its contexts — one of seven built (ADR 0006) | | `mesh-controller` | 2 | **exists.** The controller and its contexts — one of seven built (ADR 0006) |
| `mesh-surfaces` | 3 | tools, web, cli | | `mesh-surfaces` | 3 | tools, web, cli |
| `mesh-sdk` | — | the stable spine modules build against — the tool-serving 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. |
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
@@ -0,0 +1,41 @@
---
layer: as-is
status: implemented
code: [mesh-controller, mesh-tools]
updated: 2026-10-16
decisions:
- "ADR 0106"
---
# Logins on the bus, as they run
**The bus reads one static list of logins from its settings file, in one account, written by the controller from
its own records. Every module has a login per assignment; a person's login may call only the tools it was given;
the sessions on the operator's machines call through their machine's tool-runner login, which may call every tool.
Nothing marks what a tool does.** Read from the controller's and the tool runner's code on main on 2026-10-15
(research 053, question 8). Design 58 replaces it.
## The list
- **One list, one account.** The controller writes every login from its records into the bus's settings file;
the bus re-reads it without dropping connections (ADR 0106 §3).
- **A module's login per assignment**, named by machine and module, may publish and subscribe only what its
manifest declares it emits, consumes, serves and calls.
- **A person's login** may publish only to the tools it was given and listen only on its own inbox.
- **The caller is in the address.** A call's address carries the caller's name, so the runtime takes the caller
from the address, never from the message. For a seat every machine holds, the address also names the machine.
## What it cannot do
- **No expiry.** A login lives until its record is deleted and the list rewritten on the bus's node.
- **No per-agent login.** Agent sessions reach the bus through each machine's tool-runner login, which may call
every tool on the mesh; what one session did cannot be told from another's.
- **No effect.** No tool or seat verb declares whether it reads, acts or is dangerous; some module documents say
it in prose, and nothing reads that.
- **Asks and warrants exist** (ADR 0259): a module can have one act authorised by the operator, bound to that
act; nothing yet asks for a dangerous call by itself.
## How it is checked
By reading: the controller's code that renders the bus's settings and the tool runner's caller check, on main.
When design 58's switch is done, this document is replaced by the as-is of signed logins.
+1
View File
@@ -24,6 +24,7 @@ Where the two disagree, the implementation wins and the disagreement is stated.
| [`13-the-console.md`](13-the-console.md) | The mesh's tools on the machine a person sits at, served by a module the mesh assigned there | | [`13-the-console.md`](13-the-console.md) | The mesh's tools on the machine a person sits at, served by a module the mesh assigned there |
| [`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 |
## What these documents are not ## What these documents are not
@@ -2,8 +2,9 @@
layer: to-be layer: to-be
status: in-progress status: in-progress
code: [mesh-host, mesh-controller, mesh-catalog] code: [mesh-host, mesh-controller, mesh-catalog]
updated: 2026-10-12 updated: 2026-10-16
decisions: decisions:
- "ADR 0394"
- "ADR 0310" - "ADR 0310"
- "ADR 0268" - "ADR 0268"
- "ADR 0252" - "ADR 0252"
@@ -90,6 +91,7 @@ WP5 assign and prove (operator-gated) needs WP
WP6 a module puts the account in a group (mesh-host, mesh-controller, mesh-catalog) issue 247 WP6 a module puts the account in a group (mesh-host, mesh-controller, mesh-catalog) issue 247
WP7 the control-node withholds execute (mesh-controller, mesh-catalog) ADR 0268 WP7 the control-node withholds execute (mesh-controller, mesh-catalog) ADR 0268
WP8 a node's files and accounts are read (mesh-sdk, mesh-catalog, mesh-controller) ADR 0310, issue 418 WP8 a node's files and accounts are read (mesh-sdk, mesh-catalog, mesh-controller) ADR 0310, issue 418
WP9 every file on a node is managed (mesh-catalog, mesh-controller) ADR 0394, issue 900
``` ```
WP1, WP2 and WP4 are independent, and are built in parallel on one feature branch per repository WP1, WP2 and WP4 are independent, and are built in parallel on one feature branch per repository
@@ -341,6 +343,10 @@ out, the control-node's value is set to `withhold` and zsh is pushed to it. Then
*`mesh-sdk`, `mesh-catalog`, `mesh-controller`. ADR 0310, extending ADR 0268; tracked by issue 418.* *`mesh-sdk`, `mesh-catalog`, `mesh-controller`. ADR 0310, extending ADR 0268; tracked by issue 418.*
**Replaced by WP9.** ADR 0394 supersedes ADR 0310: `node-files` takes over every read below, with the same
bounds, content rule and refusal of secret places; once `node-files` is held on every node, the next delivery
removes `node-reads` and `mesh-reads`.
ADR 0268 §5 has narrow read tools take the common reads over from `execute`, so that an approval is asked for an ADR 0268 §5 has narrow read tools take the common reads over from `execute`, so that an approval is asked for an
act and not for a read, and ADR 0286 §44 sets the approval mark on `execute` only once those reads have a tool on act and not for a read, and ADR 0286 §44 sets the approval mark on `execute` only once those reads have a tool on
every node. Two had none: reading or searching a file, and looking up an account or a group. every node. Two had none: reading or searching a file, and looking up an account or a group.
@@ -366,3 +372,38 @@ under the same issue.
`mesh-sdk` test of the content rule and one in each of its users; `mesh_search` for `cat`, `grep` and `mesh-sdk` test of the content rule and one in each of its users; `mesh_search` for `cat`, `grep` and
`getent passwd` finding the verbs. Live, the kernel's RAID status file is read through the seat on the home `getent passwd` finding the verbs. Live, the kernel's RAID status file is read through the seat on the home
server, and the agent's credentials file on the laptop is refused. server, and the agent's credentials file on the laptop is refused.
## WP9 — Every file on a node is managed through `node-files`
*`mesh-catalog`, `mesh-controller`. ADR 0394; tracked by issue 900.*
`node-reads` only reads, and as an account that cannot open a person's home, so nothing in the mesh could copy,
move, write or delete a file, and a cleanup needed a shell on the machine. Most files belong to no module: homes,
project folders, downloads, logs, scratch.
- **A node seat, `node-files`, on every node**, held by a module of its own, `mesh-files`, beside `mesh-reads`
in the catalogue; the controller holds the seat's definition beside the other node seats. It has a verb for each file action: list,
stat, readlink, read, search, write, copy, move, delete, make a folder, change mode, change owner, account and group
lookup, and the trash's list and restore. Each says what it replaces (ADR 0245): `ls`, `stat`, `readlink`, `cat`,
`grep`, `cp`, `mv`, `rm`, `mkdir`, `chmod`, `chown`, `getent passwd`, `getent group`, `id`, and `ssh <node> cp` for a
copy between nodes.
- **It may touch every file.** It declares no account of its own: the tool runner starts it as the node's
operator account, which has sudo without a password, and it uses `sudo -n` only where that account's own rights
do not reach. It keeps each file's owner and modes as well as it can: a copy or a move keeps them, a new file
takes its folder's owner. Which files to touch is the caller's responsibility; it judges no permission.
- **Nothing is lost at once.** A file deleted or overwritten goes to the node's trash with who asked and why, is
restorable for 7 days, and is emptied after.
- **A path a module owns is refused**, naming the module and its tool, unless the call says `override` with a
reason; an override is recorded and said to the operator.
- **It replaces `node-reads`.** Its reads keep WP8's bounds, content rule and kernel reads. Every node is
assigned `mesh-reads`, so `node-reads` and `mesh-reads` are removed by the next delivery, once `mesh-files`
holds `node-files` on every node.
- **No Approve is asked**; every act is recorded with its caller. Read and search never return the content
of a known credential file or a secret place (the mesh never prints a token); copying or moving one is
allowed.
**Proof.** The module's tests: each verb against a temporary tree keeps owner and modes, a new file takes its
folder's owner, a delete is restorable from the trash and gone after 7 days, an owned path is refused and an
override recorded, a credential file's content is never returned. Live: the operator's `~/.claude` on a workstation is listed, a file in `/etc` is read through sudo, and a file copied from one node to another
arrives with its owner and modes.
@@ -2,8 +2,10 @@
layer: to-be layer: to-be
status: in-progress status: in-progress
code: [mesh-controller, mesh-catalog modules/mesh-delivery] code: [mesh-controller, mesh-catalog modules/mesh-delivery]
updated: 2026-10-13 updated: 2026-10-16
decisions: decisions:
- "ADR 0395"
- "ADR 0382"
- "ADR 0350" - "ADR 0350"
- "ADR 0335" - "ADR 0335"
- "ADR 0323" - "ADR 0323"
@@ -102,7 +104,9 @@ allows.
at the moment the build seat clones it, the **trunk commit**, and the verdict names both. When another merge at the moment the build seat clones it, the **trunk commit**, and the verdict names both. When another merge
moves the trunk, a `ready` delivery goes back to `proposed` on the observed event `trunk moved`, with both moves the trunk, a `ready` delivery goes back to `proposed` on the observed event `trunk moved`, with both
commits in the transition's reason; a `rejected` one stays `rejected`, its statuses saying the verdict is out commits in the transition's reason; a `rejected` one stays `rejected`, its statuses saying the verdict is out
of date. Nothing is asked for it: a recheck, a new head or the merge tool asks the check again. The row from of date. The move itself asks the check again of every active open pull request into that trunk that merges
cleanly (ADR 0395, *A move of the trunk asks every check again* below); a recheck, a new head or the merge tool
asks it too. The row from
`proposed` to `checked` takes only a verdict that names the trunk's head as its trunk commit, so a verdict for `proposed` to `checked` takes only a verdict that names the trunk's head as its trunk commit, so a verdict for
an older trunk is kept and shown and does not count, and the rows reach a fixed point. Checked by an older trunk is kept and shown and does not count, and the rows reach a fixed point. Checked by
mesh-delivery's `TestATrunkMoveSendsAReadyDeliveryBackToProposed` and the table's own rollout. mesh-delivery's `TestATrunkMoveSendsAReadyDeliveryBackToProposed` and the table's own rollout.
@@ -123,7 +127,7 @@ What this leaves as it is, said so nobody reads more into it:
- **A check that was asked and never answers** is listed by `stalled` an hour after the ask, and is asked again - **A check that was asked and never answers** is listed by `stalled` an hour after the ask, and is asked again
with `mesh-delivery.recheck`, which the merge tool's refusal names. with `mesh-delivery.recheck`, which the merge tool's refusal names.
The transitions are those of ADR 0239 decision 2, ADR 0270 and ADR 0323. A delivery that is The transitions are those of ADR 0239 decision 2, ADR 0270, ADR 0323 and ADR 0395. A delivery that is
merged while not `ready` goes to `held`. Its check did not pass, and only a person decides that it goes on. A merged while not `ready` goes to `held`. Its check did not pass, and only a person decides that it goes on. A
delivery merged with no rollout answering it within ten minutes is held too, with the true reason, and listed by delivery merged with no rollout answering it within ten minutes is held too, with the true reason, and listed by
`stalled` at once. Nothing waits silently. `stalled` at once. Nothing waits silently.
@@ -371,6 +375,12 @@ only once it is built.
group's members puts the composed check out of date: the group reads `checking` with the reason, and the group's members puts the composed check out of date: the group reads `checking` with the reason, and the
composed check runs again once a member is judged again. Checked by mesh-delivery's composed check runs again once a member is judged again. Checked by mesh-delivery's
`TestAGroupsComposedCheckGoesOutOfDateOnAMergeThatIsNotItsMember` and `TestARebasedMemberIsComposedAgainNotHeld`. `TestAGroupsComposedCheckGoesOutOfDateOnAMergeThatIsNotItsMember` and `TestARebasedMemberIsComposedAgainNotHeld`.
- **Each member's own check reads its siblings** (ADR 0382): the group's other open members into a base of
the same name are read beside a member's own check at their head merged onto their base, and a mesh-controller
sibling's merge judges it, so changes that are only consistent together pass their own checks. The controller
learns the siblings from mesh-delivery's `groups`; the verdict names each one. A sibling's new head, close or
base move puts that verdict out of date, and the merge tool merges such a member only when
`mesh/delivery-group` passed at the heads read and every sibling head read is reviewed.
- **Delivering**: in order. A member waits in `published` until the one before is `delivered` or `covered`. - **Delivering**: in order. A member waits in `published` until the one before is `delivered` or `covered`.
A member published while its group's composed check is asked and not answered waits there for the answer; A member published while its group's composed check is asked and not answered waits there for the answer;
one held because that check had not passed is *lifted* back to `published` once it passes or warns for the one held because that check had not passed is *lifted* back to `published` once it passes or warns for the
@@ -382,6 +392,34 @@ only once it is built.
A group's state is derived from its members and its composed check, and is shown, never stored apart from A group's state is derived from its members and its composed check, and is shown, never stored apart from
them. them.
## A move of the trunk asks every check again
*ADR 0395, changing ADR 0323 §4.* When the forge's holder hears a repository's trunk move, mesh-delivery asks the
check of every active open pull request into that trunk against its new head at once, so a verdict is current by
the time anyone looks.
- **A conflict is told, not checked.** Before asking a check, mesh-delivery asks the forge whether the head merges
cleanly into the new head, which needs no builder. When it does not, no check is asked: its statuses fail naming the trunk commit and the files in
conflict where the forge gives them, and it is told once on the pull request, not again while the same conflict
stands. Every later trunk move and its next push ask the forge again; once it merges cleanly its check is asked.
- **One check per pull request.** When a check against a newer trunk commit is asked for a pull request, its check
against an older trunk commit is dropped if it waits and ended if it runs, said in the build log and on the
queue, as a new head already ends its old head's check (issue 726). An ended check delivers no verdict, so none of its builds is reused (ADR 0350 §7 keeps only builds a verdict
names); its scratch records wait for ADR 0350 §5's seven-day sweep. A try and a delivery's build are never touched.
- **The queue's order.** Delivery builds first; then checks of pull requests marked ready; then the rest, the most
recently pushed first.
- **Inactive pull requests are left out.** One with no push and no ready mark for 7 days is not asked again on a
trunk move; its statuses say out of date and that a push, a ready mark or a recheck asks its check. A recheck
takes a delivery that is still proposed and whose verdict is out of date, and asks its check (issue 888).
- **Groups.** For a group's members a move asks only each member's own check; the group's composed check runs again
through those rechecks (ADR 0323 §6) and is never asked by the move itself. A move made by a member merging in
the group's order asks the other members nothing, since they merge moments later; their verdicts are out of date
until then, and one that does not merge is asked again at the next move, its next push or a recheck.
**Its cost** is at most one check asked per active, cleanly merging pull request per trunk move: about 550 a day at
ADR 0323's measured load, fewer once conflicting and inactive ones are left out; ending an older check saves builder
time, not checks asked. Issue 944 measures it once built.
## What every transition does ## What every transition does
In this order, each by its owner: In this order, each by its owner:
@@ -413,7 +451,7 @@ In this order, each by its owner:
| `what-if` | the delivery plan a diffset would have, asked of the controller's planner, kept nowhere | | `what-if` | the delivery plan a diffset would have, asked of the controller's planner, kept nowhere |
| `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, and every waiting rollout no live delivery holds | | `stalled` | every delivery held past its bound, with the transition H2 may take, and every waiting rollout no live delivery holds |
| `recheck` | a rejected or ready delivery back to proposed, with why | | `recheck` | a rejected or ready delivery back to proposed, with why; a delivery still proposed whose verdict is out of date has its check asked (ADR 0395) |
| `release` | a held delivery to delivering, with why: a person's word | | `release` | a held delivery to delivering, with why: a person's word |
| `stop` | any delivery not final to stopped, with why; its rollout is ended through the controller. On a rollout that carries other deliveries it first answers what ending the rollout would take down, and acts on `way`: `again` or `walk` (ADR 0299) | | `stop` | any delivery not final to stopped, with why; its rollout is ended through the controller. On a rollout that carries other deliveries it first answers what ending the rollout would take down, and acts on `way`: `again` or `walk` (ADR 0299) |
| `close` | H2's only verb: the transition the table names for a stalled delivery, refused otherwise | | `close` | H2's only verb: the transition the table names for a stalled delivery, refused otherwise |
@@ -0,0 +1,188 @@
---
layer: to-be
status: designed
code: [mesh-catalog, mesh-media-catalog, mesh-sdk, mesh-tools, mesh-controller, mesh-host, mesh-lab]
updated: 2026-10-16
decisions:
- "ADR 0383"
- "ADR 0384"
- "ADR 0385"
- "ADR 0386"
- "ADR 0387"
- "ADR 0388"
- "ADR 0389"
- "ADR 0390"
---
# 58 — Agent work as the mesh's own
**An agent's work belongs to the mesh, not to one vendor's program or one machine. A thread is kept whole on the
bus by a thread seat; a thread handler on an always-on node runs the agent harness headless for it, in a sandbox
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
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.**
(ADR 0383 to ADR 0390, from research 053.)
**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
nothing lost; the agent's calls outside its role are refused, saying which grant is missing.
## 1. The parts
```
any node always-on nodes the control-node
───────── ─────────────── ────────────────
front ends nox-thread-handler nox-threads (seat thread)
├ terminal client (first) ├ takes a thread's lease ├ a restricted stream per thread
├ web page (then) ├ runs the agent harness headless ├ the object store for large content
└ Telegram: questions, │ inside a microsandbox └ leases in the bus's key-value store
approvals ├ answers permission requests
│ │ through the mesh's asks identity seat
│ └ commits + pushes the workspace ├ persons, agents, modules, roles
│ branch every turn │ in the bus's key-value store
│ nox-workspace (node seat workspace) ├ signs logins (one key per role)
│ └ read, glob, search, edit, write, └ revokes over the bus, reads back
│ git, named tools; no command runner
│ claude-code
│ ├ mirrors a session into its thread
│ ├ rebuilds a session on pick-up
│ └ writes the login and licence token
│ into the sandbox
└──────────────── every call over the bus, carrying its thread and,
for a module acting for an agent, the agent's token
```
## 2. Threads (ADR 0383)
A **thread** is one piece of agent work, kept as a stream on the bus in the mesh's own format. The stream is the
source of truth: the conversation, the agent's questions and their answers, tool calls and results, usage, and
every change of state. Large content goes to the object store and is named from the stream. The stream is
restricted (ADR 0376): only modules granted it read it. A thread is never deleted; the trail is kept for good.
A thread is **open**, **assigned** (to a node and an agent harness), **released** or **stopped**. Its seat's verbs create,
assign, release, stop, move it, and take it over with a reason. The assignment is a **lease** in the bus's
key-value store, changed only by a compare-and-set on its revision. Its holder renews it every 20 s. A lease not
renewed for 60 s is said to the operator; nothing takes it over by itself.
## 3. Running a thread (ADR 0384)
The thread handler on an always-on node takes the lease and starts the agent harness (Claude Code today) headless, one
turn at a time. It answers the agent harness's permission requests through the mesh's asks, so a front end answers them in
place. At the end of every turn it commits and pushes the workspace, which is the thread's own branch on the forge;
a node that picks the thread up fetches it, so a move loses at most the turn in progress. Before a shutdown it
releases at a turn boundary; on a stop it ends the process group, since a tool's child process outlives the agent harness.
The claude-code module mirrors the session's stream into the thread and, on pick-up, rebuilds a session from the
thread. Usage per thread comes from each run's result and rate-limit messages on the stream, not the transcript, and the
licence manager records it with the thread as its session.
The front ends are clients of a thread doing the same work: the terminal client first, then the web page, and
Telegram for questions and approvals. Their first scope is the conversation as it streams, the agent's questions and
permission requests answered in place, sending a message and stopping a turn. The rest is tracked by issue 850.
## 4. The workspace toolkit (ADR 0385)
The node seat `workspace`, held by `nox-workspace` on every thread-handler node and served through the mesh's MCP
server, works in the thread's workspace only, by relative path: read, glob, regex search, edit, write, git, and named
tools such as running the repository's tests and its merge check. It is built early. There is no general command
runner in it, and no module offers one to an agent: a command an agent needs becomes a named tool in the module that
owns it. Claude Code's own built-in tools, its command tool included, are turned off once the toolkit is in.
## 5. Identities and roles (ADR 0386)
The identity seat, held once for the mesh, owns every **identity** (a person, an agent or a module) and every
**role**, in the bus's key-value store. An identity has a name, a kind, a role and whether it is switched on. They
change only through the seat's verbs, each recorded with who asked; a module or front end whose role grants the
seat's reads may watch the store live, and it holds no secret. Only the
operator creates a person or an agent, or changes its role. A module's identity is asked for by the controller when
the module is assigned to a node, named by machine and module, with a role made from what its manifest declares it
calls.
A role holds **grants**. A grant is an effect everywhere, an effect on one seat, or one named tool, and may be
limited to named machines. Two fences check them: the bus, coarsely, by seat and module, from the role's signing
key; and the tool runner, exactly, tool by tool, in the SDK. Every call carries its thread; a module acting for an
agent carries a token the identity seat signed for that agent, and the tool runner then checks the agent's role. A
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.
## 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:
**reads**, **acts** or **dangerous**. The catalogue's check refuses an unmarked tool, once every module already in
the catalogue is marked. A dangerous tool is granted only by name, and naming one in a role goes to the operator as
an ask first. Every other dangerous act is an ask (ADR 0259), bound to that act and carried out on the warrant.
The **break-glass** agent holds every tool and never asks. It is sealed until the operator opens it, or a condition
the operator has named as opening it is raised (none is named at the start); it stays open until the operator closes
it, and every act it takes is said to the operator at once. A tool served without an effect, from a module built
before its mark, counts as dangerous.
## 7. Signed logins on the bus (ADR 0388)
The bus checks each login by its signature and reads no list. The root key is sealed in the vault and used only to
replace the identity seat's signing keys, on the operator's Approve. The seat signs with one scoped signing key per
role, so every login stays small and each identity gets its own inbox from the role's template. An agent's login
lives minutes, a module's and a node's hours; each is renewed by a reconnect forced before expiry, by the handler or
the node-engine.
A switched-off identity is revoked over the bus. The seat reads back every update, sends updates at least a second
apart, and resends the current state to every server id it has not seen on the system account, which every joined
server carries. This changes ADR 0106's rule that the bus is configured only through its settings file.
## 8. The switch (ADR 0389)
A new bus with signed logins runs beside today's, joined as a leaf node carrying the system account. Identities move
one at a time, each checked before the next. Streams, key-value buckets and the object store move last, each by a
copy of its whole configuration: copy, wait for equal counts, stop its writers, then one update hands the addresses
over. Today's bus is switched off when nothing is left on it.
## 9. Sandboxes (ADR 0390)
Each session runs in a microsandbox of its own, its version pinned and declared in a module. The workspace is
mounted; the claude-code module writes in the agent's login and the licence token without its refresh part, and
replaces the token as the licence manager refreshes it. Connections are allowed only to the vendor's interface, by
address range, and to the bus. A node whose KVM modules are not loaded gets them from the sandbox module.
## 10. The build's acceptance tests
The build is not done until each of these has been shown, and the milestones of the work breakdown (playbook 04)
carry them:
1. A licence token replaced under a running session keeps that session working.
2. The sandbox module loads KVM on the home server, and a sandbox runs there.
3. Every existing module's tools carry an effect, and the catalogue's check refuses an unmarked one.
4. The switch, run on the mesh's own modules in the lab from the bed test kept on the lab's branch
`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.
## 11. Milestones
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
[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). The issues are opened once the operator confirms
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
881, 5 is 882, 6 is 883, 7 is 884, 8 is 885, 9 is 886, 10 is 887. A milestone's issue is split into one issue per
repository part when its feature step starts (playbook 07): several of the parts are modules that do not exist yet,
and the issue tracker takes only registered owners. The building waits until the delivery under way of
[57 — Building config and provisioning](57-building-config-and-provisioning.md) is done; the status here stays
`designed` until milestone 1 starts.
| # | Outcome, in one sentence | Proves | Repositories and their parts | Depends on |
|---|---|---|---|---|
| 1 | Every tool on the mesh says whether it reads, acts or is dangerous, a tool's description shows it, and the catalogue refuses a module that leaves one unmarked | test 3 | mesh-sdk: the effect in a tool's declaration, an unmarked tool served as dangerous; mesh-controller: the manifest field; mesh-catalog and mesh-media-catalog: every module marked, the check | — |
| 2 | A session works on a repository's workspace through the mesh's tools alone: read, glob, search, edit, write, git, its tests and its merge check, with no command runner | — | mesh-catalog: the module `nox-workspace` on the node seat `workspace`, on the always-on nodes; mesh-controller: the seat | 1 |
| 3 | A thread created through the thread seat runs headless on an always-on node, its conversation readable through the seat, its usage counted per thread, and a move to another node loses at most the turn in progress | — | mesh-catalog: `nox-threads` (seat `thread`, restricted streams, leases), `nox-thread-handler` (seat `thread-handler`), claude-code (mirror and rebuild), claude-licence-manager (usage per thread) | 2 |
| 4 | The operator starts, follows, answers and stops a thread from a terminal client, and answers its questions on Telegram | — | mesh-catalog: the terminal client, a Go program of `nox-threads`; claude-code: questions and permission requests through the asks | 3 |
| 5 | Every session runs in a sandbox of its own that reaches only the vendor and the bus, and the home server runs one too | tests 1 and 2 | mesh-catalog: the sandbox module (microsandbox pinned, KVM loaded where off); `nox-thread-handler`: runs sessions in it; claude-code: writes the login and the licence token without its refresh part | 3 |
| 6 | Every person, agent and module is an identity with a role, and an agent's call outside its role is refused, saying which grant is missing | — | mesh-catalog: the identity seat's module; mesh-sdk: the tool runner's exact check, calls carrying thread and agent token; mesh-controller: an identity asked for each assignment; mesh-tools: sessions call as a person or an agent, never through the tool runner's login | 1 |
| 7 | A dangerous act not named in a role comes to the operator as an ask, and the sealed break-glass agent opens only on the operator's word or a named condition | test 5 | mesh-sdk: dangerous calls asked; identity module: dangerous tools granted by name on an ask, break-glass and its telling | 6, 5, 4 |
| 8 | The bus checks signed logins on a second bus beside today's, the identity seat signs and revokes them, and the switch, run in the lab on the mesh's own modules, loses nothing | test 4 | identity module: signing keys, renewal, revocation read back and resent to new servers; the node-engine (`mesh-host`): node and module logins renewed; mesh-controller: the second bus as a leaf with the system account; mesh-lab: the bed test from the branch `research/053-signed-bus-logins` | 6 |
| 9 | Every identity is on the new bus, its data moved by copy, and today's bus is switched off | — | mesh-controller and the identity module: one identity at a time, then streams, buckets and the object store; mesh-catalog: the old bus's module retired | 8 |
| 10 | The operator follows and answers threads from the web page as from the terminal client, logged in as a person identity | — | mesh-catalog: the web page, a client of the thread seat over the bus's WebSocket listener | 9, 4 |
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.
+1
View File
@@ -57,6 +57,7 @@ document is written and this one's status becomes `implemented`.
| [`55-the-knowledge-base.md`](55-the-knowledge-base.md) | **Designed.** The mesh keeps its decision records and research efforts whole in one module, `mesh-knowledge`, on the control-node: number, status, topic, typed links both ways, each decision's clauses with their own status, and the reasoning, with every version; a record is proposed, checked at the write, reviewed by the knowledge base's own judgement and accepted there, with the operator's Approve when it needs it; agents ask what holds in one bounded call | ADR 0290 | | [`55-the-knowledge-base.md`](55-the-knowledge-base.md) | **Designed.** The mesh keeps its decision records and research efforts whole in one module, `mesh-knowledge`, on the control-node: number, status, topic, typed links both ways, each decision's clauses with their own status, and the reasoning, with every version; a record is proposed, checked at the write, reviewed by the knowledge base's own judgement and accepted there, with the operator's Approve when it needs it; agents ask what holds in one bounded call | ADR 0290 |
| [`56-the-mail-client-set-up-by-the-mesh.md`](56-the-mail-client-set-up-by-the-mesh.md) | **Designed.** On every node with a display server, the `betterbird` module installs Betterbird from a pinned upstream release mirrored into the artifact store, ships the Owl add-on pinned and installed by Betterbird's enterprise policy, and declares the operator's mail accounts in a trusted setting without credentials; each account signs in once at a person's first use, and one the profile already holds is adopted by its address | ADR 0316, ADR 0317 | | [`56-the-mail-client-set-up-by-the-mesh.md`](56-the-mail-client-set-up-by-the-mesh.md) | **Designed.** On every node with a display server, the `betterbird` module installs Betterbird from a pinned upstream release mirrored into the artifact store, ships the Owl add-on pinned and installed by Betterbird's enterprise policy, and declares the operator's mail accounts in a trusted setting without credentials; each account signs in once at a person's first use, and one the profile already holds is adopted by its address | ADR 0316, ADR 0317 |
| [`57-building-config-and-provisioning.md`](57-building-config-and-provisioning.md) | **Designed.** The work of ADR 0348 and ADR 0349 as 37 packages, each an issue with its proof: foundations, template files, config in its own module, the provisioner and its client, secrets in the vault, bindings from providers, collections, a module's runtime readers, and removing the old forms; at most five worked at once | ADR 0348, ADR 0349 | | [`57-building-config-and-provisioning.md`](57-building-config-and-provisioning.md) | **Designed.** The work of ADR 0348 and ADR 0349 as 37 packages, each an issue with its proof: foundations, template files, config in its own module, the provisioner and its client, secrets in the vault, bindings from providers, collections, a module's runtime readers, and removing the old forms; at most five worked at once | ADR 0348, ADR 0349 |
| [`58-agent-work-as-the-meshs-own.md`](58-agent-work-as-the-meshs-own.md) | **Designed.** Agent work as the mesh's own: a thread seat keeps every thread whole on the bus; a thread handler runs the harness headless in a sandbox per session, the workspace on the thread's own forge branch; a workspace toolkit and named tools, no general command runner; one identity seat for persons, agents and modules, roles of grants, every tool an effect, signed bus logins, and the switch to them one identity at a time | ADR 0383, ADR 0384, ADR 0385, ADR 0386, ADR 0387, ADR 0388, ADR 0389, ADR 0390 |
## Not yet written ## Not yet written
+6
View File
@@ -0,0 +1,6 @@
# Release 57: config and provisioning
This branch carries the work of design 57 (ADR 0348, ADR 0349) in this repository. Each package
merges here through its own reviewed pull request; the branch reaches main as one pull request, in one
delivery group with the branch of the same name in mesh-controller, mesh-host, mesh-sdk, mesh-catalog
and hq, after a rehearsal in the lab. This file is deleted by the last commit before that pull request.