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