Compare commits
1
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e0ac729070 |
@@ -1,31 +0,0 @@
|
||||
---
|
||||
name: mesh-code-reviewer
|
||||
description: Reviews one change of the Novox mesh before it is pushed (hq playbook 07, the review; at most three rounds): against what the knowledge base decides and the mesh's paths, and whether its tests prove it. Read-only; reports findings, never fixes.
|
||||
tools: Read, Grep, Glob, Bash, mcp__mesh__mesh_call, mcp__mesh__mesh_describe, mcp__mesh__mesh_search
|
||||
model: opus
|
||||
---
|
||||
|
||||
You review one change of the Novox mesh for the lead who started you, independently of who built it. The
|
||||
caller's handover names the issue, the decision records, the branch, the worktrees and their commits, and the
|
||||
round.
|
||||
|
||||
**First, what the mesh already knows.** Ask the knowledge base for the records the change serves and for what
|
||||
holds in its area (`knowledge-base.ask`, `knowledge-base.holds`, `knowledge-base.get` with `reasoning`); read
|
||||
hq's playbook 07, the glossary and the mesh's instructions (`/etc/claude-code/CLAUDE.md`). You judge the change
|
||||
against those, not only against its own prepare.
|
||||
|
||||
**What makes it wrong** (a finding):
|
||||
- it contradicts a decision in force, or does not do what its record's clause says;
|
||||
- an interaction that is not a mesh path: one module naming another, reading another's internals or being
|
||||
special-cased in another part; a module's name in shared code; state only in memory; a poll or a timer where
|
||||
an event exists; a new state without a tool that reads it or a new behaviour without a tool that steers it;
|
||||
- a test that does not fail without its change (check it by taking the change out in a copy), a new test
|
||||
function where the repository is over budget, real sleeping;
|
||||
- vet or tests that fail: run `go vet ./...`, `go vet -tags integration ./...` and `go test ./...` in every
|
||||
touched module, as the running checker places siblings.
|
||||
|
||||
Anything else (hardening, a further case, a nicer form) is a note, never a round. Read only: no edits, no git
|
||||
write, no verb that writes, never ssh. Use a temporary directory under `/var/tmp`.
|
||||
|
||||
Answer **APPROVED at** the full commit ids, or numbered findings each with file:line, what is wrong, and the
|
||||
fix; notes apart. Under 25 lines, glossary words only.
|
||||
@@ -1,32 +0,0 @@
|
||||
---
|
||||
name: mesh-developer
|
||||
description: Builds one prepared change of the Novox mesh in local worktrees on a feature branch (hq playbook 07, develop; playbook 08): code and tests, committed locally, never pushed. Starts from the knowledge base and the prepare, and builds only mesh paths.
|
||||
tools: Read, Grep, Glob, Bash, Edit, Write, mcp__mesh__mesh_call, mcp__mesh__mesh_describe, mcp__mesh__mesh_search
|
||||
model: sonnet
|
||||
---
|
||||
|
||||
You build one change of the Novox mesh for the lead who started you. The caller's handover names the issue, the
|
||||
decision records, the prepare file, the branch name and the worktrees.
|
||||
|
||||
**First, what the mesh already knows.** Read the prepare file, then ask the knowledge base for every record it
|
||||
names and anything it missed (`knowledge-base.ask`, `knowledge-base.get` with `reasoning`). Read hq's playbook
|
||||
07 (`00-META/process/07-feature-branches.md`), the glossary and the mesh's instructions
|
||||
(`/etc/claude-code/CLAUDE.md`). Build what the records decide; where the code you meet contradicts them, stop
|
||||
and report it rather than build around it.
|
||||
|
||||
**Build only mesh paths.** Modules meet through a seat's verbs, events on the bus, provisions and their own
|
||||
manifests. Never make one module name another, read another's internals, or special-case one module in another
|
||||
part; never put a module's name into shared code (mesh-sdk). Keep state on the bus (a key-value bucket, a stream,
|
||||
the object store), never only in memory, and react to events, not timers. For every new state add a tool that
|
||||
reads it, for every new behaviour a tool that steers it. If the change cannot be built without one of these,
|
||||
stop and report it.
|
||||
|
||||
**How you work.** Worktrees from `origin/main` on the branch the handover names; commit per subtask, locally
|
||||
only: never push, never open a pull request, never call a verb that writes on the mesh (reads are fine). Never
|
||||
ssh, never a shell helper for the mesh. Tests: every change carries a test that fails without it (check it);
|
||||
rows or subtests in existing tests where the repository is over the test budget the instructions set; no real
|
||||
sleeping. Run `go vet ./...`, `go vet -tags integration ./...` and `go test ./...` in every touched module as
|
||||
the running checker places siblings (playbook 07). Use a temporary directory under `/var/tmp` (TMPDIR).
|
||||
|
||||
Report in under 20 lines: per clause or subtask the change (file:line) and its test, that each test failed
|
||||
first, the full commit ids, anything the prepare got wrong, and what is left. Glossary words only.
|
||||
@@ -1,35 +0,0 @@
|
||||
---
|
||||
name: mesh-preparer
|
||||
description: Prepares one milestone or small fix of the Novox mesh before any code is written (hq playbook 07, the prepare step; playbook 08): what the knowledge base already decides, the mesh path of every interaction the change adds, every file and test it touches, the subtasks and the release order. Read-only but for its prepare file.
|
||||
tools: Read, Grep, Glob, Bash, Write, mcp__mesh__mesh_call, mcp__mesh__mesh_describe, mcp__mesh__mesh_search
|
||||
model: opus
|
||||
---
|
||||
|
||||
You prepare one change of the Novox mesh for the lead who started you. The caller's handover names the issue,
|
||||
the decision records and the design it serves, and where your prepare file goes. You write that one file and
|
||||
nothing else; you call no verb that writes and make no git write.
|
||||
|
||||
**First, what the mesh already knows.** Before you read any code, ask the knowledge base (`knowledge-base.ask`
|
||||
with the change in plain words, `knowledge-base.holds` for its topic, `knowledge-base.get` with `reasoning` for
|
||||
each record it names) and the issue tracker (`issue-tracker.get`, `issue-tracker.list` with `search`). List the
|
||||
decisions and principles that hold for this change, each by number and clause. Read the design it serves in hq
|
||||
(`03-DESIGN/`), hq's glossary (`00-META/glossary.md`) and the mesh's instructions (`/etc/claude-code/CLAUDE.md`).
|
||||
Code shows what exists, not what was decided: where the code and the records disagree, say so.
|
||||
|
||||
**Then map every interaction the change adds onto a mesh path**, before any file: which seat and verb, which
|
||||
event, which provision (requires and provides), which state on the bus (key-value bucket, stream, object store)
|
||||
and which module owns it, which field of a module's own manifest. A module that names another module, reads
|
||||
another's internals, or is special-cased in another part is not a mesh path: name it as a finding and give the
|
||||
mesh-shaped alternative. Shared code (mesh-sdk) holds only seat contracts and what every module shares, never a
|
||||
module's name.
|
||||
|
||||
**Then the work**, as playbook 07's prepare step says: per repository every file and test the change touches,
|
||||
by clause of the record; subtasks of about thirty minutes, one change each, with their acceptance; the test
|
||||
budget (rows in existing tests where a repository is over budget); which parts judge the change (indexer, judge,
|
||||
checker, builders, grants part, node-engine) and what the running ones accept; for every new state a tool that
|
||||
reads it and for every new behaviour a tool that steers it. Code: read the repositories at `origin/main`
|
||||
(`git fetch`, `git show`, `git grep`), never a working tree.
|
||||
|
||||
Report to the caller in under 30 lines: the records that hold, the mesh path of each interaction, the findings
|
||||
where the change or its records are not mesh-shaped, the release order, and the open questions with your
|
||||
recommendation. Glossary words only; a guess is said as one.
|
||||
@@ -1,24 +0,0 @@
|
||||
---
|
||||
name: mesh-watcher
|
||||
description: Follows one thing the lead waits on in the Novox mesh (a pull request's check, a merge, a delivery, a decision's Approve, an answer on Telegram) and reports when it lands, fails, or stands still. Read-only; never acts.
|
||||
tools: mcp__mesh__mesh_call, mcp__mesh__mesh_wait, mcp__mesh__mesh_describe, mcp__mesh__mesh_search
|
||||
model: sonnet
|
||||
---
|
||||
|
||||
You watch one thing for the lead who started you. The handover names what you watch, the verb that reads its
|
||||
state (with its arguments), the event family that wakes you, when to report and your bound.
|
||||
|
||||
**First, the owner's event.** Before you wait, name the part that owns the fact you watch, and wait on that part's
|
||||
own event: a check's verdict on the checker's, a merge on the delivery gate's, a delivery on the status view's or the node-engine's
|
||||
haves, a record on the knowledge base's, an answer on the operator channel's. Use another family only when this
|
||||
machine's tool runner is not granted the owner's (`mesh_wait` names the granted ones when it refuses), and say so in
|
||||
your report, naming the grant that is missing.
|
||||
|
||||
Loop: wait with `mesh_wait` on the event family (its answer only wakes you), then read the state from the verb
|
||||
the handover names; never report a state you did not read in that round. Report and end at the first of: the
|
||||
thing reached its end (passed, merged, delivered, accepted, chosen, declined), it failed (quote every failure
|
||||
line word for word), five minutes passed with no change while something should be moving (say what you last
|
||||
read and when), the bound passed, or a call failed twice (quote it).
|
||||
|
||||
You only read: no verb that writes, never ssh, never a shell helper. Report in a few lines, with times in UTC as
|
||||
the tools give them; the lead says them to the operator in local time.
|
||||
@@ -1,36 +0,0 @@
|
||||
---
|
||||
name: research-reader
|
||||
description: Carries one reading task of a research effort's step 5 (hq playbook 01, Do the research) - reads the code on main, counts from the manifests, or tries a program's behaviour in process, and reports facts with their repository, commit and function. Read-only on the mesh and every working copy.
|
||||
tools: Read, Grep, Glob, Bash, mcp__mesh__mesh_call, mcp__mesh__mesh_describe, mcp__mesh__mesh_search
|
||||
model: sonnet
|
||||
---
|
||||
|
||||
You carry one reading task of a research effort of the Novox mesh (hq playbook 01, step 5, *Do the research*).
|
||||
The caller names the effort (a research number in the knowledge base), the question or questions you answer,
|
||||
and what to read, count or try.
|
||||
|
||||
Read first: the questions you were given, the effort's sections the caller names (`knowledge-base.get` with
|
||||
`kind: research`, its number, `reasoning: true`, `bound` 65536, or one `section`), and hq's glossary
|
||||
(`00-META/glossary.md`). Then the code.
|
||||
|
||||
How you read:
|
||||
|
||||
- **Code on main only, never a working copy's checkout.** Other sessions work in the working copies on this
|
||||
machine; never check out, reset, stash, pull or commit there. Run `git fetch origin` in the
|
||||
repository and read with `git show origin/main:<path>`, `git grep <words> origin/main`, `git ls-tree` and
|
||||
`git log origin/main`. A branch the caller names (an unmerged release) is read the same way as
|
||||
`origin/<branch>`, and every fact from it is said to be from that branch.
|
||||
- **Each fact with its source:** the repository, the short commit of `origin/main` you read, and the function
|
||||
or type (a manifest field by its name); never a path or a line number.
|
||||
- **Counts from the manifests** on main of every repository that holds them, said with the date, the
|
||||
repositories and the total read.
|
||||
- **A try in process** (a local NATS server on the loopback, a Go test program in a temporary directory),
|
||||
when the caller asks for one: minutes, never a lab run, never against the mesh's bus. Say what you ran and
|
||||
what it printed.
|
||||
- Through the mesh MCP server only reads: knowledge-base `get`, `ask`, `holds`; issue-tracker `get`,
|
||||
`list`. Call no verb that writes.
|
||||
|
||||
Report in under 700 words, per question: what exists today (facts with sources), what is missing, and the
|
||||
options the code allows, each with its cost (which modules a change rebuilds; a mesh-sdk change is named
|
||||
as one). Separate fact from guess: a guess is said as one, with how to check it. Write plain words in the
|
||||
glossary's words. Say what you read and what you could not.
|
||||
@@ -1,38 +0,0 @@
|
||||
---
|
||||
name: review-health-and-events
|
||||
description: Reviews a research effort's settled direction or a design against the architectural domains health and events and logs, before it is proposed or merged (hq playbook 01, Review by architectural domain). Read-only; never reopens a decision.
|
||||
tools: Read, Grep, Glob, Bash, mcp__mesh__mesh_call, mcp__mesh__mesh_describe, mcp__mesh__mesh_search
|
||||
model: opus
|
||||
---
|
||||
|
||||
You review one subject of the Novox mesh against two architectural domains: **health** and **events and logs**. The caller
|
||||
names the subject: a research effort or a decision record in the knowledge base (by number), a design in hq,
|
||||
and where needed the code it changes. Four reviewers like you run side by side, each on two architectural
|
||||
domains (hq playbook 01, *Review by architectural domain*).
|
||||
|
||||
**First, what the mesh already knows.** Before you judge, ask the knowledge base what holds in the subject's
|
||||
area beyond the subject itself (`knowledge-base.ask` with its topic in plain words, `knowledge-base.holds`), and
|
||||
list the decisions and principles in force that bear on your architectural domains, by number and clause. Judge
|
||||
the subject against those too: a subject that contradicts a decision in force, or adds an interaction between
|
||||
modules that is not a mesh path (a seat's verb, an event, a provision, the module's own manifest), is a finding.
|
||||
|
||||
Read first: the subject whole (`knowledge-base.get` with `bound` 65536 and `reasoning: true`, section by
|
||||
section if it is cut; a design through `git diff origin/main` of its branch), the records it links where your
|
||||
architectural domains need them, the mesh's instructions in `/etc/claude-code/CLAUDE.md`, and hq's glossary
|
||||
(`00-META/glossary.md`). Read code and records only. Call no verb that writes, edit nothing, and make no git
|
||||
write.
|
||||
|
||||
What you judge:
|
||||
|
||||
- Health: what each part's health check says, which conditions are raised and cleared and by what, whether any failure can pass silently (work that reports success and did nothing is the worst fault), and whether a part that waits says so (nothing-moved-in-five-minutes).
|
||||
- Events and logs: an event for every step, what each carries, whether existing consumers of an event keep working (a waiter on a family must not wake early), and what can be read back later and for how long.
|
||||
|
||||
Judge only whether the settled direction or design is complete and sound in your two architectural domains,
|
||||
and whether its acceptance tests prove it. Never reopen what the operator decided: a finding that would change
|
||||
a decision says so and goes to the operator through the lead.
|
||||
|
||||
Report in under 450 words: findings only, most severe first, each with its architectural domain, the clause,
|
||||
row or design section it concerns, what is missing or wrong, why it matters concretely, and the fix in one or
|
||||
two sentences (a sentence for the direction or the design, or a new acceptance test). Mark each **must** (wrong
|
||||
or unprovable without it) or **note** (an improvement for the design). Write plain words in the glossary's
|
||||
words, with no analogies. Say what you read; a guess is said as one.
|
||||
@@ -1,38 +0,0 @@
|
||||
---
|
||||
name: review-running-and-testing
|
||||
description: Reviews a research effort's settled direction or a design against the architectural domains running qualities and testing, before it is proposed or merged (hq playbook 01, Review by architectural domain). Read-only; never reopens a decision.
|
||||
tools: Read, Grep, Glob, Bash, mcp__mesh__mesh_call, mcp__mesh__mesh_describe, mcp__mesh__mesh_search
|
||||
model: opus
|
||||
---
|
||||
|
||||
You review one subject of the Novox mesh against two architectural domains: **running qualities** and **testing**. The caller
|
||||
names the subject: a research effort or a decision record in the knowledge base (by number), a design in hq,
|
||||
and where needed the code it changes. Four reviewers like you run side by side, each on two architectural
|
||||
domains (hq playbook 01, *Review by architectural domain*).
|
||||
|
||||
**First, what the mesh already knows.** Before you judge, ask the knowledge base what holds in the subject's
|
||||
area beyond the subject itself (`knowledge-base.ask` with its topic in plain words, `knowledge-base.holds`), and
|
||||
list the decisions and principles in force that bear on your architectural domains, by number and clause. Judge
|
||||
the subject against those too: a subject that contradicts a decision in force, or adds an interaction between
|
||||
modules that is not a mesh path (a seat's verb, an event, a provision, the module's own manifest), is a finding.
|
||||
|
||||
Read first: the subject whole (`knowledge-base.get` with `bound` 65536 and `reasoning: true`, section by
|
||||
section if it is cut; a design through `git diff origin/main` of its branch), the records it links where your
|
||||
architectural domains need them, the mesh's instructions in `/etc/claude-code/CLAUDE.md`, and hq's glossary
|
||||
(`00-META/glossary.md`). Read code and records only. Call no verb that writes, edit nothing, and make no git
|
||||
write.
|
||||
|
||||
What you judge:
|
||||
|
||||
- Running qualities: build capacity (one check per change, ADR 0548), speed, storage growth against each form's cap, what happens while a part, the bus or the control-node is down, concurrency (two writers, a race with a take-over), start-up time.
|
||||
- Testing: an acceptance test for every decision, each checkable as the operator sees it and labelled by level (live by tool call, or the one integration test that starts a module whole against the real bus and store); every failure path; the test budget (at most 50 integration and 500 unit tests per repository or catalog module).
|
||||
|
||||
Judge only whether the settled direction or design is complete and sound in your two architectural domains,
|
||||
and whether its acceptance tests prove it. Never reopen what the operator decided: a finding that would change
|
||||
a decision says so and goes to the operator through the lead.
|
||||
|
||||
Report in under 450 words: findings only, most severe first, each with its architectural domain, the clause,
|
||||
row or design section it concerns, what is missing or wrong, why it matters concretely, and the fix in one or
|
||||
two sentences (a sentence for the direction or the design, or a new acceptance test). Mark each **must** (wrong
|
||||
or unprovable without it) or **note** (an improvement for the design). Write plain words in the glossary's
|
||||
words, with no analogies. Say what you read; a guess is said as one.
|
||||
@@ -1,38 +0,0 @@
|
||||
---
|
||||
name: review-seams-and-tools
|
||||
description: Reviews a research effort's settled direction or a design against the architectural domains seams and tools, before it is proposed or merged (hq playbook 01, Review by architectural domain). Read-only; never reopens a decision.
|
||||
tools: Read, Grep, Glob, Bash, mcp__mesh__mesh_call, mcp__mesh__mesh_describe, mcp__mesh__mesh_search
|
||||
model: opus
|
||||
---
|
||||
|
||||
You review one subject of the Novox mesh against two architectural domains: **seams** and **tools**. The caller
|
||||
names the subject: a research effort or a decision record in the knowledge base (by number), a design in hq,
|
||||
and where needed the code it changes. Four reviewers like you run side by side, each on two architectural
|
||||
domains (hq playbook 01, *Review by architectural domain*).
|
||||
|
||||
**First, what the mesh already knows.** Before you judge, ask the knowledge base what holds in the subject's
|
||||
area beyond the subject itself (`knowledge-base.ask` with its topic in plain words, `knowledge-base.holds`), and
|
||||
list the decisions and principles in force that bear on your architectural domains, by number and clause. Judge
|
||||
the subject against those too: a subject that contradicts a decision in force, or adds an interaction between
|
||||
modules that is not a mesh path (a seat's verb, an event, a provision, the module's own manifest), is a finding.
|
||||
|
||||
Read first: the subject whole (`knowledge-base.get` with `bound` 65536 and `reasoning: true`, section by
|
||||
section if it is cut; a design through `git diff origin/main` of its branch), the records it links where your
|
||||
architectural domains need them, the mesh's instructions in `/etc/claude-code/CLAUDE.md`, and hq's glossary
|
||||
(`00-META/glossary.md`). Read code and records only. Call no verb that writes, edit nothing, and make no git
|
||||
write.
|
||||
|
||||
What you judge:
|
||||
|
||||
- Seams: where each piece of state lives on the bus (key-value bucket, stream, object store) and which module owns it; which seat, verb or event joins two parts; any link that is not NATS-native (a poll, a timer, HTTP between modules, a shared file, state only in memory); whether a form the manifest lacks is named with its cost (a mesh-sdk change rebuilds every module).
|
||||
- Tools: for every new piece of state a tool that reads it, and for every new behaviour a tool that steers it (do by hand, ask again, drop, repair, re-check), as the instruction tools-are-how-a-module-is-spoken-to says.
|
||||
|
||||
Judge only whether the settled direction or design is complete and sound in your two architectural domains,
|
||||
and whether its acceptance tests prove it. Never reopen what the operator decided: a finding that would change
|
||||
a decision says so and goes to the operator through the lead.
|
||||
|
||||
Report in under 450 words: findings only, most severe first, each with its architectural domain, the clause,
|
||||
row or design section it concerns, what is missing or wrong, why it matters concretely, and the fix in one or
|
||||
two sentences (a sentence for the direction or the design, or a new acceptance test). Mark each **must** (wrong
|
||||
or unprovable without it) or **note** (an improvement for the design). Write plain words in the glossary's
|
||||
words, with no analogies. Say what you read; a guess is said as one.
|
||||
@@ -1,38 +0,0 @@
|
||||
---
|
||||
name: review-security-and-upgrade
|
||||
description: Reviews a research effort's settled direction or a design against the architectural domains security and upgrade, before it is proposed or merged (hq playbook 01, Review by architectural domain). Read-only; never reopens a decision.
|
||||
tools: Read, Grep, Glob, Bash, mcp__mesh__mesh_call, mcp__mesh__mesh_describe, mcp__mesh__mesh_search
|
||||
model: opus
|
||||
---
|
||||
|
||||
You review one subject of the Novox mesh against two architectural domains: **security** and **upgrade**. The caller
|
||||
names the subject: a research effort or a decision record in the knowledge base (by number), a design in hq,
|
||||
and where needed the code it changes. Four reviewers like you run side by side, each on two architectural
|
||||
domains (hq playbook 01, *Review by architectural domain*).
|
||||
|
||||
**First, what the mesh already knows.** Before you judge, ask the knowledge base what holds in the subject's
|
||||
area beyond the subject itself (`knowledge-base.ask` with its topic in plain words, `knowledge-base.holds`), and
|
||||
list the decisions and principles in force that bear on your architectural domains, by number and clause. Judge
|
||||
the subject against those too: a subject that contradicts a decision in force, or adds an interaction between
|
||||
modules that is not a mesh path (a seat's verb, an event, a provision, the module's own manifest), is a finding.
|
||||
|
||||
Read first: the subject whole (`knowledge-base.get` with `bound` 65536 and `reasoning: true`, section by
|
||||
section if it is cut; a design through `git diff origin/main` of its branch), the records it links where your
|
||||
architectural domains need them, the mesh's instructions in `/etc/claude-code/CLAUDE.md`, and hq's glossary
|
||||
(`00-META/glossary.md`). Read code and records only. Call no verb that writes, edit nothing, and make no git
|
||||
write.
|
||||
|
||||
What you judge:
|
||||
|
||||
- Security: who may call each new verb, how its grants are scoped (ADR 0363's grant form), which credentials go or remain, what can be replaced or forged (a digest, an author, a tag), what enters from outside the mesh, and every act that removes data.
|
||||
- Upgrade: how what runs today moves to the new shape without a delivery breaking, in which order (mesh-sdk, the grants part, the indexer's judge, the module), what readers see during and after, how each step is rolled back, what a node on an older node-engine does, and bootstrap before the bus.
|
||||
|
||||
Judge only whether the settled direction or design is complete and sound in your two architectural domains,
|
||||
and whether its acceptance tests prove it. Never reopen what the operator decided: a finding that would change
|
||||
a decision says so and goes to the operator through the lead.
|
||||
|
||||
Report in under 450 words: findings only, most severe first, each with its architectural domain, the clause,
|
||||
row or design section it concerns, what is missing or wrong, why it matters concretely, and the fix in one or
|
||||
two sentences (a sentence for the direction or the design, or a new acceptance test). Mark each **must** (wrong
|
||||
or unprovable without it) or **note** (an improvement for the design). Write plain words in the glossary's
|
||||
words, with no analogies. Say what you read; a guess is said as one.
|
||||
@@ -21,7 +21,7 @@ All through the mesh MCP server (`mesh_describe` gives each verb's arguments).
|
||||
number is spent; never pick one by hand.
|
||||
3. **Propose the effort.** `mesh_call knowledge-base.propose` with `kind: research`, the reserved `number`,
|
||||
`title`, `status: active`, `date` (today), `sections` (the documents in order, each `name` and `text`;
|
||||
each named by a plain title, never as a file; the first, **Overview**: **what** is investigated, **why**, and **what it touches**), and `links`
|
||||
the first `00-overview.md`: **what** is investigated, **why**, and **what it touches**), and `links`
|
||||
of kind `touches` for every record, effort, issue or design it touches (`cites` for one it only names).
|
||||
4. **Do the research** in further documents of the same effort. Each change is `propose` again with the
|
||||
effort whole and `why`. A refusal names every rule broken: fix them all and propose again. On review
|
||||
|
||||
@@ -1,2 +1 @@
|
||||
__pycache__/
|
||||
.work/
|
||||
|
||||
@@ -52,7 +52,7 @@ const (
|
||||
linesFrom = "2026-10-09"
|
||||
)
|
||||
|
||||
var wordsAlways = []string{"00-META/", "03-DESIGN/", ".claude/", "AGENTS.md", "README.md"}
|
||||
var wordsAlways = []string{"00-META/", "03-DESIGN/", "AGENTS.md", "README.md"}
|
||||
|
||||
// daemonLines are the daemons' lines (ADR 0272): each daemon's commands. "alone" is a command that is a line by
|
||||
// itself; "with an argument" one that is a line only when written as one ("the mesh-controller module" is prose;
|
||||
|
||||
+10
-103
@@ -30,8 +30,7 @@ is listed under [Homonyms](#homonyms), with the qualified form each domain uses.
|
||||
- **mesh** — the whole: the nodes, the modules assigned to them, the controller that decides and the
|
||||
records it keeps. One mesh per operator; a second mesh is a second everything.
|
||||
- **domain** — an area of the mesh that owns a set of concepts and the one word for each, as described
|
||||
in the section above (ADR 0244); its qualified form is **mesh domain**. Bare, "domain" keeps this sense; the
|
||||
review's sense is always written **architectural domain** (Homonyms).
|
||||
in the section above (ADR 0244).
|
||||
ADR 0006 called seven of them the controller's *contexts*; they carry over as domains under the same
|
||||
names, except *observability*, which is now **Health and repair**. Where a domain's records live in
|
||||
the controller, the domain owns that store alone (ADR 0008).
|
||||
@@ -58,9 +57,6 @@ is listed under [Homonyms](#homonyms), with the qualified form each domain uses.
|
||||
promised as optional, then served, then required (ADR 0246).
|
||||
A node seat may also name **the paths its holder owns**, such as `/etc/hosts`: a claim whose module does
|
||||
not declare them is refused, and only a claimant declares them (ADR 0287).
|
||||
- **seat definition** — the one language-neutral file (a schema with conformance fixtures) that names a seat's
|
||||
verbs and their messages, its events, its state and the forms of its provisions; its Go and TypeScript types
|
||||
are generated from it. The indexer keeps every trunk's and open pull request's as bus state (ADR 0608 §1, §2).
|
||||
- **operator** — the person who runs the mesh, and the one the mesh talks to.
|
||||
*Not:* ~~master~~ (hq)
|
||||
- **person** — any human, as against the mesh acting unattended: *a person's word* releases a held
|
||||
@@ -98,17 +94,8 @@ and repair).
|
||||
- **tool** — one capability a module serves through the tool runner, addressed `<module>.<tool>`. A
|
||||
seat's **verb** is the same thing carried by a seat rather than a module.
|
||||
- **mesh-sdk** — the library a module's own code is written against, including the SDK harness that serves a
|
||||
tools bundle to the tool runner. The SDK harness is part of it, not a library of its own. It keeps the shared
|
||||
runtime, and its seat contracts move into `mesh-contracts` (ADR 0608 §5).
|
||||
tools bundle to the tool runner. The SDK harness is part of it, not a library of its own.
|
||||
*Not:* ~~tools-sdk~~
|
||||
- **contracts module** — a module whose work is to publish the contract packages of seats (their verbs, events,
|
||||
the layouts of their states, their messages) in every language the seats' users are written in, and never logic:
|
||||
`mesh-contracts` for the seat contracts mesh-sdk held, `mesh-<area>-contracts` for an area's. A seat's holder
|
||||
implements the contract; the contracts module holds no seat (ADR 0608 §5).
|
||||
*Not:* ~~SDK module~~, ~~domain package~~
|
||||
- **package series** — the first two numbers of a package's versions (`0.1`), declared in its manifest and raised
|
||||
only by a reviewed change, for a change that breaks its users; a user's manifest names the package series it
|
||||
accepts (ADR 0606 §4, ADR 0609 §1). Bare "series" only where the package is plain (see [Homonyms](#homonyms)).
|
||||
- **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
|
||||
@@ -129,8 +116,6 @@ downstream of Module and Placement. **Decided by** ADR 0262, 0277, 0348.
|
||||
**Uses:** module, node, manifest (Module), assignment (Placement), provision (Provisioning), proposal
|
||||
(Operator and conversation).
|
||||
|
||||
- **template file** — a file a module ships with placeholders, such as `${config:<key>}`, which the provisioner
|
||||
client fills on each node (ADR 0349). Never a template of bus state (see [Homonyms](#homonyms)).
|
||||
- **config value** — anything a module reads that someone may change: a font size, a domain, an address.
|
||||
A module declares every one it reads under `config` in its manifest, with a type, a description,
|
||||
validation, whether it is trusted, optionally the predefined key it falls back to, and a default or
|
||||
@@ -227,8 +212,7 @@ every other domain has something to run on. **Recorded by** the controller and t
|
||||
A module reaches it by requiring `mesh-bus` (ADR 0128);
|
||||
one that does not require it has no account on it. Held by the `mesh-broker` seat, which is named
|
||||
after the role rather than the server, so the server can change without the seat doing so. The `nats`
|
||||
module holds it. The **bus module** is the module holding `mesh-broker`; it also provides bus state (Data,
|
||||
ADR 0595).
|
||||
module holds it.
|
||||
- **the deprecated broker** — the lavinmq module. It was the mesh's bus and is not any more. It keeps
|
||||
running as an **ordinary provider** of the `amqp` provision, for modules that need a message broker
|
||||
of their own the way something needs a database
|
||||
@@ -525,29 +509,12 @@ controller (the planner, the rollout) until release 6 of ADR 0429; from each rel
|
||||
and at the end the outcome and each artifact's digest). A build's duration is read from its two entries, and no
|
||||
other part records durations; the builder's module tool `builder_builds_since` answers the builds a reader
|
||||
missed from it (ADR 0571 §1, §12).
|
||||
- **package** — what code resolves when it is **compiled**: a Go module, or an npm, cargo or pypi dependency, by
|
||||
**version**. Published by the registry seat of its language, on the request of its language's build module, from
|
||||
a trunk build under the **next version** or for a **tagged version** (ADR 0605 §3, ADR 0607). Only a build module
|
||||
asks a registry seat to store one.
|
||||
- **registry seat** — a seat held once for the mesh whose holder stores what build modules publish, under the
|
||||
version the builder gave, and serves its toolchain's read side: `npm-package-registry` and
|
||||
`go-package-registry` (the **package-registry**, held today by the forge module), `container-image-registry`
|
||||
and `oci-artifact-registry` (held today by the `distribution` module). One contract: `publish`, `versions`, a
|
||||
verb removing a tagged version, the events `published` and `removed` (ADR 0605 §1, §2). Not the forge.
|
||||
*Not:* ~~docker-package-registry~~
|
||||
- **next version** — the version the builder gives a trunk build of a published thing: its package series and one
|
||||
above the highest count it holds for that name, kept with its fingerprint, commit, digest and time in the
|
||||
builder's versions on the bus (ADR 0606 §1 to §3). Counted, never typed.
|
||||
- **tagged version** — a published version with a tag (a branch's name, or a word such as `beta`), numbered in
|
||||
Semantic Versioning's pre-release form below the next version, never a package's latest, taken only by a build
|
||||
that asks its tag, and removed when its branch is merged or deleted (ADR 0607).
|
||||
- **mesh delivery case** — a kind of change a developer makes (a package's change, a renamed config value, a new
|
||||
seat), and how the mesh carries it from the pull request to the nodes; research 081 lists them.
|
||||
- **package** — what code resolves when it is **compiled**: an npm/cargo/pypi dependency, by **version**.
|
||||
Served by the **package-registry** (gitea). Only a build module talks to it.
|
||||
- **artifact** — anything a build produces and the mesh delivers to a node by **digest**: an `image`, a
|
||||
mirrored `upstream` image, a `bundle` of the module's own code, an `archive`. Served by the
|
||||
**artifact store**, an OCI registry that holds every kind as content-addressed blobs
|
||||
(ADR 0156), reached through two registry seats: `container-image-registry` for images and
|
||||
`oci-artifact-registry` for bundles and archives, which replace the seat `mesh-artifact-store` (ADR 0605 §1).
|
||||
(ADR 0156).
|
||||
An image is one kind of artifact, and a module is not an image. A package and an artifact are two
|
||||
protocols, not one store being weak (ADR 0075).
|
||||
- **artifact sweep** — letting go of the artifacts no one needs any more, the seat `mesh-artifact-sweep`, held
|
||||
@@ -756,8 +723,8 @@ becomes a message). **Decided by** ADR 0227, 0231, 0240, 0252, 0283, 0564.
|
||||
|
||||
**Purpose.** To know every item of data a module holds, how precious it is, and to keep it recoverable.
|
||||
**Recorded by** the manifests' `data` declarations and every node's `node-backup` seat.
|
||||
**Upstream of** Delivery and Health and repair. **Decided by** ADR 0232, 0233, 0235, 0571, 0595, 0596, 0597.
|
||||
**Uses:** module, node, provider and consumer (Provisioning), person, bus module (Core), tool runner (Core).
|
||||
**Upstream of** Delivery and Health and repair. **Decided by** ADR 0232, 0233, 0235, 0571.
|
||||
**Uses:** module, node, provider and consumer (Provisioning), person.
|
||||
|
||||
- **data class** — how precious an item of data is, ranked by the operator: `irreplaceable`,
|
||||
`valuable`, `rebuildable`, `cache`; and `none` for a provision that keeps nothing of anybody's
|
||||
@@ -772,25 +739,6 @@ becomes a message). **Decided by** ADR 0227, 0231, 0240, 0252, 0283, 0564.
|
||||
self-check read them there, never the controller (ADR 0571 §5).
|
||||
- **sticky binding** — a consumer's tie to the data a provider keeps for it, which moves only by a
|
||||
person (ADR 0232).
|
||||
- **bus state** — what a module keeps on the bus, asked for in `requires` as a provision of the bus module, by a
|
||||
template and its options; created by the bus module and reached by the owner's account alone. Another module
|
||||
asks the owner's tools for its data, or hears its declared events (ADR 0595, ADR 0596).
|
||||
- **storage type** — how the bus keeps bus state: the **stream**, the base, or one built on it, the **key-value
|
||||
bucket**, the **object store** or the **ledger**, each with calls of its own (ADR 0595 §2).
|
||||
*Not:* ~~data type~~
|
||||
- **configuration** of a storage type — the settings it is given: what is kept, for how long, how much, and what
|
||||
happens when full. Never bare where it could be read as a config value (see [Homonyms](#homonyms)).
|
||||
- **template** — a storage type with a configuration, predefined and named: **state**, **events**, **work
|
||||
queue**, **log**, **objects** and **ledger** (ADR 0595 §2). What keeps its data safe, "when full" among it, is
|
||||
fixed by the template. *Template of bus state* where it could be read as a template file (see
|
||||
[Homonyms](#homonyms)). The template log is the module log of ADR 0297.
|
||||
- **option** of a template — one setting a module may change, within its bound, such as state's past values (1 to
|
||||
64) (ADR 0595 §3).
|
||||
- **ledger** — a storage type keeping, per instance of a type, a history of every **operation** (one change, kept
|
||||
in order) and a state derived from that history by each operation's **ledger effect** (one of a fixed set: set
|
||||
fields, set an entry of a map, append to a list, remove), through the tool runner's `apply` (ADR 0597).
|
||||
- **snapshot** of a ledger's state — the latest state of one instance, on a subject of its own, never expiring,
|
||||
from which a rebuild starts; distinct from the stream snapshot.
|
||||
|
||||
## Connectivity — how nodes and modules reach one another
|
||||
|
||||
@@ -1018,7 +966,7 @@ and no session (ADR 0278), in its plain sense.
|
||||
|
||||
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, 0423, 0424, 0592.
|
||||
0284, 0290, 0291, 0423, 0424.
|
||||
|
||||
- **research effort**, **graduation**, **hand-off**, **playbook** — an investigation, held whole in the
|
||||
knowledge base and named "research NNN";
|
||||
@@ -1036,33 +984,10 @@ Not a domain of the mesh but of the way it is built, listed because its words me
|
||||
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. It keeps them on the bus: each record's head in its state, its versions on a stream, its
|
||||
sections' bodies once each in an object bucket by digest (ADR 0592).
|
||||
- **section** — of a record: one part of its reasoning with a plain title, such as Context or Direction, stored once by
|
||||
the digest of its body, and written and reviewed on its own; never a file, and never named like one. It is named as
|
||||
"research 081 › Direction"; a clause as "ADR 0290 §7" (ADR 0592). Not an **instruction section** (see
|
||||
[Homonyms](#homonyms)).
|
||||
- **draft** — of a record: the version being written, one section per call, held by the knowledge base and by one
|
||||
**author**, until it is submitted as a version or dropped; another session may **take it over** with a reason, as a
|
||||
proposal (ADR 0592). Not a **drafted** clause, which the import's judgement wrote from prose (see
|
||||
[Homonyms](#homonyms)).
|
||||
- **opened** — of a draft: begun by `open` on a reserved or existing record, with its author, and said by the event
|
||||
`opened` (ADR 0592). Not **drafted**.
|
||||
- **drafted** — of a clause: written by the import's judgement from a prose Decision section, and never served as in
|
||||
force until a second judgement confirms it; the event `drafted` says so (ADR 0290, ADR 0321). Never said of a
|
||||
draft, which is **opened**.
|
||||
- **form-only version** — of a record: a version that changes only its sections' titles and the targets of its links,
|
||||
proved by a check rather than judged, and marked `form`; never a change of meaning (ADR 0592).
|
||||
- **search index** — the knowledge base's full-text index of the records, built in the module's memory from the bus
|
||||
at start and kept by the events; `ask` and `holds` answer from it (ADR 0592).
|
||||
of this repository.
|
||||
- **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/`.
|
||||
- **architectural domain** — one of the eight concerns a research effort's direction and a design are reviewed
|
||||
against before they are proposed or merged: security, seams, running qualities, health, events and logs,
|
||||
testing, upgrade and tools (playbook 01, *Review by architectural domain*; the operator, 2026-11-02). A reviewer
|
||||
judges whether the settled direction is complete and sound in its architectural domains and never reopens a
|
||||
decision. Not to be confused with a **mesh domain** (Homonyms).
|
||||
- **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).
|
||||
@@ -1125,18 +1050,6 @@ renaming one sense, the old sense moves to a *Not:* line and becomes mechanical.
|
||||
| | whether the machines a change touches still compose | **composition check** | Delivery |
|
||||
| membership | the subjects a bus account is issued | **membership** | Provisioning |
|
||||
| | an account in a group of the machine | **account group** | Placement |
|
||||
| domain | an area of the mesh that owns a set of concepts and the one word for each (ADR 0244), the sections of this page; the one sense "domain" keeps bare | **mesh domain** | The record |
|
||||
| | one of the eight concerns a direction and a design are reviewed against (playbook 01); never written bare | **architectural domain** | The record |
|
||||
| configuration | the settings of a storage type, which a template fixes or a module gives | **configuration** of a storage type | Data |
|
||||
| | anything a module reads that someone may change | **config value** | Config |
|
||||
| snapshot | the latest state of one ledger instance, from which a rebuild starts | **snapshot** of a ledger's state | Data |
|
||||
| | the bus's own backup of each stream | **stream snapshot** | Data |
|
||||
| template | a storage type with a configuration, asked for in `requires` | **template** of bus state | Data |
|
||||
| | a file with placeholders a provisioner fills | **template file** | Config |
|
||||
| option | a setting of a template a module may change | **option** of a template | Data |
|
||||
| | one choice of an ask to a person | an ask's **option** | Operator and conversation |
|
||||
| effect | what an operation does to a ledger's state | **ledger effect** | Data |
|
||||
| | a tool's mark: reads, acts or dangerous | a tool's **effect** | Identity and access |
|
||||
| push | the controller giving nodes their declarations, until release 6 | **send** (verb `push`) | Placement |
|
||||
| | a git push | **git push** | The record |
|
||||
| | a phone notification | **push notification** | Operator and conversation |
|
||||
@@ -1154,8 +1067,6 @@ renaming one sense, the old sense moves to a *Not:* line and becomes mechanical.
|
||||
| harness | the part of `mesh-sdk` that serves a tools bundle | **SDK harness** | Module |
|
||||
| | the program that runs an agent's sessions | **agent harness** | Operator and conversation |
|
||||
| | the TypeScript `provisioner` helper for database providers | **`provisioner`**, renamed by design 57 | Provisioning |
|
||||
| series | the first two numbers of a package's versions | **package series** | Module |
|
||||
| | a kind of content told in episodes | the kind **series** | Media |
|
||||
| role | what a seat names, at a scope | **seat**; the bare word in older designs is tracked by issue 872 | Module |
|
||||
| | what an identity may call | **role** | Identity and access |
|
||||
| ask | a request for the operator's input | **ask** | Operator and conversation |
|
||||
@@ -1170,10 +1081,6 @@ renaming one sense, the old sense moves to a *Not:* line and becomes mechanical.
|
||||
| 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 |
|
||||
| section | one titled part of a record's reasoning | **section** | The record |
|
||||
| | one named section of the managed instruction file | **instruction section** | Operator and conversation |
|
||||
| draft | a record's version being written, held by one author | **draft** | The record |
|
||||
| | a clause the import's judgement wrote from prose | **drafted** clause | The record |
|
||||
| | the predecessor's operational memory, in older playbooks (issue 430) | none: it no longer exists | — |
|
||||
| check | a pull request's two statuses, until release 4 of ADR 0429 | **merge check**, **repository check**; from release 4, `mesh/check` | Delivery |
|
||||
| | whether the machines a change touches still compose, the assigner's | **composition check** | Delivery |
|
||||
|
||||
@@ -43,22 +43,16 @@ An idea arrives in one of two forms, and the first step differs:
|
||||
is spent whether or not the effort is ever proposed; one never proposed is withdrawn after a week. Never
|
||||
pick one by hand.
|
||||
4. **Propose it.** `knowledge-base.propose` with `kind: research`, the `number`, a one-line `title`,
|
||||
`status: active`, `date` (the day it began), and `sections`, each a `name` and its markdown `text`. A
|
||||
section is a part of the record, never a file: its `name` is a plain title, a reference to another section
|
||||
names that title ("the Direction's test 8"), and code evidence names the repository, the commit and the
|
||||
function, never a path or a line (the operator, 2026-11-02: "don't use file references or local files
|
||||
anymore"; the knowledge base is to be shown as a web page, where a file name links to nothing). Once
|
||||
proposed, the record is the only copy: it is changed by `knowledge-base.get` and `propose`, never kept
|
||||
in a local draft. The first sections:
|
||||
- **Overview**: the idea in the operator's words, what exists, the operator's answers (dated), and the
|
||||
`status: active`, `date` (the day it began), and `sections`, each a `name` and its markdown `text`:
|
||||
- `00-overview.md`: the idea in the operator's words, what exists, the operator's answers (dated), and the
|
||||
words the effort uses that the glossary lacks, each defined in one line;
|
||||
- **Questions**: the questions to answer, the first ones first;
|
||||
- **Method**: what will be tried and measured, and on what.
|
||||
- `01-questions.md`: the questions to answer, the first ones first;
|
||||
- `02-method.md`: what will be tried and measured, and on what.
|
||||
|
||||
Name what it touches as `links` of kind `touches` (`"0276"`, `"research 045"`, `"issue 365"`, or a
|
||||
design's path), and what it names for another reason as `cites`.
|
||||
5. **Do the research.** Try, measure and read, smallest question first. Each finding goes into
|
||||
the section **Findings**: what was tried, what it showed (with numbers), what it means. Each change of the effort
|
||||
`04-findings.md`: what was tried, what it showed (with numbers), what it means. Each change of the effort
|
||||
is `knowledge-base.propose` again, whole, with `why`; every version is kept, and `knowledge-base.history`
|
||||
lists them and gives the difference between two. Until issue 844 is delivered, the knowledge base
|
||||
takes a new version of an accepted, active effort only as a progressive insight: give `insight`, saying
|
||||
@@ -75,51 +69,20 @@ An idea arrives in one of two forms, and the first step differs:
|
||||
server, a protocol) is tried in process, on the machine's loopback, in minutes. Lab runs (about 12 to
|
||||
16 minutes fresh) are for the mesh's own code; before that code exists, a lab run tests nothing an
|
||||
in-process test does not, and its lab scenario is written as the build's acceptance test instead.
|
||||
6. **Place it in the domains.** Before the direction, write the section **Domains**: the domain or domains the idea
|
||||
6. **Place it in the domains.** Before the direction, write `03-domains.md`: the domain or domains the idea
|
||||
belongs to (the glossary's sections, ADR 0244); the modules and seats it lands in, each new, changed or
|
||||
retired; what it does to each domain's words, each word added (with its domain and a one-line meaning),
|
||||
changed or retired, and none on a *Not:* line or already meaning something else (the glossary's homonyms);
|
||||
and whether a domain grows or shrinks, a concept moving between domains included. The direction names its
|
||||
decisions in those words. Asked when the idea is set up, too: an option that lands a concept in the wrong
|
||||
domain is offered with that cost named, like one that breaks a rule.
|
||||
7. **Settle the direction with the operator.** When the questions are answered, write the section **Direction**:
|
||||
7. **Settle the direction with the operator.** When the questions are answered, write `05-direction.md`:
|
||||
the proposed decisions in a few lines each, the options rejected and why, and the build's acceptance
|
||||
tests: what the build must prove before it is done, carried by its milestones (playbook
|
||||
[04](04-build-handoff.md)). Nothing else is left open: each question closes by a test, by a decision the
|
||||
effort records with its reason (a small design limit is the lead's), or by the operator. Ask the
|
||||
operator to confirm or change the direction, one point at a time. Then review it by architectural domain (below) and
|
||||
propose it once. Then the effort graduates through playbook [02](02-graduation.md).
|
||||
|
||||
## Review by architectural domain
|
||||
|
||||
A direction the operator settled can still be wrong where nobody looked. Before it is proposed, and again
|
||||
before a design is merged (playbook [02](02-graduation.md)), it is reviewed against each **architectural domain**
|
||||
(the operator, 2026-11-02), by reviewers on the larger model, about two architectural domains each, side by side:
|
||||
the four reviewer agents in `.claude/agents/` (`review-security-and-upgrade`, `review-seams-and-tools`,
|
||||
`review-running-and-testing`, `review-health-and-events`), each given the subject by number or path:
|
||||
|
||||
- **security**: who may call each new verb, how its grants are scoped, which credentials go or remain, what
|
||||
can be replaced or forged, and what enters from outside the mesh;
|
||||
- **seams**: where each piece of state lives on the bus, which seat, verb or event joins two parts, and any
|
||||
link that is not NATS-native. Each piece of state is kept in the bus storage form that fits it (a key-value
|
||||
bucket, a stream, an object store); a form the manifest lacks is added to mesh-sdk, and the review names that change
|
||||
and its cost (a mesh-sdk change rebuilds every module) to the operator, never folds it in silently (the
|
||||
operator, 2026-11-02);
|
||||
- **running qualities**: build capacity, speed, storage growth, and what happens while a part, the bus or the
|
||||
control-node is down;
|
||||
- **health**: what each part's health check says, which conditions are raised and cleared, and whether any
|
||||
failure can pass silently;
|
||||
- **events and logs**: what each step says on the bus, and what can be read back later;
|
||||
- **testing**: an acceptance test for every decision, each one checkable and within the test budget, and the
|
||||
failure paths;
|
||||
- **upgrade**: how what runs today moves to the new shape without a delivery breaking, in which order, and
|
||||
how it is rolled back;
|
||||
- **tools**: which tool reads each new piece of state and which one steers each new behaviour.
|
||||
|
||||
A reviewer judges only whether the settled direction is complete and sound in its architectural domains; it never reopens
|
||||
a decision. Each finding is a sentence for the direction or a new acceptance test; a finding that changes a
|
||||
decision goes to the operator. The direction ends with a section *Reviewed by architectural domain*: per architectural domain, what was
|
||||
found and how it was answered, or that nothing was found.
|
||||
operator to confirm or change the direction, one point at a time, then propose it once. Then the effort
|
||||
graduates through playbook [02](02-graduation.md).
|
||||
|
||||
## Bounds and models
|
||||
|
||||
@@ -127,10 +90,9 @@ found and how it was answered, or that nothing was found.
|
||||
|---|---|---|
|
||||
| reading and the setup questions (1, 2) | 30 minutes | the session's own, larger |
|
||||
| proposing (3, 4) | 15 minutes | larger |
|
||||
| each experiment or reading task (5) | 30 minutes | smaller, one agent at a time unless the operator allows more: the agent `research-reader` |
|
||||
| each experiment or reading task (5) | 30 minutes | smaller, one agent at a time unless the operator allows more |
|
||||
| placing it in the domains (6) | 15 minutes | larger |
|
||||
| the direction (7) | 30 minutes | larger |
|
||||
| the review by architectural domain | 20 minutes | larger, about two architectural domains per reviewer |
|
||||
|
||||
A step past its bound is said to the operator at once, with what is being done about it. The lead reports
|
||||
as playbook 07 does: every ten minutes and at once when something is found, stops or needs the operator,
|
||||
@@ -172,8 +134,7 @@ The rules on an effort are the knowledge base's: `propose` refuses one that brea
|
||||
record (the module's table test holds one case per rule), and the rules that judge the whole record set run
|
||||
after every acceptance and raise a condition when one fails. The habits of steps 2, 5, 6 and 7 (options checked
|
||||
against the rules, one version per settled set, the cheapest level, nothing left open) are the lead's, read by
|
||||
the reviewer; the review by architectural domain is read in the direction's *Reviewed by architectural domain* section (nothing refuses a direction
|
||||
without it yet: the lead and the reviewer hold it); of the pre-check, `propose` refuses a link to nothing and a retired word, and the review returns
|
||||
the reviewer; of the pre-check, `propose` refuses a link to nothing and a retired word, and the review returns
|
||||
a record named but not linked, an untracked deferral and a figure not as measured. That this repository holds
|
||||
no effort is the records test's `no-records` check, on every pull request. An active effort with no new version
|
||||
for seven days is the smell of research left behind: its lead closes or continues it.
|
||||
|
||||
@@ -13,7 +13,7 @@ clauses in force for a topic or a record).
|
||||
|
||||
## Graduating research
|
||||
|
||||
The effort's Direction, confirmed by the operator (playbook [01](01-research.md), step 7), is what
|
||||
The effort's `05-direction.md`, confirmed by the operator (playbook [01](01-research.md), step 7), is what
|
||||
graduates. The lead writes the records and the design on the larger model; it is one sitting, bounded at
|
||||
about an hour, and said to the operator if it runs past.
|
||||
|
||||
@@ -49,11 +49,9 @@ about an hour, and said to the operator if it runs past.
|
||||
---
|
||||
```
|
||||
|
||||
The design lists the direction's acceptance tests: what the build proves before it is done. Before it is
|
||||
merged, the design is reviewed by architectural domain as playbook [01](01-research.md#review-by-architectural-domain) says, and
|
||||
ends with its own *Reviewed by architectural domain* section; a design amended without an effort is reviewed the same way.
|
||||
The design lists the direction's acceptance tests: what the build proves before it is done.
|
||||
|
||||
The words of the effort's Domains go into the glossary in the same change, each in its domain: a
|
||||
The words of the effort's `03-domains.md` go into the glossary in the same change, each in its domain: a
|
||||
word added gets its entry, a word whose meaning changed gets its entry rewritten, and a retired word moves to
|
||||
its replacement's *Not:* line. A word that already means something else in another domain is named as a
|
||||
homonym with the qualified form each domain uses.
|
||||
@@ -66,15 +64,6 @@ about an hour, and said to the operator if it runs past.
|
||||
|
||||
## Writing a decision record
|
||||
|
||||
**Before a number: what holds, and the mesh path, reviewed.** Ask the knowledge base what is already decided in
|
||||
the record's area (`knowledge-base.ask`, `holds`), and name in the draft, for every interaction between modules the
|
||||
record decides, the seat and verb, event, provision, state on the bus or manifest field that carries it. Then
|
||||
`review-seams-and-tools` reviews the draft before it is proposed: a clause that has one module name another, read its
|
||||
internals or be special-cased in another part, or puts a module's name into shared code, is a finding (the operator,
|
||||
2026-11-03).
|
||||
How it is checked: the record's References name the seams review's verdict; a record proposed without it is a breach
|
||||
the lead names.
|
||||
|
||||
1. **Take a number.** `knowledge-base.reserve` with `kind: decision` and `for`: the record's title. The
|
||||
number comes from the issue tracker's counter and is spent whether or not the record is accepted; one
|
||||
never proposed is withdrawn after a week. Never pick a number by hand.
|
||||
@@ -140,8 +129,7 @@ A design changes only through a decision.
|
||||
|
||||
1. Write the decision record (above). If it reverses an earlier one, it `supersedes` it.
|
||||
2. Edit the to-be design document, add the record to its `decisions:` as `"ADR NNNN"`, and set `updated:`
|
||||
to today. Review it by architectural domain before it is merged (playbook [01](01-research.md#review-by-architectural-domain)), and
|
||||
rewrite its *Reviewed by architectural domain* section.
|
||||
to today.
|
||||
3. If the amendment came from an issue, give the issue the document path in the issue tracker:
|
||||
`amended-design` on its `issue-tracker.move` to `resolved`, or `issue-tracker.link` with kind
|
||||
`design` before then — never in the report's frontmatter
|
||||
@@ -176,8 +164,7 @@ record (its module's table test holds one case per rule), and the four rules tha
|
||||
changes, an accepted record extending a superseded one) run after every acceptance and raise a condition
|
||||
when one fails. In this repository, the records test checks that a design's `decisions:` entries and every
|
||||
"ADR NNNN" in the text are well formed, and that no record is written here; the index test checks the reading
|
||||
order's six topics, which the knowledge base reads. A design's *Reviewed by architectural domain* section is read by the
|
||||
reviewer of its pull request; nothing refuses a design without it yet. The three habits of graduating step 2 (side by side, clauses
|
||||
order's six topics, which the knowledge base reads. The three habits of graduating step 2 (side by side, clauses
|
||||
from the direction, the pre-check) are the lead's, read by the reviewer; `propose` refuses a link to nothing,
|
||||
a deferral no issue tracks and a retired word, and the review returns a record named but not linked.
|
||||
The rounds are read in `knowledge-base.get` (its reviews, one per version): a question to the operator about a
|
||||
|
||||
@@ -44,10 +44,6 @@ A small fix (one repository, one issue, about an hour of work) follows
|
||||
on the bus; two reviews read the code, no test ran it under a node's real permissions, and once it was live every
|
||||
node's agent was refused that read and stopped composing (issue 1126). How-we-build's rule "A bus permission is
|
||||
proven under the real permission" (ADR 0560) is what the review now asks for.
|
||||
- **A change built on what was remembered, not on what runs.** In the days up to 2026-11-02 a release took the
|
||||
controller's jobs apart from the records and the survey alone. Jobs nobody had named surfaced only after the
|
||||
delivery, a moved module's wiring and a changed failure label's readers were never read, and every check found one
|
||||
more fault. Each layer had checked the layer before it; none had read the running code. Step 0 item 3a is the answer.
|
||||
- **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.
|
||||
|
||||
@@ -59,7 +55,7 @@ opened **once**, after the review, and **one check** (ADR 0548). It runs in five
|
||||
|
||||
| Step | What happens | Bound |
|
||||
|---|---|---|
|
||||
| 0. Prepare | the lead reads what exists today in the code and on the live mesh, settles the open questions and writes one context file per repository, opening with what exists, what relies on it and what happens to each | 15 minutes, longer when what exists is large: code never starts on an incomplete answer |
|
||||
| 0. Prepare | the lead settles the open questions and writes one context file per repository | 15 minutes |
|
||||
| 1. Develop | one fixing agent per repository builds every issue locally, the whole unit suite before every commit, the integration tests compiled (those that exercise the change are run by name before the push, step 3); nothing is pushed | the milestone's issues, 30 minutes each |
|
||||
| 2. Review | one reviewer reads all of it, the local commits, judging the integration tests by reading them against the code; reports go both ways; at most 3 rounds | 15 minutes |
|
||||
| 3. Check | push the reviewed heads once and open the pull requests together; the checker's one check runs the integration tests; no further review unless the check fails | one check run |
|
||||
@@ -100,27 +96,7 @@ the running milestone, so the scope above holds. Only an issue in another design
|
||||
a time), or one that needs a decision or the operator first, waits, and the report says which and why. Each detailed report's line "Opened along the way" says for every
|
||||
issue whether it is being fixed in between, after the delivery, or waits, and on what.
|
||||
|
||||
## Step 0 — Prepare (the lead with mesh-preparer, larger model)
|
||||
|
||||
**Agents work from hq's definitions, never from a prompt written by hand.** Every agent the lead starts is one of
|
||||
`.claude/agents/`: `mesh-preparer` for this step, `mesh-developer` for step 1, `mesh-code-reviewer` for step 2,
|
||||
`mesh-watcher` for every wait, and the four reviewers by architectural domain and `research-reader` of playbook 01.
|
||||
The lead writes only the handover: what the agent works on (issue, records, branch, worktrees, commits) and where it
|
||||
reports. A subagent runs in the session that started it, from hq: its way of working comes from its definition and hq's playbooks, its knowledge from the knowledge base; one given a coding task works in a worktree under hq's `.work/` and also reads the conventions of the repository it changes, and where those contradict hq, hq wins and the agent says so. How it works, what it reads first and what it judges come from its definition, and so does its model: a
|
||||
`mesh-developer` runs on the smaller model, and the lead starts one on the larger model only for work whose parts meet
|
||||
at an interface still being designed, written with its reason in the milestone file; a lesson about how an agent
|
||||
works goes into its definition by an hq pull request (the operator, 2026-11-03: "please don't write prompts by hand,
|
||||
only the handover prompt"). How it is checked: the lead's report names each agent by its definition; an agent started
|
||||
from a prompt written by hand is a breach the lead names at the next report.
|
||||
|
||||
**Every prepare starts from the knowledge base and maps every interaction onto a mesh path, and the seams review
|
||||
judges it before code starts.** The preparer lists the decisions in force for the change and the seat, verb, event,
|
||||
provision, state on the bus or manifest field that carries each interaction the change adds; then
|
||||
`review-seams-and-tools` reviews the prepare (and any decision record written for the change) before step 1. A module
|
||||
that names another, reads its internals or is special-cased in another part, or a module's name in shared code, is a
|
||||
finding of that review (the operator, 2026-11-03: "you never follow the mesh paths for inter-module communication").
|
||||
How it is checked: the milestone file carries the seams review's verdict on the prepare; a step 1 started without it
|
||||
is a breach the lead names.
|
||||
## Step 0 — Prepare (the lead, larger model)
|
||||
|
||||
1. **Name the feature once:** `feat/<slug>`, the same branch name in every repository it touches (but see the
|
||||
exception for a new manifest word in phase 3: a release that must merge first has a name of its own). The
|
||||
@@ -138,33 +114,8 @@ is a breach the lead names.
|
||||
2026-10-26: "that is simply our delivery plan and milestones, we've already researched it"); propose new research
|
||||
only for what no record decides. A fixing agent that meets an undecided design question
|
||||
stops, and its run is spent again.
|
||||
3a. **Read what exists today, and write it down before any code** (the operator, 2026-11-02: "if i ask to implement
|
||||
feature A, you'd better first check what exists today with regards to feature A"). Research, decisions, the survey
|
||||
and the review each check the layer before them; this is the step that checks the running code. Each repository's
|
||||
context file (item 4) opens with a section `What exists today` that answers three questions, in writing:
|
||||
1. **What exists today for this feature**, read from the code (file:line) and from the live mesh through the mesh
|
||||
MCP server's tools (the seats, the modules on each machine, their state on the bus, their tools' answers), never
|
||||
from memory and never from a record alone. Where the code or the mesh contradicts a record, the answer says so,
|
||||
and the lead settles it as item 3 says.
|
||||
2. **Everything the change touches, replaces or removes, and what relies on each today.** For a part that is removed
|
||||
or moved (a module, a seat, a job of a module, a file, a value's form, a label others read), every effect it has
|
||||
is listed, found mechanically from its code and not recalled: each bus write (bucket key, stream subject, event),
|
||||
each file it writes, each credential or grant it issues or rotates, each plan or record it makes, each process or
|
||||
container it starts, each verb, tool or request it answers, and each timer it runs. Then, for each effect, every
|
||||
reader that relies on it (a search across every repository and the live bus, file:line or address). The search is
|
||||
written down as run (the pattern and where), so the review can run it again.
|
||||
3. **For each item of the second answer, what happens to it after the change** (kept as it is, moved to a named seat,
|
||||
module or verb, replaced by a named part, or dropped and why nothing needs it), **and the test that proves it**: an
|
||||
acceptance criterion (item 5) at its level, or, for a dropped effect, the search showing no reader is left.
|
||||
Code starts only when the three answers are complete for every repository: no fixing agent starts on a context file
|
||||
without them, and an effect found later is the prepare step missed, written into the answer and its issue before the
|
||||
fix. In the days up to 2026-11-02 the controller was taken apart without this answer: jobs nobody had named (making
|
||||
a provision's credentials for a new consumer, planning a module's next build) surfaced only after the delivery; a
|
||||
module was moved without reading how its pieces were wired; a failure label was changed without reading what relied
|
||||
on it; and each check found one more fault the preparation should have known.
|
||||
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:
|
||||
- first, the section `What exists today` with item 3a's three answers;
|
||||
- the issues it builds, in order, each with: the files and functions it changes, the decisions it builds
|
||||
with (quoted, with where each was decided), and its acceptance criteria (item 5), each with the test that
|
||||
proves it;
|
||||
@@ -203,9 +154,11 @@ is a breach the lead names.
|
||||
hours; it ran past its 30-minute bound for over an hour, and the reports could only say "still on part D".
|
||||
6. **List every part the change reaches, before any agent starts**, so each is an issue of the milestone and
|
||||
nothing is found on the way. Read, for each repository of the milestone, what it takes from the others and how:
|
||||
the SDK's users (they pin an SDK commit, and a pin to a commit no branch holds is a download that fails later,
|
||||
issue 1148); every program that parses a manifest strictly (search `ParseManifest` across every repository, with
|
||||
the SDK commit each pins), which must take an SDK that knows a new manifest word (2026-10-21: design 60's
|
||||
the users of every package the change alters, mesh-sdk and each seat's package (a seat's contract is a package its
|
||||
holder's repository publishes, and a user takes it as a package, never as another repository's source; each user
|
||||
builds against the latest published version, and in the group's check against the group's head, so nobody writes a
|
||||
version or a commit for them: ADR 0573); every program that parses a manifest strictly (search `ParseManifest`
|
||||
across every repository), which must take an SDK that knows a new manifest word (2026-10-21: design 60's
|
||||
catalog milestone found mesh-build-go and the delivery's indexer reading manifests with an SDK from before the
|
||||
word); the SDK's list of a defined seat's verbs, which must name a verb a module adds (2026-10-21: node-files' two
|
||||
new verbs were refused on the claim); the node-engine's decoder for a new key on the wire; the route by which a
|
||||
@@ -320,12 +273,6 @@ is a breach the lead names.
|
||||
key and stream the diff creates or writes (search the diff for the bucket and stream names) and finds each in the
|
||||
lifetime answer with a deleter and event, or a time to live; it reads the switch-over answer and checks it was done as
|
||||
written or that the operator's choice is recorded; it reads the integration-level answer against the check the pull requests will ask. A missing answer, or a key or stream the diff creates that the answer omits, is a change asked.
|
||||
6b. **The reviewer holds the change to each context file's `What exists today`** (step 0, item 3a). It refuses to
|
||||
review a repository whose context file lacks the section or leaves one of its three answers open: that is the
|
||||
prepare step undone, sent back to the lead, and not a round. For every part the diff removes, moves or changes the
|
||||
form of, it runs the recorded search again on the reviewed tree and finds each effect and each reader in the second
|
||||
answer; for each, it reads in the diff that the third answer's fate happened and that its test exists. An effect or a
|
||||
reader the search finds and the answer omits, or a fate the diff does not carry out, makes the change wrong.
|
||||
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
|
||||
@@ -377,8 +324,9 @@ is a breach the lead names.
|
||||
**While a group is being checked, nothing else is merged or pushed into a repository that is one of its members:**
|
||||
a moved main asks the group's check again. How it is checked: the lead's wait list names the group's members, and
|
||||
`mesh-delivery.show` shows one check per head.
|
||||
**A group is one check.** Before the push, every member pins the group's own mesh-sdk head exactly (the checker refuses an
|
||||
older commit of the same branch), then all members are pushed and opened together, so the group's check runs once. A head the review replaces
|
||||
**A group is one check.** No member names the group's mesh-sdk head, or any package's version, by hand: the group's
|
||||
check builds every member against the group's own package heads (ADR 0573 §4). All members are pushed and opened
|
||||
together, so the group's check runs once. A head the review replaces
|
||||
counts for nothing against a running check; say so, and ask no other until the new head is reviewed.
|
||||
**Before a session trusts an agent's "pushed", it reads the forge branch's head** (`git ls-remote origin refs/heads/<branch>`) and compares it with the commit reviewed; an agent may commit on a detached HEAD and report a push that never happened (2026-10-26: the first check of a reviewed fix failed "not our ref"). The pull request is opened only on that read head. Pull requests in two or
|
||||
more repositories that share the branch name are one delivery group (ADR 0239 §2).
|
||||
@@ -413,20 +361,13 @@ is a breach the lead names.
|
||||
(also in a delivery group that holds the catalog's pull request); the lead runs mesh-controller's unit
|
||||
suite with `MESH_CHECK_BESIDE` naming a directory that holds the catalog's branch, as mesh-controller's
|
||||
`internal/beside` says.
|
||||
**Before the push, every member's suites run as the running checker runs them**: each sibling repository placed
|
||||
beside it by main's beside rules, the node-engine at the commit the machines run, and the running indexer's judge
|
||||
over every manifest the change touches. On 2026-11-02 release sdk1 failed four checks one fault at a time (test
|
||||
globals left set, a sibling placed otherwise, the checker's manifest archive, a composition step the old checker could not
|
||||
build), each of which a run as the checker places things would have shown before the first push. How it is
|
||||
checked: the pull request's description names the placement, the node-engine commit and the judge run used
|
||||
locally; a check that fails on a placement the local run did not use is named by the lead at the next report.
|
||||
2. **The checker's merge check runs the integration tests once, as the gate** (its one status, `mesh/check`). No
|
||||
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 one more push of ADR 0548). The check is **read, never re-asked to see if it passes**: a
|
||||
check asked again without a new, reviewed head spends the build capacity for nothing. **The failure is read from the check's whole log**, never
|
||||
reasoned from its summary: `mesh-delivery-checker.checker_pull` names every failure the verdict holds, one line each, and the full report
|
||||
of the check. A check's summary picks only some failing lines;
|
||||
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 failure the check hides or loses** (a verdict naming only the first failed test, a log not kept) is a fault of the
|
||||
checking tool: it is fixed there, the same turn (instruction fix-a-bug-met-twice), not asked around.
|
||||
@@ -438,13 +379,6 @@ is a breach the lead names.
|
||||
which tests run at which level is the build tag's (ADR 0445 §2), and hq's checks are Go tests like any repository's
|
||||
(ADR 0527). The prepare step looks for this: a change to what the checker's build module selects (its recipe, a build
|
||||
tag convention) is cut ahead of the milestone.
|
||||
**The same holds for every part that judges a change**: the checker, the indexer and its judge, the grants part and
|
||||
the build modules. A release that changes one of them is judged by the version that runs, so whatever the running
|
||||
version cannot judge is delivered first as a change of its own, or left out and added back once the new part runs.
|
||||
On 2026-11-02 release sdk1 delivered the build module's relative replaces first, and took the checker's new
|
||||
composition call out of the release, adding it back in the next pull request once the new node-engines answered it. How it is checked: the prepare step's
|
||||
milestone file lists every judging part the release changes and how each is delivered first or left out; the review
|
||||
refuses a group that holds one without that line.
|
||||
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 milestone's acceptance sentence; the forge's checks run it (ADR 0391 §1, §6). Unit tests alone are not
|
||||
@@ -472,20 +406,29 @@ is a breach the lead names.
|
||||
moved to include it): one Approve, one delivery. The old Approve ask closes itself on the new head and the new
|
||||
head is asked once its checks pass. How it is checked: a follow-up that sits in a second pull request while the
|
||||
first still waits shows in `gitea.gitea_list_pull_requests` as two open pull requests of one lead.
|
||||
6. **A member pins mesh-sdk only at a commit that is a pull request's head**, merged or open (2026-10-28: the check
|
||||
refused a pin to a merge commit on main as "on no pull request", though its tree equalled the head's). How it is
|
||||
checked: the check's pin step; a refusal "on no pull request" is this rule skipped.
|
||||
7. **Two groups that pin the same module's mesh-sdk conflict in `go.mod` and `go.sum`.** The group that merges
|
||||
second merges main in and re-pins once, to a pull request head that holds both changes, after the first group
|
||||
is merged and not before (a re-pin before makes the first group conflict). How it is checked: the check's merge
|
||||
step; a group that conflicts after the first one merged is this rule skipped.
|
||||
8. **A group that adds a seat, a verb or a manifest field in mesh-sdk and uses it in a module's manifest in the
|
||||
same group cannot pass its check** until the running assigner pins a mesh-sdk that defines it, because the
|
||||
check composes with the running assigner's mesh-sdk (issue 1585). The assigner's pin ships first, as its own
|
||||
release. How it is checked: the check's composition step; a composition refusing the new word in such a group
|
||||
is this rule skipped.
|
||||
6. **Nobody pins a package** (ADR 0573 §2, §4). No commit writes a mesh package's version or commit into `go.mod`,
|
||||
`go.sum` or `package-lock.json` to choose what a module builds against: every module builds against the latest
|
||||
published version of each package it uses, a publish rebuilds every dependent (ADR 0573 §3, along ADR 0267 §3's
|
||||
edges), and a group's check builds its members against the group's own heads. This replaces the pin rules learned on
|
||||
2026-10-28 (a pin only at a pull request's head; two groups pinning one module's mesh-sdk re-pin after the first
|
||||
merges): each was a step the developer ordered by hand, which the operator ruled out (2026-10-21, 2026-10-31). A
|
||||
check that refuses a member for the version it names, an older head, a merge commit or a commit on no pull request
|
||||
is a fault of the check: say it to the operator and fix the check under issue 1585, never
|
||||
re-pin around it. How it is checked: the review refuses a hand-written version or commit of a mesh package; a check
|
||||
of the checker that refuses one is to be built (issue 1585).
|
||||
7. **A seat's contract is its holder's package, and a module takes another repository's code only as a package**
|
||||
(ADR 0573 §1). A new or changed seat's records, keys, events, verbs and message schemas go into a package its
|
||||
holder's repository publishes (a Go module or a node package), never into mesh-sdk, which keeps only what every
|
||||
seat shares; a user imports that package, never a path into, a copy of or a `replace` to another repository's
|
||||
source. A change that alters a seat whose contract mesh-sdk still holds moves it to the holder's package in the same
|
||||
change (issue 167). How it is checked: the review refuses a seat-specific type, key or verb added to mesh-sdk and an
|
||||
import of another repository's source; no check refuses them yet (issue 1585).
|
||||
8. **A group that adds a seat, a verb or a manifest field and uses it in a module's manifest in the same group is
|
||||
composed with the group's own mesh-sdk head** (ADR 0482 §3), so it passes in one group, with no release ahead of
|
||||
it. How it is checked: the check's composition step; a composition that refuses the new word in such a group is a
|
||||
fault of the check (issue 1585).
|
||||
9. **A new head on an open pull request needs its `Reviewed-by` line at that head before the Approve is asked,
|
||||
even for a pin-only commit.** How it is checked: the Approve's ask names the review record;
|
||||
even for a one-line commit.** How it is checked: the Approve's ask names the review record;
|
||||
an ask whose line names another commit is this rule skipped.
|
||||
|
||||
**One exception: a catalog change that uses a new manifest word.** The catalog's merge check reads
|
||||
@@ -701,12 +644,6 @@ issues in `fixing` that nobody works on reads to the operator as stuck work.
|
||||
request into a feature branch exists. A pull request per issue, or an agent waiting on a merge check in
|
||||
step 1, is the failure this playbook prevents.
|
||||
- A context file exists per touched repository under `.work/<slug>/` while the feature runs.
|
||||
- Every context file opens with `What exists today` holding step 0 item 3a's three answers, each search written as run
|
||||
(`grep -n -A3 'What exists today' .work/<slug>/*.context.md`), and its time (the file's commit or the lead's note) is
|
||||
older than the repository's first commit of the feature; the review report says it ran the searches again (step 2,
|
||||
item 6b). A fixing agent started on a file without the section, or an effect of a removed or moved part met only
|
||||
after the delivery, is the prepare step skipped, named in the next detailed report and written into the playbook's
|
||||
lessons here.
|
||||
- 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
|
||||
|
||||
@@ -33,12 +33,7 @@ processing power"). Everything before the push is local and fast, and no try is
|
||||
## Steps
|
||||
|
||||
1. **Read the issue and the code it names** (the session's own model, or one read-only agent on the smaller
|
||||
model answering exact questions with file:line). **Read what exists in the fix's area today, briefly, before any
|
||||
code** (playbook [07](07-feature-branches.md) step 0 item 3a, at a small fix's size): what the code and the live mesh
|
||||
(through the mesh MCP server's tools) do there now, and for each value, label, file or job the fix changes or removes,
|
||||
every reader that relies on it, found by a search across the repositories (written down as run) and not from memory,
|
||||
with what happens to each after the fix. It is a few lines in the issue's note, under `What exists today`, and the
|
||||
review runs the search again; a reader it finds that the note omits makes the fix wrong. If a design question is open, it is settled before any
|
||||
model answering exact questions with file:line). If a design question is open, it is settled before any
|
||||
code is written: on the issue, through the knowledge base (`knowledge-base.ask` first: what is decided is read before anything is designed), 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. **The session's own model (the larger) then writes the issue's acceptance
|
||||
@@ -64,7 +59,7 @@ processing power"). Everything before the push is local and fast, and no try is
|
||||
writer writes it; the review asks it.
|
||||
4. **One review of the local commit** (a reviewer that did not write it, larger
|
||||
model; at most 3 rounds, as playbook 07; from round 2 only what is wrong; it reads each acceptance criterion
|
||||
against its test, as playbook 07 step 2 item 6; it runs the `What exists today` search again, as playbook 07 step 2 item 6b; a finding outside the issue's done-when and its acceptance
|
||||
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; it judges the integration tests by reading them against the code, as playbook 07 step 2 item 4 says; it answers playbook 07 step 2 item 10's two questions, on state and seams and on mesh-agnostic repositories, in its report). 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 Approve of its merge is its review, and the review record says so.
|
||||
@@ -151,9 +146,6 @@ reading itself and uses one subagent only for the review.
|
||||
- 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 with `What exists today` (`issue-tracker.get`) older than its first commit, its
|
||||
search written as run; one without is step 1 skipped, and a reader of a changed value met only after the delivery
|
||||
is that note left short.
|
||||
- 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.
|
||||
|
||||
@@ -221,8 +221,8 @@ code{font:12.5px ui-monospace,monospace;background:var(--soft);padding:1px 5px;b
|
||||
</g>
|
||||
<!-- lead -->
|
||||
<rect x="110" y="56" width="160" height="52" rx="8" fill="var(--card)" stroke="var(--dec)" stroke-width="2"/>
|
||||
<text x="190" y="78" text-anchor="middle" class="t">What exists · survey</text>
|
||||
<text x="190" y="96" text-anchor="middle" class="mute">criteria · context file per repo</text>
|
||||
<text x="190" y="78" text-anchor="middle" class="t">Survey · criteria</text>
|
||||
<text x="190" y="96" text-anchor="middle" class="mute">context file per repo</text>
|
||||
<rect x="340" y="56" width="140" height="52" rx="8" fill="var(--card)" stroke="var(--dec)" stroke-width="2"/>
|
||||
<text x="410" y="78" text-anchor="middle" class="t">Up to trunk</text>
|
||||
<text x="410" y="96" text-anchor="middle" class="mute">each branch, local</text>
|
||||
@@ -275,7 +275,6 @@ code{font:12.5px ui-monospace,monospace;background:var(--soft);padding:1px 5px;b
|
||||
<div class="box">
|
||||
<h3>Step 0 · what the lead writes before any agent starts</h3>
|
||||
<ul style="margin:6px 0;padding-left:20px;font-size:14px">
|
||||
<li><span class="tag new">new</span>What exists today, read from the code and the live mesh, never from memory: every effect of a part removed or moved (bus writes, files, credentials, plans, processes, calls answered), every reader relying on each, what happens to each after the change and its test. It opens each context file; no code before it is complete, and the review refuses work without it.</li>
|
||||
<li>Every open question settled: on the issue, in the knowledge base, or by the operator.</li>
|
||||
<li>Into each issue: every file and test it touches (file:line), how it is checked and delivered, a group's merge order.</li>
|
||||
<li><span class="tag new">new</span>Every other reader and writer of each value the change touches, each with a criterion.</li>
|
||||
@@ -416,15 +415,15 @@ const P = [
|
||||
{n:"01",name:"Research",kind:"idea",c:"--idea",
|
||||
trig:"An idea worth investigating before it is committed to design. A symptom is not research.",
|
||||
who:"The session that heard the idea leads.",
|
||||
steps:["Read what is known: ask, get, the tracker's list.","Ask 3 to 5 setup questions, one at a time, each with a recommendation.","Check every option against the mission and how-we-build first.","Reserve a number (kind research), never pick one by hand.","Propose the effort: Overview, Questions, Method, sections named by title, no file references.","Research; findings in the section Findings, one version per settled set.","Try at the cheapest level that answers the question.","Write the Domains: modules, seats, glossary words added or retired.","Write the Direction: decisions, rejected options, acceptance tests.","Ask the operator to confirm the direction, one point at a time.","Review the direction by architectural domain: security, seams, running qualities, health, events and logs, testing, upgrade, tools.","If the review asks changes: propose again, at most three times."],
|
||||
steps:["Read what is known: ask, get, the tracker's list.","Ask 3 to 5 setup questions, one at a time, each with a recommendation.","Check every option against the mission and how-we-build first.","Reserve a number (kind research), never pick one by hand.","Propose the effort: overview, questions, method, with links.","Research; findings in 04-findings, one version per settled set.","Try at the cheapest level that answers the question.","Write 03-domains: modules, seats, glossary words added or retired.","Write 05-direction: decisions, rejected options, acceptance tests.","Ask the operator to confirm the direction, one point at a time.","If the review asks changes: propose again, at most three times."],
|
||||
prod:"A research effort in the knowledge base, status active, every version kept. Nothing in hq.",
|
||||
next:"02",bounds:"Setup 30 min · proposing 15 min · each experiment 30 min · direction 30 min · review by architectural domain 20 min · report every 10 min",
|
||||
next:"02",bounds:"Setup 30 min · proposing 15 min · each experiment 30 min · direction 30 min · report every 10 min",
|
||||
prev:["Research that measured nothing","A recommendation the operator must argue back","Efforts copied into hq"],
|
||||
check:"propose refuses rule breaks; the knowledge base reviews; the records test on every PR."},
|
||||
{n:"02",name:"Graduation & design change",kind:"idea",c:"--dec",
|
||||
trig:"A research effort concludes, or an existing design must change.",
|
||||
who:"The lead, on the larger model; the decision is recorded before the design moves.",
|
||||
steps:["Check the conclusion against mission, context and effect.","Reserve a number per decision.","Propose one record per choice that can be reversed on its own.","Pre-check, then propose siblings together.","On changes: revise whole; after three rounds fix findings yourself.","A finding that would change what was decided: ask the operator.","needs-operator: wait for the operator's Approve.","Amend the design, or create a to-be design with status designed.","Add the decisions, updated:, and the effort's glossary words.","Review the design by architectural domain before it is merged.","Hand the design to the breakdown (04).","Close the effort: graduated with became links, or abandoned.","When it ships: write the as-is, set the to-be implemented."],
|
||||
steps:["Check the conclusion against mission, context and effect.","Reserve a number per decision.","Propose one record per choice that can be reversed on its own.","Pre-check, then propose siblings together.","On changes: revise whole; after three rounds fix findings yourself.","A finding that would change what was decided: ask the operator.","needs-operator: wait for the operator's Approve.","Amend the design, or create a to-be design with status designed.","Add the decisions, updated:, and the effort's glossary words.","Hand the design to the breakdown (04).","Close the effort: graduated with became links, or abandoned.","When it ships: write the as-is, set the to-be implemented."],
|
||||
prod:"Decision records; a to-be design with frontmatter; glossary entries; later the as-is.",
|
||||
next:"04",bounds:"One sitting of about an hour",
|
||||
prev:["A decision rewritten silently","A deferral nothing tracks","A design amended without a decision"],
|
||||
@@ -464,15 +463,15 @@ const P = [
|
||||
{n:"07",name:"Features",kind:"build",c:"--build",
|
||||
trig:"Work that changes code across issues or repositories, too large for a small fix.",
|
||||
who:"A lead (larger model), one fixing agent per repository (smaller), one reviewer (larger).",
|
||||
steps:["Prepare: name the branch, make the worktrees, settle every question.","Every agent is one of hq's definitions (mesh-preparer, mesh-developer, mesh-code-reviewer, mesh-watcher); the lead writes only the handover. (new)","The prepare starts from the knowledge base and names the mesh path of every interaction; the seams review judges it before code. (new)","Read what exists today from the code and the live mesh: every effect of what is removed or moved, every reader of each, what happens to each and its test. No code before it is complete; the review refuses work without it. (new)","Write one context file per repository, opening with what exists today.","Write the findings and acceptance criteria into each issue.","Survey every other reader and writer of each changed value. (new)","Answer three questions in the milestone file: who deletes each new key and stream, how the switch-over is tried on one node first, what the one check does not cover. The review checks them. (new)","List every part the change reaches; copy fixtures from the real writer.","Develop: one agent per repository, one commit per issue.","A test fails first on behaviour, not on compiling. (new)","Bring each branch up to the trunk; nothing is pushed, no try runs. (new)","Review all of it, reading the integration tests against the code; at most 3 rounds; the report answers the state-and-seams and mesh-agnostic questions. (new)","A group pins the group's own mesh-sdk head in every member. (new)","Push the reviewed heads once and read each forge head against the reviewed commit; no draft pull request, no fix round on an open one. (new)","Open one pull request per repository, together, with Reviewed-by and Pushed-by; the one check runs. A failed check is read, fixed, reviewed, pushed once more. (new)","The operator's Approve on Telegram; the group merges in one pass.","Follow the delivery in the status view (moving, settled, stuck, delivered) to done; resolve the issues; clean up. (new)","When the builder cannot deliver its own fix: revert, a build by a session's call, a registration at the terminal, copies kept outside the store; a broken controller is put back by a want the operator writes, a hand build by mesh-hand-compose and a want. (new)"],
|
||||
steps:["Prepare: name the branch, make the worktrees, settle every question.","Write one context file per repository.","Write the findings and acceptance criteria into each issue.","Survey every other reader and writer of each changed value. (new)","Answer three questions in the milestone file: who deletes each new key and stream, how the switch-over is tried on one node first, what the one check does not cover. The review checks them. (new)","List every part the change reaches; copy fixtures from the real writer.","Develop: one agent per repository, one commit per issue.","A test fails first on behaviour, not on compiling. (new)","Bring each branch up to the trunk; nothing is pushed, no try runs. (new)","Review all of it, reading the integration tests against the code; at most 3 rounds; the report answers the state-and-seams and mesh-agnostic questions. (new)","No member pins a package: the group's check builds every member against the group's own heads (ADR 0573). (new)","Push the reviewed heads once and read each forge head against the reviewed commit; no draft pull request, no fix round on an open one. (new)","Open one pull request per repository, together, with Reviewed-by and Pushed-by; the one check runs. A failed check is read, fixed, reviewed, pushed once more. (new)","The operator's Approve on Telegram; the group merges in one pass.","Follow the delivery in the status view (moving, settled, stuck, delivered) to done; resolve the issues; clean up. (new)","When the builder cannot deliver its own fix: revert, a build by a session's call, a registration at the terminal, copies kept outside the store; a broken controller is put back by a want the operator writes, a hand build by mesh-hand-compose and a want. (new)"],
|
||||
prod:"One pull request per repository, one source revision delivered, resolved issues, no branch or worktree left.",
|
||||
next:"02 (when it ships)",bounds:"Prepare 15 min · 30 min per issue · review 15 min, ≤ 3 rounds · one check, pushed once",
|
||||
prev:["Pull requests as scratchpads","Context bought again and again","A change built on what was remembered, not on what runs","Review rounds spent on what a run would show","A node agent's bus reads, proven only by review"],
|
||||
prev:["Pull requests as scratchpads","Context bought again and again","Review rounds spent on what a run would show","A node agent's bus reads, proven only by review"],
|
||||
check:"The checker's one status, mesh/check; the review record; the tracker's move."},
|
||||
{n:"08",name:"Small fixes",kind:"build",c:"--build",
|
||||
trig:"One issue in one repository, about an hour of work.",
|
||||
who:"The session, with one reviewer on the larger model.",
|
||||
steps:["Read the issue and the code; settle any open question.","Read what exists in the fix's area today, briefly: every reader of what the fix changes. (new)","Write the acceptance criteria.","Move the issue to fixing.","Build on fix/<issue>-<slug> in its own worktree.","A test that fails today first, on behaviour. (new)","Nothing is pushed and no try runs before the review. (new)","One review of the local commit.","Push the reviewed head once; only then open the pull request. (new)","The operator's Approve; the merge; the delivery, followed in the status view. (new)","Prove the fix live and resolve the same hour.","During a milestone: ride in its delivery group, unless broken for the operator now. (new)"],
|
||||
steps:["Read the issue and the code; settle any open question.","Write the acceptance criteria.","Move the issue to fixing.","Build on fix/<issue>-<slug> in its own worktree.","A test that fails today first, on behaviour. (new)","Nothing is pushed and no try runs before the review. (new)","One review of the local commit.","Push the reviewed head once; only then open the pull request. (new)","The operator's Approve; the merge; the delivery, followed in the status view. (new)","Prove the fix live and resolve the same hour.","During a milestone: ride in its delivery group, unless broken for the operator now. (new)"],
|
||||
prod:"One pull request, one delivery, a resolved issue.",
|
||||
next:"—",bounds:"About an hour · one review, one push, one check",
|
||||
prev:["A fix reviewed after its pull request opened","A long part run twice","A fix waiting on someone else's delivery","Ten SDK merges and eight controller merges in a day, one per fix"],
|
||||
|
||||
@@ -7,14 +7,9 @@ code:
|
||||
- mesh-tools src/broker-amqp.ts (to be replaced)
|
||||
- mesh-catalog modules/nats (to be written)
|
||||
- mesh-sdk src (the protocol's NATS binding, step 3)
|
||||
- mesh-tools node-tools/internal/bus (a module's state, ADR 0201; the new calls and apply, ADR 0595, ADR 0597)
|
||||
- mesh-sdk (the typed bus-state entry, its binding and the new calls' package, ADR 0595)
|
||||
- mesh-delivery (the judge's rules and the grants part's binding, ADR 0595)
|
||||
updated: 2026-11-03
|
||||
- mesh-tools node-tools/internal/bus (a module's state, ADR 0201)
|
||||
updated: 2026-10-10
|
||||
decisions:
|
||||
- "ADR 0595"
|
||||
- "ADR 0596"
|
||||
- "ADR 0597"
|
||||
- "ADR 0289"
|
||||
- "ADR 0264"
|
||||
- "ADR 0235"
|
||||
@@ -43,11 +38,6 @@ under which subject, with which guarantee, under whose account; how a node joins
|
||||
reaches a tool; how the mesh moves from the bus it has to this one; and how each claim is checked.
|
||||
Prose and diagrams only; no configuration is pasted.
|
||||
|
||||
**Amended, 2026-11-03** (ADR 0595, ADR 0596, ADR 0597): what a module keeps on the bus is its **bus state**, a
|
||||
provision of the bus module asked for by template, created by the bus module, read by its owner alone; and the
|
||||
**ledger** is a storage type of its own. § 12 is the design; the `KV_<module>_<name>` row of § 3 and the module's
|
||||
state in § 4 hold for a module still on the old sections.
|
||||
|
||||
## 1. What the bus is for
|
||||
|
||||
**The bus is where the mesh happens.** Not a transport the mesh sends things over — the place a
|
||||
@@ -189,7 +179,7 @@ Core NATS is at-most-once. Everything the mesh must not lose lives in a JetStrea
|
||||
| EVENTS | `mesh.mod.*.event.>` and `mesh.seat.*.event.>`; and `mesh.again.>` (ADR 0264) | limits (age, size), durable consumer per subscribing module | a subscriber that was down catches up; after `max-deliver` attempts the advisory feeds `mesh.events.dead` (its own small stream). *2026-10-08, ADR 0264:* that stream is DEAD_LETTERS, below; each consumer also filters `mesh.again.<consumer>.>`, where a message it gave up on is delivered again to it alone. *2026-10-01:* a build's whole log is here too, as the build seat's `log.<build id>` events (ADR 0157) — one subject per build, a week of retention, read back by `builds --log <id>` with a consumer that is gone when the reading is done |
|
||||
| DEAD_LETTER_NOTICES | the server's maximum-deliveries advisory | work queue, a week; one consumer (the controller), no `max-deliver` | the server's word that a consumer gave up on a message, kept whether or not a controller is listening, until the controller has copied the message (ADR 0264) |
|
||||
| DEAD_LETTERS | `mesh.events.dead.<stream>.<consumer>` | limits: no age, 256 MiB, refuses new when full; a day's de-duplication | every message a consumer gave up on, with its consumer, subject, deliveries and when, until a person delivers it again or drops it with why (ADR 0264); a `max-deliveries` condition is open per consumer while it holds any |
|
||||
| `KV_<module>_<name>` | `$KV.<module>_<name>.>` | the newest value per key — as many past values as the owner declared — no age unless the owner declared one; a value at most 256 KiB, a bucket at most 64 MiB | a module's state (ADR 0201): one per name in a manifest's `state`, created from the catalog on every raise, so it exists before its owner runs anywhere; kept when the module is unassigned, because what it holds is data. *2026-11-03, ADR 0595:* a module's kept data in the new form is created by the bus module from `requires`, by template (§ 12) |
|
||||
| `KV_<module>_<name>` | `$KV.<module>_<name>.>` | the newest value per key — as many past values as the owner declared — no age unless the owner declared one; a value at most 256 KiB, a bucket at most 64 MiB | a module's state (ADR 0201): one per name in a manifest's `state`, created from the catalog on every raise, so it exists before its owner runs anywhere; kept when the module is unassigned, because what it holds is data |
|
||||
|
||||
Tool calls and heartbeats stay on core NATS: a lost heartbeat is the next heartbeat; a lost tool
|
||||
call is a timeout the caller already handles.
|
||||
@@ -276,7 +266,7 @@ expresses this exactly, per subject, and better than a vhost could:
|
||||
permissions for each consumed event's subject, its tool subjects, and that same inbox prefix.
|
||||
Nothing else. A module that tries to publish outside its emits is refused by the server, not by
|
||||
convention.
|
||||
- **A module's state** (ADR 0201; for the new form, § 12.2: the owner's account alone, no reader, ADR 0596), for whichever principal carries the module — today the machine's
|
||||
- **A module's state** (ADR 0201), for whichever principal carries the module — today the machine's
|
||||
runtime, whose grant is the union of its modules': binding to the bucket, reading a key directly,
|
||||
and an ordered consumer for listing and watching, created and deleted on the bucket's own stream
|
||||
and nothing else's; and, for the owner's instances only, publishing under the bucket's own
|
||||
@@ -627,275 +617,3 @@ find what changed and why.
|
||||
that needs it? ADR 0039's test —
|
||||
*does editing it recompile unrelated modules, and does it change often* — probably says no for
|
||||
one instance; worth asking again if a second module needs the same shape.
|
||||
|
||||
## 12. Bus state: what a module keeps on the bus, and the bus module that provides it
|
||||
|
||||
*Added 2026-11-03* (ADR 0595, ADR 0596, ADR 0597, from research 082; the operator, 2026-11-02 20:45: "The names
|
||||
simply represent named configurations or templates"). Until now a manifest declared what a module keeps on the bus
|
||||
in four sections with four vocabularies (`state`, `logs`, `streams`, `objects`), the grants part created each
|
||||
entry once and never changed it, and nothing compared what the bus holds with what was asked. This section is the
|
||||
design of the one form that replaces them; [design 32](32-what-a-module-declares.md) § 4 says how a manifest asks
|
||||
for it, and [design 55](55-the-knowledge-base.md) § 2.1 is its first ledger.
|
||||
|
||||
**Bus state is a provision of the bus module** (ADR 0595 §1). The **bus module** is the module holding the seat
|
||||
`mesh-broker` (today `nats`). A module asks it for each piece of its **bus state** the way it asks postgres for a
|
||||
database: an entry in `requires`, by its local name, its storage type, its template and the template's options.
|
||||
The bus module creates it; the module receives the names on the bus in its membership; nothing else creates it.
|
||||
|
||||
### 12.1 Three layers
|
||||
|
||||
```
|
||||
layer 3 templates state events work queue log objects ledger
|
||||
│ │ │ │ │ │
|
||||
layer 2 storage types key-value │ │ │ object store ledger
|
||||
built on the bucket │ │ │ │ (a stream and
|
||||
stream │ │ │ │ │ key-value buckets)
|
||||
└──────────┴───────────┴──────────┴──────────┴────────────┘
|
||||
layer 1 base type stream
|
||||
```
|
||||
|
||||
A **storage type** is how the bus keeps the data: the **stream** is the base, and the **key-value bucket**, the
|
||||
**object store** and the **ledger** are built on it, each with calls of its own (ADR 0595 §2). A **configuration**
|
||||
is the settings a storage type is given. A **template** is a storage type with a configuration, predefined and
|
||||
named: six exist, from the uses counted on main (158 state entries, 6 event streams, 4 logs, 3 work queues). An
|
||||
**option** is one setting of a template a module may change, within its bound. A module gives a configuration of
|
||||
its own only where no template fits: the bare stream, or a ledger whose history expires.
|
||||
|
||||
**What keeps the data safe is fixed, never an option** (ADR 0595 §3). Each template fixes what happens when it is
|
||||
full, and the judge refuses a manifest that gives it:
|
||||
|
||||
| Template | On | Fixed | Options, their bounds (default) |
|
||||
|---|---|---|---|
|
||||
| state | key-value | refuses when full; a value at most 256 KiB; the bucket at most 64 MiB; one copy, on disk | past values 1 to 64 (1), 0 refused; time to live (none); a lifetime per key; one bucket per machine |
|
||||
| events | stream | drops the oldest when full; the module's own subjects | age 1 to 30 days (7); cap 1 to 8192 MiB (1024); a grant (ADR 0363 §4) |
|
||||
| work queue | stream | removed once taken; refuses when full; one taker; no grant | deliveries 1 to 10; duplicate detection up to its age; cap; age |
|
||||
| log | stream | refuses when full; never rewritten; an entry at most 256 KiB; one subject per key | cap 1 to 8192 MiB (1024); age 1 to 3650 days (none) |
|
||||
| objects | object store | refuses when full; kept for good | cap 1 to 8192 MiB, required; a grant |
|
||||
| ledger | ledger | as log, plus its type and a snapshot per instance; its state and index buckets as state | cap; keeping days only in a ledger's own configuration |
|
||||
| (none: a bare stream) | stream | the module's own subjects; within the mesh's bounds | its own configuration, which must give "when full" (refuse or drop the oldest), a cap of 1 to 8192 MiB and an age of 1 to 3650 days or none, and may give a grant; the judge refuses one missing any of the three |
|
||||
|
||||
The template log is the **module log** of ADR 0297. The mesh, never the module, names the bus objects and their
|
||||
subjects (ADR 0201 §4): `<module>_<local name>`, and a local name holding the separator is refused so no two
|
||||
modules' names meet.
|
||||
|
||||
### 12.2 Who creates it, and how a module receives it
|
||||
|
||||
```
|
||||
indexer's judge ──verdict: accepted, with the module's bus-state entries whole──▶ bus module
|
||||
│ creates each entry
|
||||
▼
|
||||
grants part ◀──────────────── created (module, local name, bus name, template, settings)
|
||||
│ writes the binding into the module's membership
|
||||
▼
|
||||
the tool runner reads the membership ──▶ the module's calls reach its own entries, and only those
|
||||
```
|
||||
|
||||
- **The trigger is the judge's verdict** (ADR 0595 §4), heard as an event that carries the module's entries whole,
|
||||
so the bus module never reads the indexer's state and never polls. It hears the verdicts on a durable consumer, so
|
||||
one given while it was down is heard when it is back, and at start it runs `check` over every accepted manifest,
|
||||
creating what is missing. The judge checks every entry once, at the merge
|
||||
check and at take-in (ADR 0578; ADR 0595 §8).
|
||||
- **The bus module has a process of its own** beside its tool bundle, with a declared health check. Its credential
|
||||
creates streams and buckets under modules' names only, and deletes only through `drop` and `recreate`. Its health
|
||||
turns unhealthy, naming the cause, when an accepted verdict has waited on its durable consumer, unanswered, for
|
||||
more than three minutes, or when its last pass over fill, which runs every minute, is more than three minutes old, so the only guard against a store filling never stops without a sound; another module's entry at 75 percent
|
||||
raises that module's condition and leaves the bus module healthy.
|
||||
- **The binding travels on `created`** (ADR 0595 §1, §7). The grants part, which issues every membership, hears it
|
||||
on a durable consumer and writes the binding (the names on the bus) into the module's membership, kept in its own
|
||||
state `memberships`, so no part writes another's state and a binding outlives the 30 days its event is kept.
|
||||
A module whose binding is missing at start fails loudly, naming the entry; it never starts blind. A `refused`
|
||||
create hands no binding. **A refused or overdue creation is loud**: overdue means no `created` or `refused` within
|
||||
five minutes of an accepted verdict that asks for the entry. The condition is raised by the watchman, which today raises the judge's
|
||||
refusals as findings and gains, through its `consumes`, the accepted verdicts and the seat `mesh-broker`'s `created`
|
||||
and `refused`; never by the bus module, which cannot raise it while it is down; it names the module
|
||||
and the entry; a later `created` clears it, and so does a later accepted verdict of that module that no longer asks
|
||||
for the entry.
|
||||
- **Only the owner reaches it** (ADR 0596 §2). The membership grants the owner's account its own entries and no
|
||||
other module's: the new form has no readers. What another module needs of that data it asks through the owner's
|
||||
tools or seat verbs, and what the owner lets others hear is its declared events, subscribed to through
|
||||
`consumes` (ADR 0363, unchanged). A part watches only its own state, and hears another's events (ADR 0596 §4).
|
||||
|
||||
### 12.3 The seat's verbs, events and fill
|
||||
|
||||
The seat `mesh-broker`, until now a seat with no verbs, gains these (ADR 0595 §5, §6). Where the seat's definition
|
||||
lives, and so the exact form of each verb's arguments, waits for the research effort on how seats are defined,
|
||||
tracked on issue 1966; the build's third step does not start before it.
|
||||
|
||||
| Verb | Does | Who may call it |
|
||||
|---|---|---|
|
||||
| `list` | every entry, its template, its settings, its fill against its cap | any caller whose role grants it (ADR 0386) |
|
||||
| `show` | one entry, the same | the same |
|
||||
| `check` | compares what the bus holds with what was asked, entry by entry | the same |
|
||||
| `orphans` | entries the bus holds that no manifest asks for; never one whose manifest the judge refused or outgrew, nor one of the foundation's old sections | the same |
|
||||
| `create` | creates one entry now | the bus module's own process only |
|
||||
| `apply` | applies a difference that only grows: a larger cap, a longer age | the bus module's own process only |
|
||||
| `recreate` | rebuilds an entry whose settings differ in a way that cannot grow, from its data where the storage type allows | the bus module's process, on the operator's warrant |
|
||||
| `drop` | removes one orphan | the bus module's process, on the operator's warrant |
|
||||
|
||||
`create`, `apply`, `recreate` and `drop` are granted to the bus module's own credential alone, so a module calling
|
||||
them is refused by the bus. A person asks for them through the bus module's tools, and `recreate` and `drop` act
|
||||
only on the operator's warrant (ADR 0259 §6), asked through the operator channel. Before `recreate` or `drop` acts,
|
||||
the bus module takes a stream snapshot of the entry. A drifted entry is said, never quietly fixed: only a difference
|
||||
that grows is applied without a person.
|
||||
|
||||
**Events**: `created`, `refused` (with why), `drifted` and `drift-cleared`, `full` and `full-cleared`, `orphaned`
|
||||
(cleared by a `drop`, or by a manifest asking for the entry again, which emits `created` for it);
|
||||
each carries the module, the local name, the bus name, the template, and the settings asked against those held. The
|
||||
bus module's own kept data stays on the old sections (ADR 0595 §10), because it cannot wait for its own process to
|
||||
create it: it declares this stream in its `streams` section, kept 30 days (ADR 0363's bound), created by the grants
|
||||
part as today, with a cap of 256 MiB; it drops its oldest when full, which loses no live state, because every drift,
|
||||
orphan and fill stands as a condition until it clears. Thirty days, because a drift or an orphan is read back days later by a person; the conditions hold the
|
||||
live state. The tool `history` of one entry reads them back: every event the bus module said of it, in order. This
|
||||
settles where they are kept, which ADR 0595 §12 left to the design.
|
||||
|
||||
**Fill is the bus module's one periodic read** (ADR 0595 §6): every minute, for every entry that refuses when full,
|
||||
because the bus says nothing as a store fills. The condition `full` is raised from 75 percent of the cap and cleared
|
||||
below it. The grants part's `mesh-grants.history-full` moves to it: the new condition is raised first, then the old
|
||||
key cleared. A bare stream that refuses when full is read the same way. **The conditions**, each naming the module
|
||||
and the entry: `full` (cleared below 75 percent), `drifted` (cleared by `drift-cleared`), `orphaned` (cleared by a
|
||||
`drop` or by a manifest asking for it again) and a refused create (cleared by a later `created`).
|
||||
|
||||
**Its tools** (tools-are-how-a-module-is-spoken-to): the four reading verbs, marked reads, answer every entry the
|
||||
bus module created and its fill, and `history` its events for one entry; `create` and `apply`, marked acts, and
|
||||
`recreate` and `drop`, marked dangerous and so granted only by name (ADR 0386 §6, ADR 0387), are its tools for a
|
||||
person. Such a tool, in the bus module's bundle, holds no grant of those verbs' subjects, and neither does the tool
|
||||
runner's account: it hands the request to the process on the process's own tool subject, carrying the caller as
|
||||
the token the identity seat signed (ADR 0386 §8), whose signature the process checks, so no account granted that
|
||||
subject can name another caller; the process acts only for a caller whose role grants that tool, for `recreate` and `drop` only once
|
||||
the operator's warrant is held, and answers what it did. The bus seat's `apply` (a difference that grows) and the
|
||||
ledger's `apply` (§ 12.4) are two verbs on two subjects, granted apart; this design writes them qualified. Its health
|
||||
names the process's last pass, the entries it could not create, and the entries over 75 percent.
|
||||
|
||||
### 12.4 The ledger
|
||||
|
||||
A **ledger** is a storage type whose state is derived from its history (ADR 0597). It is built on a stream and
|
||||
key-value buckets, and asked for with the template ledger.
|
||||
|
||||
```
|
||||
module ──apply(instance, operation)──▶ tool runner on its own machine
|
||||
│ 1 checks the operation against the type
|
||||
│ 2 reads the history's last sequence for the instance; replays if the state is behind
|
||||
│ 3 appends to the history, that sequence expected ──▶ history (stream, a subject per instance)
|
||||
│ 4 derives the state by the operation's effect ──▶ state (key-value, updated at the revision read)
|
||||
│ 5 writes the instance's snapshot ──▶ snapshot subject (latest only, never expires)
|
||||
▼
|
||||
answers: taken, with its sequence (and "state behind" when the state bucket is full)
|
||||
module's process: a watcher keeps each declared index (a second key-value bucket) from the history
|
||||
```
|
||||
|
||||
- **A type** is a schema and the operations allowed on it; an **instance** of a type is named by a path
|
||||
(`listing/a17`, `decision/0290`); every change is an **operation**, kept in the instance's history.
|
||||
- **A ledger effect** is what an operation does to the state, one of a fixed set: set fields, set an entry of a
|
||||
map, append to a list, remove (ADR 0597 §2). An effect copies only the fields its type names, and may add the
|
||||
operation's sequence and author. Where a type pairs a text field with its digest, `apply` refuses an operation
|
||||
whose digest does not match. A module's own rules are checked by the module before it calls `apply`, never by the
|
||||
effects. Only the owning module calls `apply` on its ledger, for every effect, remove among them; a person
|
||||
removes through the owner's tools. This settles who may call the remove effect, which ADR 0597 §13 left to the
|
||||
design.
|
||||
- **One writer wins, and the state never goes back** (§5): the expected sequence makes the bus refuse a racing
|
||||
writer; the state is written as an update of the revision read, never a plain put; when that update loses to
|
||||
another writer, `apply` answers success with its operation's sequence and re-reads, never appending again.
|
||||
- **A full state bucket loses no operation** (§6): `apply` answers that the operation is taken, with its sequence,
|
||||
and that the state is behind because the bucket is full; the next `apply` after room is made catches it up.
|
||||
- **The author is proven** (§7): `apply` stamps the owning module's authenticated account and the account that
|
||||
called the owner's tool. The owner passes that author on by naming the tool call it serves: the tool runner, which
|
||||
served that call and proved its caller, stamps the caller it proved, so the module cannot set the author to words of
|
||||
its own; an `apply` naming no call in flight carries the owner's account alone, and one naming another module's
|
||||
call is refused. A copy of old records (design 55 § 8.1) is stamped with the owner's account and keeps the old
|
||||
author as a separate field marked imported, never as the proven author.
|
||||
- **Every instance has a snapshot** (§8): the latest state on a subject of its own, never expiring, written by every
|
||||
`apply`. It makes a kept ledger's rebuild fast and an expiring ledger's possible; a repair that finds it behind
|
||||
the state rewrites it.
|
||||
- **Only the tool runner's ledger credential publishes on a ledger's history** (§9): one per module, held by the
|
||||
tool runner and never by the module's own code, whose account may read its ledger and not publish on it. The grants
|
||||
part issues it with the module's membership on that machine, scoped to the subjects of that module's ledger
|
||||
histories and snapshots, and removes it when the module is unassigned from the machine. Only the
|
||||
owner's account reads the history, the snapshot and the state.
|
||||
- **An index** is a second key-value bucket kept by a watcher in the owning module's process, rebuildable from the
|
||||
history; it raises a condition when it lags behind (§4).
|
||||
|
||||
### 12.5 The calls, and where they live
|
||||
|
||||
The new calls live in a small mesh-sdk package of their own, served by the tool runner and imported only by the
|
||||
modules that use them; the module library, which 112 modules import, does not change (ADR 0595 §9). They are the
|
||||
object store's (put, get, list, delete, info, watch); a stream's read-back and fill; a work queue's size and dead
|
||||
letters; and the ledger's: `apply`, `get`, `history`, `watch`, `query`, and its repairs `rebuild` (the state from
|
||||
the history or the snapshot), `reindex`, `snapshot` and `lag` (ADR 0597 §10). The tool runner serves the repairs as
|
||||
tools of every module that owns a ledger, limited to its own ledgers, so no owner declares them and the next ledger's
|
||||
owner has them on its first day. mesh-sdk's catalog and membership
|
||||
packages change once for the typed `requires` entry and the binding, rebuilding about 30 modules.
|
||||
|
||||
### 12.6 The move, and the order of the build
|
||||
|
||||
**The build, each step undoable on its own** (ADR 0595 §10): (1) mesh-sdk's typed `requires`, binding and calls
|
||||
package; (2) the judge's rules; (3) the bus module's process, verbs and events, creating only for the new form, with the grants part's writing of
|
||||
the binding on `created`; (4) the tool runner's calls, the ledger's `apply` and its ledger credential among them; (5)
|
||||
modules move, each owner first. Each undo: undoing step 2 refuses the new form again, which no module uses before
|
||||
step 5; undoing step 3 leaves what it created in place, and a module moved after it fails loudly on its missing
|
||||
binding; undoing step 4 leaves a moved module without its calls, which the placement holds off that machine. The
|
||||
placement holds a moved module off a machine whose tool runner does not serve the typed binding and the calls the
|
||||
module asks for (a ledger's `apply` among them), and says so; where the placement reads what a tool runner serves is
|
||||
left to the build of step 4 and tracked on issue 1966.
|
||||
|
||||
**A module moves when it is next touched** (ADR 0595 §11), keeping its local names and carrying its old settings
|
||||
as options, so its bus objects and data stay and no drift is raised. It asks for each entry once: an old section
|
||||
and the new form are never both used for one local name. During the move the grants part keeps creating the old
|
||||
sections' entries, so each entry has one creator. The indexer counts the modules on each old section, a tool reads
|
||||
the count, and the judge refuses a section once its count is 0. The foundation's own kept data (the bus module's,
|
||||
the grants part's and the indexer's) stays on the old sections for good and is not counted: those modules cannot
|
||||
wait for a process that needs them first.
|
||||
|
||||
**Readers move after their owners** (ADR 0596 §3). Today 195 `reads` entries in 37 modules read another module's
|
||||
bus state, the indexer's 36 times and the assigner's 30. An owner declares the events and gains the tools its
|
||||
readers need before any reader moves: the judge refuses a moved module's subscription to an event its owner does
|
||||
not declare, and the review of a reader's move reads that the owner's tool is on main and delivered. These moves
|
||||
are tracked by issue 1966.
|
||||
|
||||
### 12.7 How it is checked
|
||||
|
||||
| Rule | Checked by |
|
||||
|---|---|
|
||||
| A manifest's bus state is valid, once | the judge's unit tests, each refusal naming the field: an unknown template, an option outside its bound, past values 0, "when full" given, a fixed setting given, a name used twice across storage types, two modules' bus names alike, a `reads` in the new form, an effect outside the fixed set (ADR 0595 §8, ADR 0596 §2, ADR 0597 §2) |
|
||||
| The bus module creates, and the binding follows | live, by tool call: a module asking for a state, a log, objects and a ledger in `requires` receives each binding in its membership, written after `created`, never before; `show` gives each entry's template, settings and fill; a module calling `create` is refused |
|
||||
| The bus module runs whole | one integration test in the nats module's repository that starts the bus module whole against the real bus server: a verdict, then `created`, then the binding in the membership; a restart that catches up a verdict given while it was down; a bucket whose settings differ raises `drifted`, the seat's `apply` of a larger cap clears it with `drift-cleared`; a refused create raises its condition and hands no binding; `recreate` and `drop` without the operator's warrant are refused, and with it act after a stream snapshot; an orphan is listed, raises `orphaned`, and goes only by `drop`; each template at a small cap refuses, events drop the oldest, and `full` is raised at 75 percent and cleared below |
|
||||
| Drift and orphans, live | live, made only by a manifest change: a manifest that shrinks a setting raises `drifted`; the conditions clear as above. Nothing is filled on the live bus, and no orphan is made there |
|
||||
| Overdue and stopped are loud | live, carried by the change of the first module that moves in step 5, never by a merge made for the test alone: with the bus module's process stopped, an accepted manifest asking for an entry raises the overdue condition within five minutes, from the watchman, and a later `created` clears it; the bus module's health turns unhealthy when its pass is more than three minutes old |
|
||||
| A person's tools reach the process, and only by role | live: a role without the dangerous grant is refused `drop`'s tool; the tool runner's account holds no grant of `create`, the seat's `apply`, `recreate` or `drop` |
|
||||
| The author is the proven caller | the ledger's integration test: an `apply` naming another module's tool call is refused; the author stamped is the caller the tool runner proved, whatever the module passed |
|
||||
| Only the owner reaches its data | live: another module's account reading an entry in the new form, a ledger's history, snapshot or state among them, is refused; a raw publish on a ledger's history is refused |
|
||||
| The ledger keeps its promises | one integration test against the real bus (ADR 0597's How it is checked): two writers race and one is refused; two interleaved applies leave the state at the higher sequence; a write cut between history and state is repaired; a full state bucket answers taken with its sequence and catches up; an idle instance whose operations expired rebuilds from its snapshot; `rebuild`, `reindex` and `lag` repair by hand; a forged author is refused; every `apply` leaves the snapshot equal to the state; an index behind raises its condition and `reindex` clears it |
|
||||
| Only the owner applies | the ledger's integration test: another module's ledger `apply` on the knowledge base's ledger is refused |
|
||||
| A bare stream is complete | the judge's unit test: a bare stream missing "when full", a cap or an age is refused, naming the field |
|
||||
| The calls rebuild only their users | a read of the delivery plan: only the importers of the calls package rebuild when it changes |
|
||||
| The move keeps data | live: a module moved from `state` keeps its bucket and data and raises no drift; a new entry in an old section is still created by the grants part; the count falls by one; at 0 a manifest using the section is refused and the foundation's are still accepted; a moved module is held off a machine whose tool runner does not read the typed binding |
|
||||
|
||||
**The test budget** (50 integration and 500 unit tests per repository): the design adds one integration test in the
|
||||
nats module's repository and one in mesh-tools (the ledger), and table cases, not new functions, to the judge's unit
|
||||
tests in mesh-delivery and to mesh-sdk's decoding tests. Each repository's count against the budget is read in the
|
||||
prepare step of issue 1966, and a repository over it merges tests to stay within it.
|
||||
|
||||
**Acceptance.** The operator asks the bus module's `list` and sees every module's kept data with its template, its
|
||||
settings and its fill; a module that moved keeps its data; a drift or an orphan reaches the operator as a condition
|
||||
and is changed only on their word; and the knowledge base keeps its records as a ledger (design 55 § 10).
|
||||
|
||||
## Reviewed by architectural domain
|
||||
|
||||
The amendment of 2026-11-03 to ADR 0595, ADR 0596 and ADR 0597 (designs 25 § 12, 32 § 4 and 55 together) was
|
||||
reviewed by the four reviewers by architectural domain side by side (playbook 01), over three rounds, and the lead
|
||||
ruled on every finding. What was found, and what answers it:
|
||||
|
||||
| Domain | Found | Answered by |
|
||||
|---|---|---|
|
||||
| security | the author could be words the module chose; a person's dangerous tools had no role, mark or path to the process; the caller travelled as a plain field | design 25 § 12.3 (marks, the process's own tool subject, the identity seat's signed token), § 12.4 (the tool runner stamps the caller it proved; the move's authors marked imported) |
|
||||
| seams | the bus module asked for its own events in the new form, which it would have to create itself (ADR 0595 §10); a person's tool had no join to the process; a verdict was written twice with no order | design 25 § 12.3 (its events on its old `streams` section; the process's own tool subject); design 55 § 2.1 (the `review` operation first, `reviews` caught up from it) |
|
||||
| running qualities | overdue creation was undefined and raised by nobody; the database's rollback write lived in memory; a forward delivery after a rollback could miss acceptances and verdicts; no size estimate of the heads | design 25 § 12.2 (five minutes, the watchman, durable consumers, `check` at start); design 55 § 8.1 step 6 (`database.sequence`, the check at every start before the drop), § 2.1 (the heads' estimate) |
|
||||
| health | the bus module's health would have fired on a quiet mesh; conditions had no clearing; `database-behind` had no clearing | design 25 § 12.2 and § 12.3 (each condition with what clears it); design 55 § 8.1 step 6 |
|
||||
| events and logs | an event lost when the module stopped after `apply`; the bus module's events had no reader | design 55 § 1 (each event sent with its operation's sequence as id, the state `sent`); design 25 § 12.3 (`history` of one entry) |
|
||||
| testing | the bus module had no test that starts it whole; live tests would have filled the live bus; the budget was not counted | design 25 § 12.7 (the nats module's integration test, live drift only by a manifest change, the budget paragraph); design 55 § 10 |
|
||||
| upgrade | the build order lacked the grants part's binding and two undo steps | design 25 § 12.6 |
|
||||
| tools | the ledger's repairs had no tool for the next owner; the bus module's events had no read tool | design 25 § 12.5 (served for every ledger owner), § 12.3 |
|
||||
|
||||
**Left to the operator**, because each changes an accepted record: a margin between raising and clearing the
|
||||
condition `full` (ADR 0595 §6 raises and clears it at 75 percent, so an entry sitting there flaps), and an event of
|
||||
its own for `drop` and `recreate`. **A correction of fact** for ADR 0595 §4: the bus module deletes through `recreate`
|
||||
as well as `drop`. Each is noted on issue 1966.
|
||||
|
||||
@@ -15,10 +15,8 @@ code:
|
||||
- mesh-catalog modules/gitea/module.json
|
||||
- mesh-controller internal/inventory/migrations/0063-a-machines-names-are-one-seats.sql
|
||||
- mesh-controller internal/inventory/migrations/0064-the-resolver-file-is-the-uplinks.sql
|
||||
updated: 2026-11-03
|
||||
updated: 2026-10-10
|
||||
decisions:
|
||||
- "ADR 0608"
|
||||
- "ADR 0605"
|
||||
- "ADR 0287"
|
||||
- "ADR 0223"
|
||||
- "ADR 0220"
|
||||
@@ -143,11 +141,9 @@ convention, which later seats departed from.
|
||||
| `mesh-store` | — | mesh | — | the store the mesh's own records live in |
|
||||
| `mesh-broker` | — | mesh | `mesh-bus` | the broker carrying the mesh's own bus |
|
||||
| `mesh-vault` | — | mesh | `secret` | the vault (in the seed since ADR 0161; the earlier *reserved* named an effect no rule produced) |
|
||||
| `container-image-registry` | `mesh-artifact-store` for images (ADR 0605; the old name readable until every node-engine reads the new one) | mesh | `artifact-store` | the artifact registry (`distribution`) |
|
||||
| `oci-artifact-registry` | `mesh-artifact-store` for bundles and archives (ADR 0605) | mesh | `artifact-store` | the artifact registry (`distribution`) |
|
||||
| `mesh-artifact-store` | `the-artifact-store` (renamed 2026-09-30, ADR 0156) | mesh | `artifact-store` | the artifact registry |
|
||||
| `mesh-catalog` | `the-catalogue` | mesh | — | the catalog |
|
||||
| `npm-package-registry` | — (its rename to `mesh-*` stays deferred, ADR 0156 §8) | mesh | `npm-package-registry` | the forge |
|
||||
| `go-package-registry` | — (ADR 0605) | mesh | `go-package-registry` | the forge |
|
||||
| `mesh-npm-package-registry` | `npm-package-registry` | mesh | `npm-package-registry` | the forge |
|
||||
| `mesh-git` | `git` | mesh | `git` | the forge |
|
||||
| `mesh-build-machine` | `the-build-machine` | node | — | a builder |
|
||||
| `mesh-resolver` | — | mesh | — | the mesh's resolvers, each holding every node's internal domain (ADR 0194); replicated, held on the anchor and the home server (ADR 0223) |
|
||||
@@ -169,50 +165,6 @@ things here: seats held by assignments rather than claimed by definitions, the `
|
||||
its reservation, and the foundation's seats delivering nothing. It is brought to this table before it
|
||||
merges.
|
||||
|
||||
## A seat's definition (from 2026-11-03)
|
||||
|
||||
**A seat is defined once, in one language-neutral file, and every part learns it from the bus** (ADR 0608, from
|
||||
research 081). The registry seats (design 51) are the first defined this way, then `mesh-broker`'s verbs.
|
||||
|
||||
- **The file** is a schema with conformance fixtures: the seat's verbs and their messages, its events and their
|
||||
bodies, its state and the forms of its provisions. It lives in the contracts module of its area. Its Go and
|
||||
TypeScript types are generated from it and committed beside it; nobody writes them by hand.
|
||||
- **The generator** is each language's build module's: the Go build module's package for Go, the Node and TypeScript
|
||||
build module's for TypeScript (the logic is the module's whose work it is). The check of a change regenerates and
|
||||
fails, naming the file, when a committed type differs; its verdict event names the out-of-step files, and that is
|
||||
how the indexer learns of them. The indexer runs no generator.
|
||||
- **On the bus**: the indexer reads every seat definition from every trunk and open pull request, from the forge
|
||||
bridge's messages, and keeps them in its own state `seat-definitions`, keyed by seat and source (the trunk, or a
|
||||
pull request's head), as their only writer; a definition is at most 256 KiB. It emits `seat-definition-changed`
|
||||
(seat, source, digest), kept seven days. **Only the trunk's entry is the seat's contract**: the readers (the contracts
|
||||
modules' builds, the tool runner's descriptions, the review) ask `indexer_seat_definition`, which answers the trunk's
|
||||
unless a pull request's head is named, and says which it answered. A pull request's entries are removed on the forge
|
||||
bridge's event that it closed or merged. A definition that does not parse fails its pull request's check; the
|
||||
trunk's stays. The merge is the only approval. The foundation's own definitions are bootstrapped before the bus
|
||||
exists, as the bootstrap of a fresh mesh has it (research 075).
|
||||
- **Findings**, written by the indexer, from which the condition keeper raises and clears the conditions (ADR 0564): a
|
||||
trunk's seat definition that does not parse (a pull request's is its check's verdict, not a condition); generated
|
||||
types on a trunk out of step with their definition. **Tools** of the indexer: `indexer_seat_definition` (read one),
|
||||
`indexer_seat_definitions_again` (read a trunk's or a pull request's again now) and `indexer_out_of_step` (the
|
||||
generated types out of step).
|
||||
- **Contracts modules.** A seat's contract is published by a contracts module, which holds contracts only (every file
|
||||
generated from a seat definition) and no seat; the seat's holder implements it. The seat contracts mesh-sdk holds
|
||||
move into `mesh-contracts` in one change, delivered with the first numbered publish (design 51, *The move*, step 2);
|
||||
an area's contracts move into `mesh-<area>-contracts` when the area's contract next changes, and that change creates
|
||||
the module. mesh-sdk keeps its name and the shared runtime. A catalog check refuses a contracts module holding a file
|
||||
its generator did not write, and a seat-specific type in mesh-sdk once `mesh-contracts` publishes.
|
||||
- **Older readers keep working**: a moved contract keeps its subjects, keys and message shapes, and a reader ignores
|
||||
a field it does not know.
|
||||
- **A vendor-specific** provision or contribution lives in its module's own extension package, published by version
|
||||
like any package; a later holder of the seat may publish its own the same way. A seat's definition stays
|
||||
vendor-neutral, which the seams review checks.
|
||||
|
||||
Acceptance tests: research 081's 14, 21, 25 and 26, and cases of the indexer's whole-module integration test: a trunk
|
||||
definition that does not parse writes its finding and a fixed one clears it; a check verdict naming out-of-step types
|
||||
writes the finding and a clean one clears it; `indexer_seat_definition` answers the trunk's entry while a pull request
|
||||
holds another; a pull request's entries go when it closes; a definition over 256 KiB is refused; each of the three
|
||||
tools answers.
|
||||
|
||||
## The foundation's seats
|
||||
|
||||
`mesh-controller`, `mesh-store` and `mesh-broker` name which assignment the mesh *itself* uses: the
|
||||
@@ -395,11 +347,3 @@ checked as their tables say:
|
||||
| The uplink's step-back comes from the seat | 0287: the composition tests for the uplink and the resolver on one machine pass with the case in the collision check removed. |
|
||||
| Holdings are derived, and the overview lists every seat | 0118: the `seats` command test, including an unheld seat. |
|
||||
| A build source on the seat records no address; an unheld seat refuses only self-hosted builds | 0111's tests. |
|
||||
|
||||
## Reviewed by architectural domain
|
||||
|
||||
The amendment of 2026-11-03 for research 081 (ADR 0605 to 0611; *A seat's definition*) was reviewed with designs 51
|
||||
and 32 by the four reviewers by architectural domain; the findings and what answers them are listed in design 51's
|
||||
*Reviewed by architectural domain*. For this design: only the trunk's definition is the contract (security), the
|
||||
indexer learns out-of-step types from the check's verdict event and runs no generator (seams), findings go to the
|
||||
condition keeper (health), a pull request's entries are removed when it closes (running qualities).
|
||||
|
||||
@@ -12,13 +12,8 @@ code:
|
||||
- mesh-tools src/main.ts
|
||||
- mesh-catalog modules/mesh-catalog
|
||||
- mesh-tools node-tools/internal/runtime (a module's state, ADR 0201)
|
||||
- mesh-sdk (the typed bus-state entry in `requires` and its binding, ADR 0595)
|
||||
updated: 2026-11-03
|
||||
updated: 2026-10-12
|
||||
decisions:
|
||||
- "ADR 0609"
|
||||
- "ADR 0606"
|
||||
- "ADR 0595"
|
||||
- "ADR 0596"
|
||||
- "ADR 0307"
|
||||
- "ADR 0240"
|
||||
- "ADR 0233"
|
||||
@@ -232,7 +227,7 @@ service" versus "one worker per machine".
|
||||
| credential | sealed, per consumer | none | none | none | none |
|
||||
| reply | — | none | none, or an event later | a report | awaited |
|
||||
| retention | — | age and size | work queue, explicit ack | **last per subject** | none |
|
||||
| declared | `provides`/`requires` | `emits`/`consumes` | seat `accepts` / `uses` | the mesh's own; a module's bus state in `requires` (ADR 0595), the old `state` / `reads` until it moves | `serves` |
|
||||
| declared | `provides`/`requires` | `emits`/`consumes` | seat `accepts` / `uses` | the mesh's own; a module's `state` / `reads` | `serves` |
|
||||
|
||||
**Job** is the one ADR 0041 had no room
|
||||
for. Its table has events at 1:many and provisions at 1:1; a module submitting work to a service
|
||||
@@ -283,36 +278,6 @@ module's assignment** — what a module stored is data
|
||||
(ADR 0030) — and one whose
|
||||
declaration is gone is reported, never removed by the mesh.
|
||||
|
||||
**A module asks for its bus state in `requires`, and reads no other module's.** *Amended 2026-11-03, ADR 0595
|
||||
and ADR 0596, from research 082.* The `state` and `reads` of the two paragraphs above, and the sections `logs`,
|
||||
`streams` and `objects` beside them, are four vocabularies for one need: what a module keeps on the bus. They are
|
||||
replaced by one form, a **provision** of the bus module, as a database is postgres's:
|
||||
|
||||
- **An entry of `requires`, in its typed long form** (ADR 0595 §1): the local name, the storage type, the template
|
||||
(state, events, work queue, log, objects, ledger) and the template's options, within their bounds; a ledger also
|
||||
names its type (its schema, its operations each with its ledger effect, its indexes). The templates, their fixed
|
||||
settings and their options are [design 25](25-the-bus-on-nats.md) § 12.1. mesh-sdk's catalog package carries the
|
||||
entry's type and its strict decoding; every rule that judges it lives in the indexer's judge (ADR 0595 §8).
|
||||
- **The binding is what the module receives**: the names on the bus for each local name, written into its
|
||||
membership by the grants part once the bus module says `created` (design 25 § 12.2). The module still names only
|
||||
locally (§ 1). A module whose binding is missing at start fails loudly, naming the entry.
|
||||
- **One name, one form.** A module asks for each entry once: an old section and the new form are never both used for
|
||||
one local name, and one name serves one storage type only.
|
||||
- **No readers.** The new form has no `reads`, and the judge refuses one (ADR 0596 §2). Only the owner's account
|
||||
reaches its bus state. **A module's events are subscribed to; its data is asked for**: what another module needs
|
||||
of a module's kept data it asks through that module's tools or seat verbs (§ 4's tool relationship, `serves`),
|
||||
and what a module lets others hear is its declared events, heard through `consumes` (ADR 0363, unchanged; ADR 0596
|
||||
§1). A bucket's layout is never a contract another module depends on; a tool or a declared event is.
|
||||
- **The move is a module at a time** (ADR 0595 §11, ADR 0596 §3), each when it is next touched, keeping its local
|
||||
names and its old settings as options so its data stays. A reader moves after its owner: the owner declares the
|
||||
event or gains the tool its readers need first, and the judge refuses a moved module's subscription to an event
|
||||
its owner does not declare. An old section is refused once no module but the foundation's uses it.
|
||||
|
||||
What follows for the paragraphs above: for a module that has moved, ADR 0201 §2 (another module's state read by
|
||||
naming it in `reads`) no longer holds, and the bucket "every instance of the owner writes; a reader reads and
|
||||
watches" becomes "every instance of the owner writes and reads". The runtime's `get`, `put`, `delete`, `keys`,
|
||||
`watch` and `history` stay the calls of the template state.
|
||||
|
||||
**A module declares the data it holds.** *Added 2026-10-06,
|
||||
ADR 0233.*
|
||||
State on the bus is one kind of data; the rest lives on a machine's disks or with a provider, and the
|
||||
@@ -472,71 +437,6 @@ meaning into shape wherever it can go — a required `channel` field is caught,
|
||||
interpretation of an existing one is not. Saying so is better than implying the fingerprint is
|
||||
complete, because a team that believes it is complete stops reviewing for the case it misses.
|
||||
|
||||
### A package, its series, and the series its users accept (from 2026-11-03)
|
||||
|
||||
(ADR 0606 §4, ADR 0609, from research 081.) This is the manifest's half of design 51's versions.
|
||||
|
||||
- **A package is declared as an artifact**: its name, the kind `package`, its language (`go` or `typescript`), its
|
||||
paths and its **package series** (`0.1`). An image, a bundle or an archive may declare a package series the same
|
||||
way; one that declares none is numbered in `0.1`. Nobody types a version: the builder counts it (design 51).
|
||||
- **Raising a package series** is a reviewed change of the package's manifest, for a change that breaks its users.
|
||||
The checker's verdict names every change of a package series it sees, so the review of the change sees it.
|
||||
- **A user names the mesh packages it takes in a section of its own, `packages`**, each with the package series it
|
||||
accepts. It is not `uses`, which grants publishing on a seat's `accepts` subjects (§ 3); a package is not a seat. A
|
||||
user builds, and is checked, against the newest version of that series; a publish in another series does not move
|
||||
it, and moving is a reviewed change of the user's manifest. A user that names none accepts the series of the
|
||||
package's first numbered version, so nothing is edited in today's users. The indexer's tool `indexer_package_users`
|
||||
lists, per package, its users and the series each accepts, so a user left on an older series is seen.
|
||||
- **Inside one repository** a module uses its sibling package by path, from the same commit, and waits for no publish.
|
||||
- **A user outside the package's delivery group is not built before the merge**: it is built after it, when the new
|
||||
version is in its series. A user that breaks fails its own build, is not wanted, keeps its previous build, and a
|
||||
condition tells the operator. A group's members are built in the group's check, against the group's tagged version.
|
||||
- **Cost**: the new manifest fields are read through mesh-sdk's manifest types, so their change rebuilds every module
|
||||
once; it is delivered with design 51's step 2, the one rebuild that step already takes.
|
||||
|
||||
### A config value renamed (from 2026-11-03)
|
||||
|
||||
(ADR 0609 §4.) A config value's declaration may name its old name, `renamed-from`. On the merge, mesh-config moves the
|
||||
value it holds under the old name to the new name in its own state, without the operator giving it again, emits
|
||||
`renamed` (module, old name, new name, when; its family `config.events`, kept seven days) and records it. This design decides what ADR 0609 leaves open:
|
||||
|
||||
- **Which values**: a module's own config values only, never a secret, never another module's value. A rename keeps
|
||||
the value's trust: a value set without the operator's Approve stays untrusted under its new name (ADR 0348 §8), and
|
||||
the checker refuses a rename that would change a value's trust.
|
||||
- **How long the old name is read**: until every node-engine whose node has a want of the module reports applied a
|
||||
build of it that reads the new name: mesh-config hears the status view's `delivered` events, and asks its
|
||||
`node_haves` at its start (design 47); until then mesh-config answers both names. A later change may drop
|
||||
`renamed-from` only after that, and the checker refuses it before. The checker also refuses `renamed-from` in any
|
||||
manifest until a build of mesh-config that understands it is delivered, which it learns from the status view's
|
||||
`delivered` events for mesh-config. `config_renames` names, per rename, the nodes still holding the old name open.
|
||||
- **A refused move** (the old name holds nothing, or a secret) is mesh-config's finding, from which the condition keeper
|
||||
raises the condition. **Tools of mesh-config**: `config_renames` (what moved, from which name, when) and
|
||||
`config_rename_again` (move one value by hand when a move was refused).
|
||||
|
||||
### A value no longer read (from 2026-11-03)
|
||||
|
||||
A config value or secret no manifest on any trunk reads any more (mesh-config learns it from the indexer's
|
||||
`indexer_config_readers`, on each of the indexer's events that a manifest was taken in) is marked retiring only once no
|
||||
node-engine reports applied a build that reads it, as the rename's window is (§ above). mesh-config then emits
|
||||
`retiring` (family `config.events`, kept seven days) and asks the operator once, through the operator channel, whether
|
||||
to remove it, naming any node that still read it until lately; it is removed only on the operator's Approve. An ask
|
||||
unanswered for seven days is asked once more, and then written as a finding. `config_retiring` lists what is retiring,
|
||||
since when, and its ask.
|
||||
|
||||
### The mesh delivery cases this design carries
|
||||
|
||||
Research 081 listed the mesh delivery cases; these are the ones it handed to a design, each with its acceptance test,
|
||||
each a case of its module's one whole-module integration test unless it says live.
|
||||
|
||||
| case | what holds | acceptance test |
|
||||
|---|---|---|
|
||||
| C6, a package series raised | users on `0.1` keep the newest `0.1`; a reviewed change moves a user | builder: a `0.2.0` publish moves no `0.1` user, its check takes the newest `0.1` too, a reviewed change naming `0.2` moves it, a user naming no series builds against the newest `0.1` |
|
||||
| C14, a provision's form gains a field | the provider serves it in a new version; the consumer reads it once rebuilt; an older consumer ignores it | mesh-sdk: a consumer built against the older form reads a provision with the new field |
|
||||
| C18, a new required config value with no default | the check names the nodes that lack it; the want waits; the operator is asked once | live: a module gaining such a value waits on each node lacking it, the operator is asked once naming the nodes, and once given the module is applied |
|
||||
| C20, a config value renamed | the value moves, keeps its trust, both names read until every node runs the new build | mesh-config: a renamed value is present under the new name with no operator input and its trust; the old name answers until the node-engines report the new build; dropping `renamed-from` early, or changing trust, is refused; a refused move writes its finding |
|
||||
| C22, a secret only a person can give | the want waits; the operator is asked once what to give and where | live: a module requiring such a secret waits, one ask names it and where, and once given the module starts |
|
||||
| C24, a config value or secret no longer read | it is retiring, the operator is asked, removed only on the Approve | mesh-config: with one node-engine still reporting a build that reads the value it is not marked retiring and no ask is sent; once that node reports the new build it is marked retiring, `retiring` is emitted and one ask reaches the operator; an ask unanswered seven days is asked once more, then written as a finding; the value stays until an Approve removes it |
|
||||
|
||||
## 9. Provisioning over the bus
|
||||
|
||||
Provisioning rides the bus, and the provider stops having an address.
|
||||
@@ -702,41 +602,6 @@ billing existing under that name.
|
||||
- **A module reaches only the state it declared.** The composer's test: an owner's runtime may write
|
||||
its buckets, a reader's may only read, and nothing else is granted; the runtime's test over a real
|
||||
bus: a name not issued and a reader's write are refused with the reason.
|
||||
- **Bus state is asked for once, in one form, and read by its owner alone** (ADR 0595 §8, ADR 0596). The judge's
|
||||
unit tests: a `reads` in the new form, a local name used twice across storage types or in an old section and the
|
||||
new form, an unknown template and an option outside its bound are each refused, naming the field. Live: a moved
|
||||
module receives its binding after `created`; another module's account reading its entries is refused; a moved
|
||||
reader hears its owner's declared event and asks its owner's tool. The rest is [design 25](25-the-bus-on-nats.md)
|
||||
§ 12.7.
|
||||
- **State is current at once.** The runtime's test over a real bus: a watch hands the current values
|
||||
without deletions, then an end-of-current marker, then changes. Live: a machine assigned after a put
|
||||
reads it at start.
|
||||
|
||||
## Reviewed by architectural domain
|
||||
|
||||
The amendment of 2026-11-03 to ADR 0595, ADR 0596 and ADR 0597 (designs 25 § 12, 32 § 4 and 55 together) was
|
||||
reviewed by the four reviewers by architectural domain side by side (playbook 01), over three rounds, and the lead
|
||||
ruled on every finding. What was found, and what answers it:
|
||||
|
||||
| Domain | Found | Answered by |
|
||||
|---|---|---|
|
||||
| security | the author could be words the module chose; a person's dangerous tools had no role, mark or path to the process; the caller travelled as a plain field | design 25 § 12.3 (marks, the process's own tool subject, the identity seat's signed token), § 12.4 (the tool runner stamps the caller it proved; the move's authors marked imported) |
|
||||
| seams | the bus module asked for its own events in the new form, which it would have to create itself (ADR 0595 §10); a person's tool had no join to the process; a verdict was written twice with no order | design 25 § 12.3 (its events on its old `streams` section; the process's own tool subject); design 55 § 2.1 (the `review` operation first, `reviews` caught up from it) |
|
||||
| running qualities | overdue creation was undefined and raised by nobody; the database's rollback write lived in memory; a forward delivery after a rollback could miss acceptances and verdicts; no size estimate of the heads | design 25 § 12.2 (five minutes, the watchman, durable consumers, `check` at start); design 55 § 8.1 step 6 (`database.sequence`, the check at every start before the drop), § 2.1 (the heads' estimate) |
|
||||
| health | the bus module's health would have fired on a quiet mesh; conditions had no clearing; `database-behind` had no clearing | design 25 § 12.2 and § 12.3 (each condition with what clears it); design 55 § 8.1 step 6 |
|
||||
| events and logs | an event lost when the module stopped after `apply`; the bus module's events had no reader | design 55 § 1 (each event sent with its operation's sequence as id, the state `sent`); design 25 § 12.3 (`history` of one entry) |
|
||||
| testing | the bus module had no test that starts it whole; live tests would have filled the live bus; the budget was not counted | design 25 § 12.7 (the nats module's integration test, live drift only by a manifest change, the budget paragraph); design 55 § 10 |
|
||||
| upgrade | the build order lacked the grants part's binding and two undo steps | design 25 § 12.6 |
|
||||
| tools | the ledger's repairs had no tool for the next owner; the bus module's events had no read tool | design 25 § 12.5 (served for every ledger owner), § 12.3 |
|
||||
|
||||
**Left to the operator**, because each changes an accepted record: a margin between raising and clearing the
|
||||
condition `full` (ADR 0595 §6 raises and clears it at 75 percent, so an entry sitting there flaps), and an event of
|
||||
its own for `drop` and `recreate`. **A correction of fact** for ADR 0595 §4: the bus module deletes through `recreate`
|
||||
as well as `drop`. Each is noted on issue 1966.
|
||||
|
||||
The amendment of 2026-11-03 for research 081 (ADR 0606, ADR 0609; the package, rename, retiring and delivery-case
|
||||
sections of § 8) was reviewed with designs 51 and 26 by the four reviewers by architectural domain; the findings and
|
||||
what answers them are listed in design 51's *Reviewed by architectural domain*. For this design: a rename keeps a
|
||||
value's trust (security), `packages` is a section of its own, not `uses`, and mesh-config hears the status view's
|
||||
`delivered` events (seams), a value retires only once no node-engine runs a build reading it (security), and `renamed`
|
||||
and `retiring` have a family and an ask that is asked again (events).
|
||||
|
||||
@@ -2,9 +2,8 @@
|
||||
layer: to-be
|
||||
status: in-progress
|
||||
code: [mesh-controller, mesh-catalog modules/mesh-delivery-gate, mesh-delivery, mesh-host, mesh-sdk, mesh-vault, hq 00-META/checks]
|
||||
updated: 2026-11-03
|
||||
updated: 2026-10-29
|
||||
decisions:
|
||||
- "ADR 0594"
|
||||
- "ADR 0572"
|
||||
- "ADR 0571"
|
||||
- "ADR 0570"
|
||||
@@ -846,21 +845,6 @@ backup holder backs up nothing, and the have says so.
|
||||
fingerprint, the digest, the phase and the health after the module's start window. A failed apply is loud,
|
||||
retried by the next change of the want or the composition, never by a clock, and fixed forward (ADR 0360 §8,
|
||||
ADR 0539 §9).
|
||||
- **The bus module is wanted like any module; only a warranted want replaces its server** (ADR 0594, changing
|
||||
ADR 0539 §14 and §15, ADR 0546 §8 and ADR 0565 §7 for the bus module). The assigner writes the bus module's want by
|
||||
the same rules as every module's, and the node agent applies what that want brings (its files, its processes' bus
|
||||
accounts and secrets, its tools) while it keeps the server's container as it runs, with no record ask. Only a want
|
||||
that carries the operator's warrant (a put after the streams' snapshot, or the operator's hand write) replaces the
|
||||
server; until then the bus module's have names the fingerprint applied and the one the server runs, and the view
|
||||
says the server waits. mesh-sdk names the resources the bus's
|
||||
server holds, so the assigner and the node agent read one list; the assigner writes the bus module's want only to a
|
||||
node agent whose have says it keeps the server (the version it reports), so the node agent's keeping of the server
|
||||
is delivered before the assigner's change. Until
|
||||
then nothing writes the bus module's process accounts, and its tools that write to the bus fail (issue 774, the
|
||||
operator, 2026-11-03). How it is checked: a node agent unit test applies a bus module's new want and shows the
|
||||
server's container untouched and the tools' account written; an assigner unit test writes the bus module's want
|
||||
by the rules, holds the server's replacement for a put, and writes none to a node agent whose have does not say it
|
||||
keeps the server.
|
||||
- **Every want is owed.** The watchman mirrors every want as a pulse owed by that node's agent until its have
|
||||
names the want's fingerprint, so a node that does not move is a stall (ADR 0539 §19).
|
||||
- **A move a person must allow is judged by the node and asked by the assigner.** The node agent applies ADR 0377
|
||||
@@ -1270,8 +1254,8 @@ release reports the controller's code lines against research 063's baseline (acc
|
||||
| R4g | The last milestone of release 4 and release 5 as one, with one switch-over and no rehearsal (ADR 0524 §1): builds and checks are asks on streams that each build node pulls when it has a free slot, deliveries first, with nothing holding checks and no controller queue (ADR 0525); the assigner is made with its composition check, writes each node's wants compared with what the controller sends and every difference, and the view answers the asks that need no proof of who answered (ADR 0528, ADR 0437 §10); no module's fingerprint takes the builder (ADR 0530); the scripts of the repositories a module is built from, build-agent with its socket and Dockerfile code, the toolchain images and the controller's build code go, and the builder delivers its own module, the build modules and the controller from its keys (ADR 0453 clauses 5, 8, 10; ADR 0524 §5, §7); the ways to deliver without the builder are recorded (ADR 0526); hq's four checks are Go tests (ADR 0527); each repository's script goes (ADR 0529); the word for the composition check's predecessor is struck. Its pull requests are opened only after five readings, the revert path read, not shown on a test merge (ADR 0524 §3) | 4 (from R4g mesh-build-image, rootless, is the only part that builds an image: ADR 0487 §7), 17, 24 (no `merge-check.sh` in a repository a module is built from) | mesh-sdk, mesh-controller (build-agent removed), mesh-delivery, mesh-catalog, mesh-tools, mesh-tools-go, the node-engine (`mesh-host`), mesh-media-catalog, okioki, hq | 1887 (the flow), 1297 (the assigner), 1295 (`merge-check.sh` goes), 979 (closed with the toolchain images), 1296 and 1303 (hq's check, the old word struck), 1299, 1305, 1294, 1318 (the controller's deletions) |
|
||||
| R6 | Each node applies from its wants the compositions its own node agent composes (ADR 0547 §2; as released it applied the controller's signed ones), a new version reaches the module's canary first and the rest once it is healthy there, a move a person must allow is asked before its want, the view says what is moving, settled, stuck and delivered, and the plans are deleted from the code; switched over by release 5's last plan (ADR 0539) | 18, 19, 20 | mesh-sdk, the node-engine (`mesh-host`: the node agent), mesh-controller (plans deleted; as released also compositions and credentials, which ADR 0547 moves to the node agents, the grants part and the vault), mesh-delivery (assigner, watchman, view), mesh-catalog (the gate, the messenger, the issue tracker, the backup holder), hq | 1942 (the planner puts the node-engine and the controller in one tier for the switch; delivered first), the bus's user list granting what release 6 uses (delivered first, under L1's issue), 1943 (mesh-sdk: the composition's layout and signature, the want's digest, `mesh-hand-compose`, the verb tables without the plan verbs), 1944 (the node agent), 1946 (the gate keeps the merge's states; the messenger and the issue tracker hear the view), 1906 (the assigner's seventh rule and its asks, the watchman's pulses for wants, the view's wants and events), 1130 (hq) |
|
||||
| L1 | The controller leaves build and delivery, first milestone: the bus node follows the user list alone, the grants part composes in shadow and the vault holds the bus accounts' hashes, so the operator's take-over can make the grants part's list the one loaded (ADR 0547 §8, §11; ADR 0550 §4) | — | mesh-controller, mesh-delivery, mesh-catalog (the vault, the grants part), the node-engine (`mesh-host`), mesh-sdk | 1945 |
|
||||
| L2 | Nodes compose: each node agent composes its own modules, and no node applies a composition made elsewhere but the bus's user list (ADR 0547 §11); where modules run is bus state held by the seat `mesh-placement` on the assigner, changed by a proposal the nodes answer and the operator approves, and no repository names a node or holds what one installation decides (ADR 0567). Built as release sdk1 (ADR 0593) and delivered to every machine on 2026-11-02, the controller's code emptied (mesh-controller #491) and its repository archived; done once a check has composed a candidate on every machine | — | the node-engine (`mesh-host`), mesh-sdk, mesh-delivery (the assigner as the seat's holder), mesh-catalog, mesh-controller (its pin only) | 1126 |
|
||||
| L3 | Nothing is left with the controller: the vault mints bus accounts and signs, the grants part writes grants, the try is the checker's own verb, placement is read from the seat `mesh-placement` alone, each node-engine holds the node seat `node-engine`, judges `root-free` for its node and asks the operator itself for a hand-over or an access act, acting on the warrant with nothing signed (ADR 0563), and, once L4 to L7 are delivered, the lease write passes the lease to a holder that is no controller, made right after the delivery that drops every listed read of the controller but the three ADR 0571 §13 keeps to the write itself (ADR 0547 §11, ADR 0549 §5, §6, ADR 0550 §2, ADR 0571 §13); the rest of the controller's code lines is the issue of L2. Since the controller left (2026-11-02), nothing writes the bus module's own process accounts while the bus module has no want on the broker node, so its tools that write the bus fail (issue 774) | — | mesh-controller, mesh-sdk, the node-engine (`mesh-host`), mesh-catalog (the vault, the grants part, the messenger), mesh-delivery | 774 |
|
||||
| L2 | Nodes compose: each node agent composes its own modules, and no node applies a composition made elsewhere but the bus's user list (ADR 0547 §11); where modules run is bus state held by the seat `mesh-placement` on the assigner, changed by a proposal the nodes answer and the operator approves, and no repository names a node or holds what one installation decides (ADR 0567) | — | the node-engine (`mesh-host`), mesh-sdk, mesh-delivery (the assigner as the seat's holder), mesh-catalog, mesh-controller (its pin only) | 1126 |
|
||||
| L3 | Nothing is left with the controller: the vault mints bus accounts and signs, the grants part writes grants, the try is the checker's own verb, placement is read from the seat `mesh-placement` alone, each node-engine holds the node seat `node-engine`, judges `root-free` for its node and asks the operator itself for a hand-over or an access act, acting on the warrant with nothing signed (ADR 0563), and, once L4 to L7 are delivered, the lease write passes the lease to a holder that is no controller, made right after the delivery that drops every listed read of the controller but the three ADR 0571 §13 keeps to the write itself (ADR 0547 §11, ADR 0549 §5, §6, ADR 0550 §2, ADR 0571 §13); the rest of the controller's code lines is the issue of L2 | — | mesh-controller, mesh-sdk, the node-engine (`mesh-host`), mesh-catalog (the vault, the grants part, the messenger), mesh-delivery | 774 |
|
||||
| M1 | A milestone is a record of the issue tracker: each row of a design's milestone table is a milestone record its issues are linked to by `issue-tracker.link`, an issue to at most one, and pull requests and commits to issues the same way; the knowledge base's `progress` reads the milestones from the tracker, not from this table (ADR 0493 §1 to §4) | — (ADR 0493 §13 in part) | hq, mesh-catalog (mesh-issues, mesh-knowledge) | 1781 |
|
||||
| L4 | Health leaves the controller (ADR 0564): the condition keeper holds the seat `mesh-conditions` and keeps every open condition from the observers' findings, saying today's condition events; the watchman holds `mesh-watchdogs`, its watchdogs pulses, reads each node-engine's node health report, and keeps the dead letters from the lease write; `mesh-self-check` runs the probes, `root-free` among them from L7 (ADR 0571 §11), and says `checked`; of the probes still deferred, D13, D8, D-fill and DA are built and the rest are not, their kinds ending with the controller at the lease write (ADR 0572); `mesh-healers` heals on events and acts only once the lease names no controller; `status` is the view's derivation; built dark, compared, then switched by the operator's take-over; the bus module gains a tool that removes one named consumer, which the lease write uses (ADR 0547 §11) | — | mesh-sdk, mesh-catalog (mesh-conditions, mesh-self-check, mesh-healers, the router, mesh-watcher, nats), mesh-delivery (watchman, view, the grants part) | 1952 |
|
||||
| L5 | Control records leave the controller, and the controller is not changed (ADR 0565): mesh-config keeps each module's declared config in its state `declared` and publishes the predefined keys, which the indexer reads from the forge bridge's messages with each open pull request's config candidate; config switches to that source only on the operator's take-over, refused while `compare` shows a difference; each node agent writes its node's declared packages (the node seat `node-engine`, verb `packages`); the assigner holds the new seat `mesh-wants` with the upgrade policy a person chose and the bus step; the hand-act log (`hand-act`, `hand-acts`, `warranted`) is the stream of the new seat `mesh-hand-acts`, held by a new module in mesh-catalog, which every recorder calls from this delivery with no switch; the checker and the assigner's composition check read the held values of their pull requests from mesh-config's state `kept`; from the lease write a trusted config value is set only on the operator's Approve; `settings`, `move-config` and `move-placement` end with the lease and nothing replaces them. each person's six policy choices are set through `mesh-wants.upgrade` and recorded as hand acts, `upgrade compare` and the assigner's finding `policy-split` show any split, and the lease write waits for `compare` to be empty (ADR 0565 §8). One delivery group, built before the lease write of L3, which waits for the group delivered, the six choices set, mesh-config's `compare` empty and its take-over done, `mesh-wants.upgrade compare` empty, and the readings of ADR 0565 §12 (ADR 0565 §14; ADR 0348 §9, ADR 0502 §2, ADR 0539 §14, §15, ADR 0547 §11) | — | mesh-sdk, mesh-catalog (mesh-config, mesh-hand-acts, pacman, the forge module, claude-code, mesh-knowledge, mesh-conditions, mesh-healers), mesh-delivery (indexer, assigner, checker, grants part, forge bridge), the node-engine (`mesh-host`); mesh-controller is not a member | 1953 |
|
||||
|
||||
@@ -6,20 +6,15 @@ code:
|
||||
- mesh-catalog modules/artifact-store-tools (the store's tools), modules/distribution (its unbuilt code removed)
|
||||
- mesh-catalog modules/gitea (package retention)
|
||||
- mesh-catalog modules/docker (image pruning)
|
||||
updated: 2026-11-03
|
||||
updated: 2026-10-12
|
||||
decisions:
|
||||
- "ADR 0605"
|
||||
- "ADR 0606"
|
||||
- "ADR 0607"
|
||||
- "ADR 0610"
|
||||
- "ADR 0611"
|
||||
- "ADR 0311"
|
||||
- "ADR 0251"
|
||||
- "ADR 0189"
|
||||
- "ADR 0257"
|
||||
---
|
||||
|
||||
# 51 — The registries: what is published, under which version, and what is kept
|
||||
# 51 — The registries keep what is named
|
||||
|
||||
**Three places hold built things: the artifact store, the package registry and each machine's
|
||||
container runtime. Each gets tools that say what it holds, what names each thing, and what could go.
|
||||
@@ -29,227 +24,6 @@ registry, and the machine's declaration for its images.**
|
||||
(ADR 0251,
|
||||
extending ADR 0189.)
|
||||
|
||||
## What is published, and how (from 2026-11-03)
|
||||
|
||||
**Every registry is a seat; a build module publishes into it over the bus; the builder gives every version.**
|
||||
(ADR 0605, ADR 0606, ADR 0607, ADR 0610, ADR 0611, from research 081.) What follows is the to-be shape; the sections from
|
||||
*The parts* on keep what the registries hold, and stand. Every interaction below is a seat verb, a module's tool or an
|
||||
event: no module reads or writes another's bus state (ADR 0596). A module asks another's data through its tools, and
|
||||
asks work of another by an event of its own (ADR 0610 §1).
|
||||
|
||||
### The four registry seats
|
||||
|
||||
| registry seat | holds | read side it serves | holder today |
|
||||
|---|---|---|---|
|
||||
| `npm-package-registry` | npm packages | the npm registry protocol | the forge module |
|
||||
| `go-package-registry` | Go modules | Go's module proxy protocol | the forge module |
|
||||
| `container-image-registry` | container images | OCI distribution, with an image's own verbs (its tags, mirroring an outside image) | `distribution` |
|
||||
| `oci-artifact-registry` | bundles and archives, by digest | OCI distribution | `distribution` |
|
||||
|
||||
Each is held once for the mesh. The last two replace the seat `mesh-artifact-store` and front the one artifact store;
|
||||
while one module holds both, both deliver the provision `artifact-store`, and splitting them between two holders takes a
|
||||
decision of its own. `mesh-artifact-store` stays served until every node-engine reports applied a build that reads the
|
||||
new names (ADR 0610 §4); a sleeping node holds it open. `distribution` closes it itself: it hears the status view's
|
||||
`delivered` events (and asks the status view's `node_haves` at its start, design 47), stops serving the old name once every
|
||||
node whose declaration names an artifact reports the new build, and its tool `registry_old_name` lists the nodes still
|
||||
holding it open. `cargo-package-registry` stays named, without a holder, until a
|
||||
build module publishes cargo packages.
|
||||
|
||||
**One contract, a seat definition** (design 26, *A seat's definition*). Verbs: `publish`, `publish-part` (ADR 0611), `versions`, `remove` (a
|
||||
tagged version only; the holder itself refuses `remove` of a version with no tag), and on `container-image-registry`
|
||||
`mirror` and `tags`. Events: `published`, `refused` (added to ADR 0605 §2's two, additively) and `removed`, each
|
||||
carrying the name, version, kind, tag, digest, fingerprint, commit and holder, and `refused` its reason; each seat's
|
||||
events are its own family, `<seat>.events`, kept seven days. A holder stores
|
||||
under the version it is given and counts nothing.
|
||||
|
||||
**Who may call which verb is the holder's to say**, in a trusted config value of the holder listing its callers, the
|
||||
form ADR 0363 gives a restricted family: `publish` and `publish-part` the build modules, and a part only from the
|
||||
proved caller that opened its transfer; `mirror` mesh-build-image; `remove` the builder.
|
||||
Declaring `invokes` alone grants nothing. The holder checks the caller identity the tool runner proved and stamps on
|
||||
the request (design 25 § 12.4), never a name in the request's body, and answers any other caller with a refusal naming
|
||||
the verb.
|
||||
|
||||
### Publishing
|
||||
|
||||
```
|
||||
builder ── build request (name, version or tag) ──▶ build module
|
||||
│ publish(name, version, digests of every part)
|
||||
▼
|
||||
registry seat's holder ── answers the parts it lacks
|
||||
│ the build module sends those parts, in chunks, by `publish`
|
||||
holder: hashes what it received, asks `builder_versions`,
|
||||
stores in its own store, emits `published` or `refused`
|
||||
▼
|
||||
builder (durable consumer: entry, done key), indexer, status view, checker
|
||||
```
|
||||
|
||||
- **The file travels in parts through the seat** (ADR 0611). `publish` opens a transfer, naming it and the digest of
|
||||
every part (an image's layers, a package's file, a bundle's blob); the holder answers the digests it lacks. The build
|
||||
module sends only those through `publish-part` (transfer, part digest, sequence, last), each at most 1 MiB, with
|
||||
mesh-sdk's bus helper for sending in parts, added to mesh-sdk's runtime in step 2's rebuild. A `publish` opening a
|
||||
transfer already open answers the parts held, so a cut transfer goes on from there. The parts land in the holder's
|
||||
own object store `incoming`, declared in its manifest with a cap of 20 GiB and a lifetime of 24 hours; a transfer with
|
||||
no part for 24 hours is dropped and its parts deleted. The holder hashes every part and refuses one whose bytes do not
|
||||
match its digest. At 80 % of the cap it writes a finding (cleared below 80 %); at the cap it refuses a new transfer
|
||||
with `refused` naming the cap. Its tool `registry_incoming` lists open transfers and the fill, and drops one by hand.
|
||||
- **The holder asks the builder**, through `builder_versions`, whether the name, version and digest are recorded, and
|
||||
refuses loudly otherwise (ADR 0605 §4). A rebuild of a held fingerprint answers the held version and stores nothing.
|
||||
- **How a publish ends**: the holder emits `published` or `refused`. The build module waits for one of them, matching
|
||||
on its transfer, name and version, at most
|
||||
10 minutes, narrating "waiting on <seat>" at least every five minutes; past the bound it writes the key refused,
|
||||
naming the seat, and the builder, hearing that build's end event as it does every build's, writes the finding for a
|
||||
refused publish. A `published` that arrives after that is still heard by the builder on its durable consumer: the
|
||||
entry turns `published` and the builder writes the done key then, so a late store is never lost.
|
||||
- **What is published when**: a package or an image from a trunk build under its version, or as a tagged version; a
|
||||
bundle or an archive from every done key by its digest alone, a candidate's included, and at the trunk's claim the
|
||||
build module calls `publish` with the version and the held digest, which stores nothing. An upstream image is copied
|
||||
by `mirror` with its pinned digest. The done key names the reference the seat answered; a key in the old
|
||||
`artifact-store://` form keeps it, every reader reads both forms, and none is built again for its form.
|
||||
- **No build module holds a registry's write credential**: the npm write token, the image push credential and the
|
||||
artifact store's write binding go in step 4. Each holder keeps its write side as an own secret beside a read-only
|
||||
tool token (the forge module: `write:package`, its tool token `read:package`).
|
||||
- **Go packages** are fetched through the Go seat's provision first and Go's public proxy second, with no `GOPRIVATE`
|
||||
and the checksum database off for the forge's paths; `go.sum` and the digest the builder records (Go's `h1:` hash,
|
||||
computed by the Go build module at the publish) refuse a mesh package with other content. The builder also tags
|
||||
each Go version in git (`go/v0.1.<n>`), so a user that names a version can fetch it from the forge's git as well.
|
||||
No mirror of outside modules is built; one could come later as a holder of a seat of its own.
|
||||
|
||||
### The versions
|
||||
|
||||
- **The builder numbers every trunk build** of a package, image, bundle or archive at its claim: the package series
|
||||
its manifest declares (design 32; `0.1` when it declares none) and the next count for that name, under
|
||||
compare-and-set. Numbers follow the order of the forge bridge's merge events: a claim of a later merge waits until
|
||||
every earlier merge of the same name whose fingerprint the indexer still holds is numbered (a fingerprint replaced
|
||||
by a later merge before its claim is skipped, design 47 *After the merge*); a wait past 30 minutes writes a finding
|
||||
naming the merge it waits on. A fingerprint already numbered keeps its number.
|
||||
- **The builder's state `versions`** holds one key per name, tag and version, with its fingerprint, commit, digest,
|
||||
time and state: `numbered`, `published`, `refused` with its reason, or `removed` with the time, the reason (a
|
||||
branch's end, retention, the sweep) and the event that removed it. History one, cap 1 GiB; past 80 % the builder
|
||||
writes a finding, cleared below it. Only the builder reaches it; others call `builder_versions` or hear its events. The build log
|
||||
carries each change too.
|
||||
- **The floor**: on a name's first claim, or when `versions` holds nothing for it, the count starts above the highest
|
||||
of the seat's `versions`, the package's highest git tag (the forge bridge's tool `bridge_tags`) and the highest
|
||||
pseudo-version a consumer requires (the indexer's tool `indexer_requirements`). The builder numbers nothing for that
|
||||
name until all three answered, and writes a finding naming the silent one meanwhile, cleared once all three answer. Today that gives Go `v0.1.14`
|
||||
and npm `0.1.18`; a name with none starts at `0.1.0`.
|
||||
- **Events of the builder**: `numbered` (name, version, tag, fingerprint, commit, head when it answers an ask), on its
|
||||
own family, kept seven days; a waiter on a build's end does not wake on it. **Tools of the builder**:
|
||||
`builder_versions` (a name's versions in order, one call), `builder_publish_again` (one fingerprint under its number,
|
||||
after a holder failed; when the build's file is gone it builds the fingerprint again under the same number, and the
|
||||
new digest replaces the recorded one only if no `published` was ever emitted for that number, else it takes a new
|
||||
number; it refuses a version refused for other bytes), `builder_tag` (ask a tagged version), `builder_tag_asks` (open asks and
|
||||
their state) and `builder_untag` (remove a branch's tagged versions).
|
||||
|
||||
### Tagged versions
|
||||
|
||||
- **Asked by an event, never by a push** (ADR 0610 §1). The checker emits `tag-wanted` (repository, branch, head,
|
||||
tag), on a family of its own, `checker.asks`, kept seven days, apart from its verdicts, so a waiter on a verdict does
|
||||
not wake on it, for each head of a delivery group member that declares a package; a person asks through `builder_tag`. The
|
||||
builder consumes the event on a durable consumer and keeps the ask in its own state `tag-asks`, removed when its
|
||||
branch is merged or deleted. An ask the builder has not answered within five minutes is narrated in the build log
|
||||
with what it waits on.
|
||||
- **Numbered below the next version** (`0.1.15-feat-x.2`), never the latest; npm keeps it under a dist-tag of its own
|
||||
and Go's `@latest` skips it. A package new in a group is numbered from its floor (`0.1.0-feat-x.1`).
|
||||
- **A group's check builds after `published`**: Go members by the tagged version, TypeScript members with their lock
|
||||
naming it and the registry's integrity; nothing is packed for a check, and a lone pull request's check publishes
|
||||
nothing. No `published` within the publish's bound (10 minutes) fails the check naming the package.
|
||||
- **Removed with its branch**: on the forge bridge's merged or deleted event, heard on a durable consumer, the builder
|
||||
calls each seat's `remove`. It acknowledges the event only once every seat emitted `removed`; until then it retries
|
||||
every minute and, past 15 minutes, writes a finding naming the seat. A version with no tag is not removed by a
|
||||
branch's end; retention may remove an old unreferenced one (below).
|
||||
|
||||
### Retention
|
||||
|
||||
The retention of *The package registry* below, and the artifact sweep, keep working, now through the seats: each
|
||||
removal emits `removed` and the builder marks its entry, so `versions` never says `published` for bytes that are gone.
|
||||
The Go seat keeps what the npm seat keeps: every referenced version and the newest five of each package series. A
|
||||
Go version is referenced when a `go.mod` on a repository's default branch requires it; the forge module reads every
|
||||
`go.mod` as it reads lockfiles, and a `go.mod` it could not read stops a real run before anything is deleted, as an
|
||||
unread lockfile does.
|
||||
|
||||
### Health and conditions
|
||||
|
||||
Observers write findings; the condition keeper raises and clears the conditions from them (ADR 0564). Findings: a
|
||||
publish refused (the holder when it refuses, the builder when a build module's bound ran out; cleared when that version
|
||||
is stored, or when a later version of the name is published), a registry seat not answering (each holder's health check,
|
||||
which calls `versions` on each seat it holds and probes the read side every minute, so a dead seat is seen with no
|
||||
publish running), a removal not done (the builder), a floor source silent (the builder), a store past 80 % (the holder,
|
||||
the builder). A build narrates its publish: asked, waiting on a seat, stored, refused.
|
||||
|
||||
### The move, in four steps
|
||||
|
||||
Each step is its own delivery, and no credential goes before its replacement works (ADR 0605 §7).
|
||||
|
||||
1. A node-engine that reads both reference forms and both seat names; the holders serve every verb of the four seats
|
||||
with their caller lists, and `incoming`; the builder's `versions`, `tag-asks`, its
|
||||
events and tools; the forge bridge's `bridge_tags` and the indexer's `indexer_requirements`.
|
||||
2. Starts only once every node-engine reports step 1's build applied (a sleeping node holds it). Every build module
|
||||
publishes through the seats: mesh-sdk's first numbered publish, images, bundles, archives and
|
||||
upstream copies, each read back; delivered with the move of mesh-sdk's seat contracts into `mesh-contracts` (design
|
||||
26), so every module is rebuilt once for both. The checker emits `tag-wanted`. Until step 3, Go users fetch from the
|
||||
forge's git, where the builder's tags let them take the numbered version.
|
||||
3. The Go fetch switches to the Go seat. Its rollback stays safe after a user moved, since the git tags exist.
|
||||
4. The three credentials go, and `package-version.sum`. The old seat name retires after it, on the node-engines'
|
||||
reports.
|
||||
|
||||
Steps are rolled back in reverse order. Steps 1 to 3 are rolled back by reverting their pull request; step 4 by
|
||||
reverting it, which mints the build modules'
|
||||
credentials again. **On a fresh mesh** the foundation's bootstrap (research 075) pushes the holders' own images with a
|
||||
one-off credential of the bootstrap, discarded once the holders serve `publish`.
|
||||
|
||||
### Acceptance tests
|
||||
|
||||
Research 081's tests 1-3, 5-7, 9, 10, 12, 13, 15-20, 22 and 23 (its Direction) are this design's, with their labels.
|
||||
Each integration test below is a case of its module's one whole-module integration test, which starts the module
|
||||
against the real bus (test budget: mesh-delivery holds 76 integration tests today, over its 50; these add cases, not
|
||||
tests, and each change that touches the builder's or the indexer's tests merges two old ones into its whole-module
|
||||
test).
|
||||
|
||||
- **Builder.** A branch pushed with no ask publishes nothing; a lone pull request's check publishes nothing; a group
|
||||
introducing a new package takes `0.1.0-<tag>.1`; the checker's `tag-wanted` yields `numbered` and `published`, and
|
||||
members are built only after `published`; no `published` in 10 minutes fails the check naming the package;
|
||||
`builder_untag` removes a branch's tagged versions with `removed`; a `remove` while the holder is down is retried and
|
||||
past 15 minutes writes its finding; a late `published` after the bound writes the done key; `builder_publish_again`
|
||||
after the build's file is gone builds again under the same number; with the seat's holder stopped at a first claim
|
||||
nothing is numbered and the finding names it; two merges claimed in reverse order are numbered in merge order; a
|
||||
retention removal turns the entry `removed`.
|
||||
- **Builder, added in round 2.** Two merges close together, the first replaced before its claim: the second is
|
||||
numbered at once; a build module's bound running out writes the builder's finding, and a later version of the name
|
||||
clears it; `builder_publish_again` after a `published` takes a new number.
|
||||
- **Holder (gitea, distribution).** A caller not on the caller list is refused for each verb, and one module naming
|
||||
another as the caller in the request's body is refused; a part sent by a module other than its transfer's opener is
|
||||
refused; a transfer cut in the middle resumes from the parts held,
|
||||
and one left 24 hours is dropped; at the cap a new transfer is refused naming the cap; a version a `go.mod` requires
|
||||
survives retention, and an unreadable `go.mod` stops the run; a part whose bytes do
|
||||
not match its digest is refused; only the missing layers are sent; `remove` of a version with no tag is refused;
|
||||
with no publish running, a Go seat that stops answering while its holder runs writes its finding and clears it when it
|
||||
answers (a holder down whole is its module's own health check's); `registry_incoming`
|
||||
lists and drops.
|
||||
- **Vault.** Reverting step 4 mints the build modules' credentials again; the bootstrap's one-off credential is gone
|
||||
once the holders serve `publish`.
|
||||
- **Live.** Step 2 waits while one node has not got step 1's build, as `node_haves` shows (the lead's gate before
|
||||
step 2's merge); `registry_old_name` names the nodes
|
||||
holding the old name open.
|
||||
|
||||
## Reviewed by architectural domain
|
||||
|
||||
The amendment of 2026-11-03 (ADR 0605 to 0611, research 081's graduation, designs 51, 26 and 32 together) was reviewed
|
||||
by the four reviewers by architectural domain side by side (playbook 01), and the lead ruled on every finding. Two
|
||||
findings reached a decision and became records: ADR 0610 (a module's bus state is its own, ADR 0596, so the checker
|
||||
asks by an event and holders ask the builder's tool; retention; the old seat name closes on what node-engines applied)
|
||||
and ADR 0611 (the file travels in parts through the seat). What was found, and what answers it:
|
||||
|
||||
| Domain | Found | Answered by |
|
||||
|---|---|---|
|
||||
| security | caller lists named only on the caller's side; the handed object's bytes unchecked; no caller identity; pull request seat definitions served as contracts; the bootstrap's credential | *Who may call which verb* (holder-side lists, the identity the tool runner proved); *Publishing* (parts hashed); design 26 (only the trunk's entry is the contract); *The move* (a one-off bootstrap credential) |
|
||||
| upgrade | the steps moved no image, bundle or archive before their credentials went; Go users between steps 2 and 3; no node-engine reading the new forms before step 2 | *The move* (every move in step 2, a node-engine reading both forms in step 1, step 2 waiting on the node-engines' reports, git tags for Go, reverse-order rollback) |
|
||||
| seams | five reaches into another module's bus state; a shared hand-off store; how a publish ends; how the indexer learns out-of-step types; no named path for the node-engines' reports | ADR 0610, ADR 0611; *Publishing* (`published` or `refused`); design 26 (the check's verdict event); the status view's `delivered` events and `node_haves` |
|
||||
| tools | no read or drop tool for asks, transfers, the old name's window | `builder_tag_asks`, `registry_incoming`, `registry_old_name`, design 32's `config_renames` and `config_retiring` |
|
||||
| running qualities | the versions value growing; the floor with a part down; merge order and a replaced merge; windows closing on wants; retention against "never removed"; a Go reference undefined; the in-transit cap | *The versions* (one key per version, a cap, the floor's wait, merge order skipping a replaced fingerprint); *Retention* (a Go reference); *Publishing* (the cap refused) |
|
||||
| testing | failure paths untested; the test budget | cases of each module's one whole-module integration test, listed under *Acceptance tests* |
|
||||
| health | conditions raised by their observers; no health check of a seat between publishes; a refused publish with no clearing | findings for the condition keeper (ADR 0564); the holder's health check; *Health and conditions* (each finding with what clears it) |
|
||||
| events and logs | removal and `published` failing silently; a publish silent for 10 minutes; events with no family or retention | the builder's durable consumers and acknowledgement rule; narration every five minutes; each event's family and seven days |
|
||||
|
||||
## The parts
|
||||
|
||||
```
|
||||
|
||||
@@ -2,62 +2,34 @@
|
||||
layer: to-be
|
||||
status: in-progress
|
||||
code: [mesh-catalog, hq]
|
||||
updated: 2026-11-03
|
||||
updated: 2026-10-18
|
||||
decisions:
|
||||
- "ADR 0290"
|
||||
- "ADR 0321"
|
||||
- "ADR 0423"
|
||||
- "ADR 0424"
|
||||
- "ADR 0592"
|
||||
- "ADR 0593"
|
||||
- "ADR 0595"
|
||||
- "ADR 0596"
|
||||
- "ADR 0597"
|
||||
---
|
||||
|
||||
# 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.
|
||||
|
||||
**Amended, 2026-11-02** (ADR 0592, on the operator's word: "our knowledge base must be nats-native"; "don't use
|
||||
file references or local files anymore"). The records leave the store for the bus: each record's head in the
|
||||
module's state, its versions on a stream, its sections' bodies once each in an object bucket by digest. A record is
|
||||
written one section at a time in a draft, and reviewed one section at a time. A section is a part with a title,
|
||||
never a file. § 1, § 2, § 4, § 5, § 8.1 and the fifth slice of § 9 say how; until it is built, the module keeps its
|
||||
records in the store as before.
|
||||
|
||||
**Amended, 2026-11-03** (ADR 0597, ADR 0595 and ADR 0596, from research 082; the operator, 2026-11-02 21:53: "the
|
||||
writes are the record"). The knowledge base is the first **ledger**: a record is an instance of a ledger type, every
|
||||
change to it is an operation kept in the ledger's history, and its head is the ledger's state, derived from those
|
||||
operations by the tool runner's `apply` and never written by the module. A section's text lives only in the
|
||||
`write-section` operation that wrote it, with its digest; the head names each section by its title, digest and that
|
||||
operation's sequence. So the object bucket `sections`, the state `names`, the stream `versions` and the module's own
|
||||
commit rule fall away (ADR 0597 §12, changing ADR 0592 §1 to §12, §17, §25, §32, §33). Its kept data is asked for in
|
||||
`requires` by template (ADR 0595), and no other module reads it: another module asks the knowledge base's tools or
|
||||
hears its events (ADR 0596). The ledger itself, the same for every module, is designed in
|
||||
[design 25](25-the-bus-on-nats.md) § 12; how a module asks for it, in [design 32](32-what-a-module-declares.md) § 4.
|
||||
§ 1, § 1.1, § 2.1, § 4, § 8.1, § 9 and § 10 say what changes here.
|
||||
|
||||
**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 in titled sections, with every version kept, all of it on the
|
||||
bus. A record is written section by section, checked when it is submitted, reviewed section by section by the
|
||||
knowledge base's own judgement and accepted there; the operator's Approve is asked when a record needs it. Agents
|
||||
ask what holds, and why, in one bounded call.**
|
||||
(ADR 0290, ADR 0592 and ADR 0597,
|
||||
from research 045 and research 082.)
|
||||
its clauses with their own status, and its reasoning, with every version kept. A record is proposed, checked when it
|
||||
is written, reviewed by the knowledge base's own judgement and accepted there; the operator's Approve is asked when a record needs
|
||||
it. Agents ask what holds, and why, in one bounded call.**
|
||||
(ADR 0290,
|
||||
from research 045.)
|
||||
|
||||
A **clause** is one numbered statement a decision record makes, with its own status. A **section** is one titled
|
||||
part of a record's reasoning. The **knowledge base** is the module that holds the records; the seat it holds is
|
||||
`knowledge-base`. The glossary holds these words.
|
||||
A **clause** is one numbered statement a decision record makes, with its own status. The **knowledge base** is the
|
||||
module that holds the records; the seat it holds is `knowledge-base`. The glossary holds both words.
|
||||
|
||||
**Acceptance.** A session started in any folder on any node asks "what holds about the merge window, and why" and
|
||||
gets, in one call under 4 KiB, the clauses in force, the records they come from, what changed them since, and how to
|
||||
read the reasoning behind each. A session writes a research effort of more than 100 KiB one section at a time, a
|
||||
session on another node takes the draft over and submits it, and every section is judged.
|
||||
read the reasoning behind each.
|
||||
|
||||
This document designs the first slice in detail, tracked by issue 425, and the fifth, the records on the bus
|
||||
(ADR 0592), kept as a ledger (ADR 0597). The slices after the first are in
|
||||
This document designs the first slice in detail, tracked by issue 425. The slices after it are in
|
||||
[§ 9](#9-the-slices-after-the-first), which also designs the third: designs and the glossary's entries become records,
|
||||
and the knowledge base works out each design's state (ADR 0423, ADR 0424).
|
||||
|
||||
@@ -68,38 +40,17 @@ and the knowledge base works out each design's state (ADR 0423, ADR 0424).
|
||||
|
||||
mesh-knowledge (seat knowledge-base, held once for the mesh)
|
||||
├ its process, launched by the node runtime
|
||||
├ on the bus, asked for in requires by template (ADR 0595), read by it alone (§ 2)
|
||||
│ ledger records one instance per record (decision/0290, research/082):
|
||||
│ history every operation, in order, each section's text in its write-section
|
||||
│ state the head, derived by apply: fields, clauses, links, sections as
|
||||
│ title, digest and sequence
|
||||
│ snapshot the latest head per record, never expiring
|
||||
│ state drafts one key per record being written: its author and its sections as title,
|
||||
│ digest and the sequence of their write-section
|
||||
│ state asked one key per part asked of the judge and not yet answered: ticket, node, when
|
||||
│ state findings the whole-set findings standing now, and the told marks (told.<topic>)
|
||||
│ state sent per record, the last operation whose event was sent
|
||||
│ stream reviews one message per part reviewed: the digest judged, the verdict, the findings
|
||||
├ written through the tool runner's apply on the control-node, the only publisher on the
|
||||
│ ledger's history (ADR 0597 §3, §9)
|
||||
├ in its memory the search index, built from the bus at start and kept by watching the
|
||||
│ ledger's state
|
||||
├ verbs ask, get, list, holds, links, history, findings, progress,
|
||||
│ reserve, open, write, remove, order, submit, drop, take-over, propose,
|
||||
│ withdraw, retract, import, return
|
||||
├ a database in the store records, versions, clauses, links; ranked full-text search
|
||||
├ verbs ask, get, holds, links, history, reserve, propose, import, return
|
||||
│ served by the control-node's tool runner
|
||||
├ events reserved, opened, written, removed, ordered, submitted, dropped, taken-over, proposed,
|
||||
│ part-reviewed, reviewed, accepted, changed, moved, checked, formed,
|
||||
│ imported, drafted, confirmed, returned
|
||||
├ events proposed, reviewed, accepted, changed, imported, drafted, confirmed, returned
|
||||
├ consumes the router's warrants: the operator's Approve for a record that needs it (§ 5)
|
||||
├ consumes claude-code's judged: the judge's answer to a part (§ 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);
|
||||
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 section or a head (§ 5), draft and confirm clauses (§ 7);
|
||||
emits judged with the answer of each ticket (§ 5, a dependency of the fifth slice)
|
||||
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
|
||||
|
||||
@@ -108,109 +59,20 @@ and the knowledge base works out each design's state (ADR 0423, ADR 0424).
|
||||
```
|
||||
|
||||
The module is written in Go against the SDK. It holds no checkout of this repository; the import reads it through
|
||||
the records module, as the issue tracker's import does. Until the move of § 8.1, its records are in a database in
|
||||
the store; from the move on, it requires no database (ADR 0592 §1, changing ADR 0290 §7).
|
||||
|
||||
**A record is changed by an operation, and the head follows from it** (ADR 0597 §1 to §8). Every change to a record
|
||||
is one operation the module hands to `apply`: among them `reserve`, `propose`, `write-section`, `submit`,
|
||||
`take-over`, `review` (a part's verdict), `ask` and `warrant` (the operator's ask and answer), `accept` and
|
||||
`withdraw`. The module checks its own rules first (an `accept` needs the operator's Approve where the record needs
|
||||
it, § 5; only the author writes a draft, § 4), then calls `apply`, which checks the operation against the record's
|
||||
type, appends it to the instance's history with the sequence it expects, derives the head by the operation's effect,
|
||||
and writes the head and its snapshot. The author is the account the bus proved for the caller of the module's verb:
|
||||
the module names the tool call it serves, and the tool runner stamps the caller it proved for it, never a name the
|
||||
caller or the module gave (ADR 0597 §7; [design 25](25-the-bus-on-nats.md) § 12.4).
|
||||
|
||||
**Every operation's event is sent once, after a restart too.** `apply` emits nothing; the module sends the event of
|
||||
each operation it applied (`submitted`, `reviewed` and the rest) with the operation's sequence as the message's
|
||||
id, so the bus drops a second send within its duplicate window, and keeps the last sequence it sent per record in a
|
||||
state of its own, `sent` (one key `<kind>.<number>`). The duplicate window of its event stream and of `reviews` is
|
||||
one day, far longer than a start (40 seconds at most, § 10); a module stopped between a send and its `sent` key for
|
||||
longer than a day sends that one event again, which a consumer already takes as a bus delivered at least once. A
|
||||
missed send is never chosen over a second one. At start it sends the events of every later operation in each record's history, so a
|
||||
module stopped between `apply` and its event loses none, and a consumer of `mesh-knowledge.reviewed` never waits for
|
||||
one that was never sent.
|
||||
|
||||
**A write is whole or refused, and an operation commits when its history takes it** (ADR 0597 §5, §6, changing
|
||||
ADR 0592 §10). The commit point of every change to a record is the operation appended to the ledger's history: a
|
||||
failure before it is appended refuses the call, and nothing is taken; a racing writer is refused by the bus, which
|
||||
expects the history's last sequence for the instance. Once it is appended, the call answers that it is taken, with
|
||||
its sequence; the head is derived by `apply` as an update of the revision it read, and a head behind its history is
|
||||
caught up by the next `apply` (ADR 0597 §6). A full state bucket answers that the operation is taken and the head behind,
|
||||
never that it was refused. So nothing an author was told was refused takes effect later. A reservation is an
|
||||
operation `reserve` on an instance with no history, so two sessions reserving at once get two numbers or one
|
||||
refusal. The module's other state (`drafts`, `asked`, `findings`) keeps the compare-and-set on the key's revision,
|
||||
retried at most three times and then refused with the reason (ADR 0597 §12).
|
||||
|
||||
**The search index** is a full-text index in the module's own memory, written with the pure Go library bleve. At
|
||||
start the module reads every head from the ledger's state and the bodies of every current version from their
|
||||
`write-section` operations into it, then keeps it current by watching the ledger's state, noting the last sequence it
|
||||
took in (ADR 0597 §12, changing ADR 0592 §11). The index is the module's own, in its memory; the ledger's declared
|
||||
index (ADR 0597 §4) is not used for it, because a full-text index is not a key-value bucket. `ask` and `holds` answer from it, ranked by title, clauses
|
||||
and sections as before, and refuse with "index not built" while it is built; `get`, `list` and `propose` answer from
|
||||
the ledger's state at once (ADR 0592 §11). The integration test measures the start; the estimate is 20 to 40 seconds and about
|
||||
100 MB of memory for today's 736 records. Nothing but the module writes it, and nothing outside it reads it.
|
||||
|
||||
### 1.1 Its tools, health and events
|
||||
|
||||
Every piece of state has a tool that reads it, and every act the module does by itself can be done by hand
|
||||
(ADR 0592 §32, changed by ADR 0597 §12: the sweep and the digest check become the ledger's repairs):
|
||||
|
||||
| Tool | Reads or does |
|
||||
|---|---|
|
||||
| read a head | one instance's state in the ledger, whole, with the sequence of the last operation it holds |
|
||||
| read a record's history | the instance's operations in order, each with its sequence, author and when; a version is its `submit` |
|
||||
| read a section | one `write-section` operation, by its sequence, its text checked against its digest |
|
||||
| read a draft | one key of `drafts`, with its author and since when |
|
||||
| read a review | the part verdicts of one version, from `reviews` |
|
||||
| in-flight parts | the keys of `asked`: each part waiting on the judge, its ticket and since when; ask one again |
|
||||
| told marks | the `told.<topic>` keys; clear one, so it is told again |
|
||||
| fill | each bucket's and stream's size and count against its cap |
|
||||
| rebuild the index | the search index, built again from the bus |
|
||||
| review again | asks one part's review again, for a version still proposed |
|
||||
| the ledger's repairs | the tool runner's `rebuild` (a head from the history or the snapshot), `snapshot` and `lag` (how far each head is behind its history), served for the module's own ledger (ADR 0597 §10) |
|
||||
| integrity | checks every section a head or draft names resolves to a `write-section` whose text hashes to its digest |
|
||||
| form-only | lists, dry-runs and runs the form-only versions of § 2.2 |
|
||||
| move, and check the move | § 8.1, each with a dry run |
|
||||
|
||||
**Health** (ADR 0592 §33) is unhealthy, naming the cause, when a bucket or stream does not answer, when the index is
|
||||
behind the ledger's state, when a head lags behind its history (the ledger's lag, ADR 0597 §12, changing
|
||||
ADR 0592 §33), and when a bucket or stream is at its cap. It names the source it serves from (the database or the bus) and the result
|
||||
of the move's last check, and counts drafts idle more than a week, records the form-only check refused, and parts
|
||||
waiting more than 30 minutes. A part waiting more than 30 minutes is asked of the operator once through the operator
|
||||
channel, and the ask clears when the part is judged; the module stays healthy meanwhile, because a record waiting on
|
||||
its review is not the module failing (issue 508). The condition at 75 percent of a cap is the bus module's (ADR 0595 §6; [design 25](25-the-bus-on-nats.md) § 12).
|
||||
|
||||
**Events** (ADR 0592 §34) are kept 30 days; what a later reader needs is also in the `submit` operation, in the ledger's history. Each carries:
|
||||
|
||||
| Event | Carries |
|
||||
|---|---|
|
||||
| `opened` | the record, the version it starts from, the author |
|
||||
| `written`, `removed` | the record, the section's title and digest, the author |
|
||||
| `ordered` | the record, the titles in order, the author |
|
||||
| `submitted` | the record, the new version, its section digests, the author |
|
||||
| `dropped` | the record, the author, the reason (asked, or idle 14 days) |
|
||||
| `taken-over` | the record, the previous author, the new author, the reason |
|
||||
| `part-reviewed` | the record, the version, the part, its digest, the verdict |
|
||||
| `reviewed` | the record, the version, every part's verdict: one event per version, as before |
|
||||
| `moved` | the record, its counts on the bus |
|
||||
| `checked` | the move's check: records compared, every difference |
|
||||
| `formed` | the record, the version, the names changed |
|
||||
|
||||
None is named `drafted`, which stays the event of a clause the import's judgement wrote.
|
||||
the records module, as the issue tracker's import does.
|
||||
|
||||
## 2. What one record holds
|
||||
|
||||
- `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**, each a plain title and its text in markdown: for a decision, Context,
|
||||
Considered Options, Consequences, How it is checked, References; for a research effort, its overview and each of its
|
||||
parts by title; for a design, its `to-be` section and, where something of it runs, its `as-is` section (§ 9);
|
||||
- its **reasoning**, in sections: for a decision, context, the options weighed, consequences, how it is checked,
|
||||
references; for a research effort, its overview and each of its documents by name; for a design, its `to-be`
|
||||
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 `submit` is a new version with who made it, when, and why in one line; `history` lists them
|
||||
- its **versions**: every change is a new version with who made it, when, and why in one line; `history` lists them
|
||||
and gives the difference between any two. A progressive insight is a version marked as one, dated, saying what stood
|
||||
before. A form-only version (§ 2.2) is marked `form`.
|
||||
before.
|
||||
|
||||
| Link | From → to |
|
||||
|---|---|
|
||||
@@ -222,105 +84,6 @@ None is named `drafted`, which stays the event of a clause the import's judgemen
|
||||
| `touches` | a research effort → a record, issue or design it bears on (by link), or an area (as text) |
|
||||
| `cites` | any record → any record or issue it names |
|
||||
|
||||
### 2.1 Where it is kept
|
||||
|
||||
A record is kept as one instance of the knowledge base's ledger `records` (ADR 0597 §11), so that a new version
|
||||
costs only the sections it changes; the forms of ADR 0592 §1 to §9 that it replaces are named in its §12.
|
||||
|
||||
```
|
||||
records: history (stream, one subject per instance) records: state (key-value, derived)
|
||||
┌───────────────────────────────────────────────┐ ┌─────────────────────────────────┐
|
||||
│ 101 decision/0290 propose fields, … │ │ decision/0290 │
|
||||
│ 102 decision/0290 write-section "Context" │ │ title, status, clauses, links │
|
||||
│ text, digest 3f9a… │ apply │ current sections: │
|
||||
│ 103 decision/0290 write-section "Options" │ ───────▶ │ Context 3f9a… 102 │
|
||||
│ text, digest 81c2… │ │ Options 81c2… 103 │
|
||||
│ 104 decision/0290 submit sections 102, 103 │ │ pending sections: none │
|
||||
│ 230 decision/0290 write-section "Context" │ │ after operation 231 │
|
||||
│ text, digest 0d77… │ └─────────────────────────────────┘
|
||||
│ 231 decision/0290 submit sections 230, 103 │ records: snapshot (one per instance,
|
||||
└───────────────────────────────────────────────┘ the latest head, never expiring)
|
||||
```
|
||||
|
||||
- **The head** is the instance's state, `<kind>/<number>`: the record's fields, its clauses with their status and
|
||||
source, its links, the operator's ask and warrant, its current and pending version, the latest verdict of each
|
||||
part's review (§ 5), and its sections in order, each a title, a digest and the sequence of the `write-section`
|
||||
that wrote it, never the text. A section's title is held to 200 bytes, so an entry is at most about 300 bytes,
|
||||
and a head with 64 pending and 64 current sections holds at most about 40 KiB of them, within a state value's
|
||||
256 KiB (ADR 0597 §11). A reserved number is a head at `reserved`, with who reserved it, when, for what and when it
|
||||
expires. It is derived by `apply` and never written by the module.
|
||||
- **A version** is the operation `submit`: who wrote it (the caller the bus proved), when, why, its mark, the
|
||||
reason of any take-over since the version before, and the version's sections (title, digest and sequence each),
|
||||
which its effect sets as the head's current sections. It never copies a section's text. Who wrote each section is
|
||||
the author of its `write-section`.
|
||||
- **A section's text** is in the `write-section` operation that wrote it, with its digest, at most 40 KiB within a
|
||||
history entry's 256 KiB: a section unchanged in a new version costs nothing, since no operation is written for it.
|
||||
`apply` refuses a `write-section` whose digest does not match its text, and the module checks the text against
|
||||
the digest each time it reads one. A dropped draft's operations stay in the history, named by no version
|
||||
(ADR 0597 §11).
|
||||
- **The history** is kept for good: the ledger template's history refuses a new operation when full, rather than
|
||||
dropping its oldest, and the bus module raises its condition at 75 percent of the cap (ADR 0595 §3, §6). Each
|
||||
instance's snapshot, the latest head on a subject of its own, makes a rebuild fast (ADR 0597 §8).
|
||||
- **A draft** is a key in the state `drafts` (§ 4). **A part's verdict** is the operation `review` on the record's
|
||||
history first, then a message on the stream `reviews`; a `review` with no message on `reviews` is sent at start
|
||||
with the operation's sequence as its id, so the consumers of `reviews` miss none and hear none twice. A part
|
||||
asked of the judge and not yet answered a key in the state `asked` (§ 5). **The whole-set findings** standing
|
||||
now are keys in the state `findings`, and what was told to the operator once is a key `told.<topic>` there. These
|
||||
keep their own forms (ADR 0597 §12), asked for in `requires`: the states by the template state; `reviews`, kept for
|
||||
good and heard only where granted (ADR 0592 §6, §8), as a bare stream with its own configuration (kept for good,
|
||||
refusing when full, a grant), because no template both keeps for good and takes a grant (ADR 0595 §2); the bus module's read of fill every minute
|
||||
covers it like any entry that refuses when full.
|
||||
|
||||
**Caps** (ADR 0592 §25, its object bucket's cap lapsing by ADR 0597 §12). Each is set from the move's dry run, within
|
||||
its template's bound (ADR 0595 §3): a history at most 8192 MiB, a state or a ledger's state at most 64 MiB. The
|
||||
starting figures, from today's 83 MB for 736 records with every version copying every section, are:
|
||||
|
||||
| Kept data | Template | Cap |
|
||||
|---|---|---|
|
||||
| `records` (history) | ledger | 1 GiB |
|
||||
| `records` (state) | ledger | 64 MiB |
|
||||
| `reviews` | a bare stream, its own configuration | 512 MiB |
|
||||
| `drafts` | state | 64 MiB |
|
||||
| `asked`, `findings`, `sent` | state | 64 MiB, the template's bound |
|
||||
|
||||
A write refused at a cap names the cap, and the module's health turns unhealthy naming it.
|
||||
|
||||
**The ledger's state is the tightest cap.** 64 MiB is already the template's top. A head holds no text, only fields,
|
||||
clauses, links and its sections' titles and digests; estimated at most 8 KiB for a usual record, the 736 heads of
|
||||
today need about 6 MiB, and about 1000 once designs and glossary entries become records (slice 3, § 9) about 8 MiB.
|
||||
Those are estimates; the move's dry run measures the real total, and a total above 32 MiB is told to the operator
|
||||
before the move.
|
||||
|
||||
**Nobody else reads any of it** (ADR 0596 §2, ADR 0597 §9, changing ADR 0592 §8 and §9). Only the knowledge base's
|
||||
own account reads its ledger's history, snapshot and state and its other kept data; the ledger's history is published
|
||||
on only by the tool runner's ledger credential. Another module asks the knowledge base's verbs (`get`, `history`,
|
||||
`ask`) or hears its events (`reviewed`, `accepted` and the rest); the reads of `heads` and `sections` that ADR 0592
|
||||
let another module declare lapse once those verbs answer what each reader needed. `reviews` stays a restricted event
|
||||
family (ADR 0363 §4), heard only by a module granted it in mesh-knowledge's trusted config value `readers`, which
|
||||
names nothing today. A draft is answered by the verbs to its author only, and a version's bodies as `get` answers
|
||||
them, so the text in the history is no more open than ADR 0592 kept it. No secret is kept in any of it.
|
||||
|
||||
### 2.2 A section is a part with a title
|
||||
|
||||
A section's name is a plain title, unique in its record: "Context", "Direction", "The move". It is never a file's
|
||||
name: a name with an extension, a number prefix such as `00-`, or a slash is refused, and so is a text that holds a
|
||||
local path (in a person's home folder or the temporary folder), a reference to a file and line, or a link whose
|
||||
target is a relative path or a file (ADR 0592 §29).
|
||||
|
||||
**A reference has one form** (ADR 0592 §30): the record, a section's title, then a row or clause inside that
|
||||
section's text, joined by ›, such as "research 081 › Direction › D3"; a clause as its record and number,
|
||||
"ADR 0290 §7". Sections never nest: `get` resolves a record and a section's title, and the row is an anchor in the
|
||||
section's text, which a web page links.
|
||||
|
||||
**The records imported with file-named sections** (the research efforts' `00-overview.md` and the like) get titles in
|
||||
one form-only version per record, written after the move by the move's tool alone; no verb writes one. It changes only section names and the
|
||||
targets of links, each link to a record's file becoming a reference in the form above. It is marked `form`, lists the
|
||||
names it changed, and is accepted without a judgement because a check proves only form changed: each section's text,
|
||||
with every link replaced by its label, is the same before and after, and each new link target resolves to the record
|
||||
the old name named. `get` accepts the old section names as aliases, recorded in that version. A part's review carries
|
||||
over to it, its meaning being unchanged. It is not a change of meaning; `history` shows it as a change of form
|
||||
(ADR 0592 §31).
|
||||
|
||||
## 3. Clauses
|
||||
|
||||
A clause holds its record's number and its own (`0276 §4`), its wording, a `status` and a `source`:
|
||||
@@ -340,49 +103,9 @@ A clause holds its record's number and its own (`0276 §4`), its wording, a `sta
|
||||
|
||||
## 4. Writing a record
|
||||
|
||||
`reserve` gives a number (§ 6). A record is then written in a **draft**, one section per call, and submitted as a
|
||||
version (ADR 0592 §17):
|
||||
|
||||
| Verb | Does |
|
||||
|---|---|
|
||||
| `open` | opens a draft of a reserved or existing record, with its fields, clauses and links; called again by its author, changes them |
|
||||
| `write` | writes one section: its title and its text; one call per section |
|
||||
| `remove` | removes one section from the draft |
|
||||
| `order` | sets the sections' order |
|
||||
| `submit` | makes the draft a version: every rule below is checked, every reason of a refusal named at once, and the review asked (§ 5) |
|
||||
| `drop` | discards the draft |
|
||||
| `take-over` | makes the caller the author of a draft or a proposal, with a reason |
|
||||
| `propose` | `open`, a `write` of each section and `submit` in one call, for a record that fits one bus message |
|
||||
|
||||
```
|
||||
open ──▶ write ──▶ write ──▶ … ──▶ submit ──▶ proposed, reviewed by part (§ 5)
|
||||
▲ │ │
|
||||
└─ remove, order └── refused: every reason named, the draft kept as it was
|
||||
|
||||
drop: the draft is gone take-over: another session becomes its author, with a reason
|
||||
```
|
||||
|
||||
A draft is a key in the state `drafts`: its author, when it was opened, the version it starts from, the record's
|
||||
fields and clauses, and its sections in order as title, digest and the sequence of the `write-section` that wrote
|
||||
each. Each `write` appends a `write-section` operation with the section's title, text and digest to the record's
|
||||
history at once, through `apply`, and names its sequence in the draft (ADR 0597 §11, changing ADR 0592 §17), so an
|
||||
unsubmitted draft's text is already in the history, the knowledge base holds the only copy of a draft, and a
|
||||
session on any node can read it through the verbs; `submit` carries the draft's sections into the version. A draft holds at most 64 sections and 1 MiB; one untouched for a week is counted in
|
||||
the health, and one idle for 14 days is dropped and its author told by `dropped`.
|
||||
|
||||
**A draft and a proposal have one author** (ADR 0592 §19). Only the author writes a draft or revises a proposal;
|
||||
another caller is refused, naming the author. `take-over` makes the caller the author, keeps the reason on the
|
||||
record, and tells the previous author by the event `taken-over`. It answers a proposal that only the machine that
|
||||
wrote it could revise (issue 1102) and one left proposed for ever by a version from an unproven writer (issue 465).
|
||||
A proposal waiting for the operator's Approve cannot be taken over: its author withdraws it first. The reason of a
|
||||
take-over is carried in the next version's message.
|
||||
|
||||
**Only a proven caller writes** (ADR 0592 §20, §21): `open`, `write`, `submit`, `propose` and `take-over` refuse a caller
|
||||
the runtime could not prove. The operator's Approve binds the section digests of the version shown; a version with
|
||||
any other section is asked again.
|
||||
|
||||
`submit` and `propose` are refused, with every reason at once, when one of the rules that judge one record or its
|
||||
prose fails, among them:
|
||||
`reserve` gives a number (§ 6). `propose` takes the record whole: its fields, its clauses, its reasoning in sections,
|
||||
its links, and for each `changes` the clauses it changes. It is refused, with every reason at once, when one of the
|
||||
29 rules of the hq checks that judge one record or its prose fails, among them:
|
||||
|
||||
- a link names a record or clause that does not exist;
|
||||
- a decision has no clause, or a clause has no bold lead;
|
||||
@@ -390,62 +113,21 @@ prose fails, among them:
|
||||
that replaced it;
|
||||
- its topic is not one of the reading order's;
|
||||
- a research effort graduates without naming what it became;
|
||||
- its text uses a retired word of the glossary, or a daemon's command line;
|
||||
- a section is over 40 KiB (it says: split it), or the clauses together are;
|
||||
- a section's name looks like a file, or its text holds a local path, a file and line, or a link to a file (§ 2.2).
|
||||
|
||||
`write` refuses a section over 40 KiB, or one whose name or text breaks § 2.2, at once, so a session learns it on the
|
||||
section it just wrote.
|
||||
- its text uses a retired word of the glossary, or a daemon's command line.
|
||||
|
||||
A proposed record can be revised by its author as new versions until it is accepted. Nothing proposed is ever served
|
||||
as in force.
|
||||
|
||||
## 5. Review and acceptance
|
||||
|
||||
When a version is submitted (by `submit` or `propose`, a new version of a proposed record included), the knowledge
|
||||
base reviews it itself, **in parts** (ADR 0592 §22 to §28, changing ADR 0290 §4): one judgement per part, each in a
|
||||
separate call, with no tools, that answers `approve` or `changes` with its findings, the way the issue tracker's
|
||||
triage judges an issue. The judgement goes through the agent module's judge (`claude_code_judge`) today, and through
|
||||
the mesh's inference layer once one exists; nothing in this design depends on which.
|
||||
|
||||
- **The head** is one part: the title, the brief, the clauses (at most 40 KiB together) and the links, judged
|
||||
against the clauses of the records it links, in what room is left, those not shown named.
|
||||
- **Each changed section** is a part of its own, judged with **the frame** of at most 16 KiB: the title, the links,
|
||||
and only what the section names (rows of other sections, clauses, the clauses of linked records), with the digest
|
||||
of the Context section (the Overview, for a research effort) it is read beside. What does not fit is cut, and the
|
||||
question says what was cut. A section is at most 40 KiB, so each question stays under the judge's 60 KiB.
|
||||
- **What is judged again.** A section is judged again only when its own text or what it names changes; the Context
|
||||
only when it changes. So a usual revision judges only the sections it changed.
|
||||
- **At most two judge calls of one version run at once.** A revision of eight sections takes a few minutes, and
|
||||
about four times the tokens of one whole judgement.
|
||||
- **The rules on the whole record** (§ 4) stay code, run at `submit`; no judgement is asked to find a link to nothing.
|
||||
|
||||
```
|
||||
version 3 of research 081
|
||||
├ head title, brief, clauses, links ── judged ──▶ approve
|
||||
├ Overview unchanged since version 2, same frame ── kept ──▶ approve (version 2's)
|
||||
├ Direction changed ── judged ──▶ changes: "D3 contradicts ADR 0290 §7"
|
||||
└ Findings changed ── judged ──▶ approve
|
||||
version 3 stays proposed, with the findings of Direction
|
||||
```
|
||||
|
||||
**The parts wait on the bus** (ADR 0592 §23). Each part asked of the judge is a key in the state `asked` (the part,
|
||||
the judge's ticket, the node, when it was asked), collected again at start, so a restart loses no review. The judge's
|
||||
answer comes as the event `judged`, which the claude-code module emits for each ticket and the knowledge base
|
||||
consumes; until claude-code emits it, the knowledge base polls the ticket, which is not NATS-native and is tracked by
|
||||
issue 1956. The judge is a tool of the claude-code module, not a seat's verb: a seat for judgements (held by the
|
||||
mesh's inference layer once one exists) is a note on that issue. A part waiting more than 30 minutes asks the
|
||||
operator once through the operator channel, and the ask clears when the part is judged.
|
||||
|
||||
**A part's verdict** is the event `part-reviewed` and a message on the stream `reviews`: the record, its version, the
|
||||
part, the digest judged, the verdict, the findings, who judged and when; the head keeps the latest verdict of each
|
||||
part. **`reviewed` keeps its meaning**: one event per version, once every part is judged, carrying each part's
|
||||
verdict, so every wait on it keeps working. A part's review is kept for a new version when what it was judged with is
|
||||
unchanged, and for a form-only version (§ 2.2); any other part is judged again. **A version is accepted when every
|
||||
part's review approves.** On `changes`, the record stays proposed, the findings are kept with that version and the
|
||||
part they name, and its author revises it with a new version. A part that cannot be judged (the judge unreachable,
|
||||
or its answer unreadable) leaves the version proposed and says which; it is never taken as an approval. When every
|
||||
part approves:
|
||||
When `propose` takes a record (or a new version of a proposed one), the knowledge base reviews it itself: it asks for
|
||||
a judgement in a separate call, with no tools, that sees only the record, the records it links and the rules, and
|
||||
answers `approve` or `changes` with its findings, the way the issue tracker's triage judges an issue. The judgement
|
||||
goes through the agent module's judge (`claude_code_judge`) today, and through the mesh's inference layer once one
|
||||
exists; nothing in this design depends on which. On `changes`, the record stays proposed, the findings are kept with
|
||||
that version, and its author revises it with a new version, which is reviewed again. A judgement that cannot be had
|
||||
(the judge unreachable, or its answer unreadable) leaves the record proposed and says so; it is never taken as an
|
||||
approval. On `approve`:
|
||||
|
||||
1. a record that does not need the operator is accepted at once;
|
||||
2. a record whose author marked it `needs operator` gets an ask on the router with Approve and Decline, shown on a
|
||||
@@ -456,18 +138,18 @@ On acceptance the record and its clauses move to `accepted` and `in force`, ever
|
||||
`supersedes` name moves to `changed` or `superseded`, and `accepted` and `changed` events are emitted. Then the 4
|
||||
rules that judge the whole record set run (an accepted decision cited by nothing, and the like). A failure does not
|
||||
undo the acceptance. It is a fault of the records, which their authors repair, not of the module: it is told to the
|
||||
operator once, as a warning naming the rule and the records, kept in the state `findings`, and cleared once the rule
|
||||
holds again. The module's health names it and stays healthy, because health says only whether the module works, and
|
||||
a finding never holds a rollout at the module's first-node gate (the operator's decision of 2026-10-12 on issue 504,
|
||||
as issue 508 decided for a proposal never reviewed).
|
||||
operator once, as a warning naming the rule and the records, and cleared once the rule holds again. The module's
|
||||
health names it and stays healthy, because health says only whether the module works, and a finding never holds a
|
||||
rollout at the module's first-node gate (the operator's decision of 2026-10-12 on issue 504, as issue 508 decided for a proposal never
|
||||
reviewed).
|
||||
|
||||
The rule `cited` is also judged ahead, in the review, before the judgement: a decision that names no home and that no
|
||||
accepted record links is not accepted. Its review, by the knowledge base itself, asks changes naming the rule (issue
|
||||
504). It is not refused at `submit`, because no record can link a number only reserved: a record that cites a new
|
||||
504). It is not refused at `propose`, because no record can link a number only reserved: a record that cites a new
|
||||
decision is proposed after it and accepted, and the new decision is proposed again.
|
||||
|
||||
An accepted record is changed only by a new record, by a progressive insight (a new version marked as one, which is
|
||||
reviewed like a proposal), or by a form-only version of § 2.2, which a check, not a judgement, accepts.
|
||||
An accepted record is changed only by a new record, or by a progressive insight: a new version marked as one, which
|
||||
is reviewed like a proposal.
|
||||
|
||||
## 6. Numbers
|
||||
|
||||
@@ -512,62 +194,6 @@ reporting nothing created (issue 488).
|
||||
Before the records leave this repository (issue 426), every record is compared with its file at the import's commit,
|
||||
field by field and section by section, and the result is written on issue 426.
|
||||
|
||||
### 8.1 The move off the store
|
||||
|
||||
The records leave the store once, and the database is dropped only after a check and the operator's yes
|
||||
(ADR 0592 §12 to §16; the forms the records are copied into are those of ADR 0597 §12):
|
||||
|
||||
1. **Order.** The ledger is built first, in the order of ADR 0595 §10: mesh-sdk's typed `requires`, binding and
|
||||
package of the new calls; the indexer's judge's rules; the bus module's process, verbs and events; the tool
|
||||
runner's calls, `apply` among them, with its ledger credential per module; then mesh-knowledge moves, asking for
|
||||
its ledger, states and stream in `requires`. An indexer whose judge predates the new form refuses a manifest
|
||||
that uses it, and the placement holds the module off a machine whose tool runner does not read the typed
|
||||
binding, so mesh-knowledge cannot be delivered before them. The release sdk1 (issue 1585, ADR 0593 §10), which
|
||||
created object buckets and history streams through the grants part, is no longer needed by the move: the
|
||||
knowledge base asks for no object bucket and no stream `versions`. The claude-code module's `judged` event is
|
||||
delivered beside, and is not needed for the move (§ 5).
|
||||
2. **Copy.** The move copies everything the database holds onto the bus, every record, version, clause, link,
|
||||
review, finding, ask with its warrant, reservation and told mark: each record's versions as operations on its
|
||||
instance in the ledger, in order (a `write-section` for each section a version changed, then its `submit`, then
|
||||
the record's review, ask, warrant and acceptance), through `apply`, so each head is derived and never copied; each operation is stamped with the knowledge base's own
|
||||
account and keeps the database's author as a separate field marked imported, which `history` shows as the author
|
||||
of an imported version;
|
||||
each standing finding into `findings`, each told mark as `told.<topic>`. A clause keeps its number, status and source as the database holds
|
||||
them (ADR 0321). It emits `moved` per record, writes nothing twice, and a second run changes nothing and says so.
|
||||
A dry run lists what it would write and the size each bucket and stream will need, from which the caps are set.
|
||||
3. **The window.** From the move's final copy through the check to the switch, every writing verb is refused with
|
||||
its reason ("the knowledge base is moving"). Reading verbs answer throughout.
|
||||
4. **Check.** A tool compares the database and the bus record by record: for each record the count of each of
|
||||
those, the digest of every section of every version, computed from the database's text and read from its
|
||||
`write-section` on the bus, that each text hashes to its digest, that each head derived equals the head the
|
||||
database holds, and a digest of every review's, finding's and ask's content. It emits
|
||||
`checked` with every difference, and the health shows its last result.
|
||||
5. **Switch.** Only once the check shows no difference, the module answers from the bus. The switch is a key on the
|
||||
bus, so a restart never falls back to the database. A difference is told to the operator, and the module stays on
|
||||
the database.
|
||||
6. **Rollback.** Until the database is dropped, the module writes each change to the database too, after `apply`
|
||||
has taken its operation, so a rollback of the delivery reads a current database. What the database holds is
|
||||
kept as a key in the state `findings`, `database.sequence`: the last history sequence written to it. At start,
|
||||
and after a write the database refused, the module catches the database up from the history from that sequence
|
||||
on, so a restart between `apply` and the database write loses nothing. A database still behind after three tries
|
||||
raises the condition `database-behind`, naming each record it lacks and saying that a rollback now would lose
|
||||
them, and never answers the author that the write was refused; the module's health is unhealthy while it stands,
|
||||
the drop (step 8) is refused while it stands, and the catch-up tool copies the records again from the ledger and
|
||||
clears it. A rollback is a person's act, and the condition is what tells them it is unsafe. **After a rollback**,
|
||||
the older module wrote to the database alone, and not only versions: acceptances, verdicts, asks, warrants,
|
||||
reservations and changed clauses too. So until the drop, every start of the module refuses every writing verb
|
||||
and runs the check of step 4 before it serves writes; a difference it finds is copied from the database onto the
|
||||
ledger as operations, and the check runs again, so writes are served only once it is clean. A record changed on
|
||||
both sides (an operation the database never took, and a change the older module made) never checks clean: it is
|
||||
told to the operator at once, naming the record and both changes, so the refused writes read as a decision
|
||||
waiting and not as a stall. A rollback is safe
|
||||
only until the drop. This settles the rollback ADR 0597 §13 left to the design.
|
||||
7. **Tell and ask.** Once the check is clean, the operator is told its result and asked, once, whether the database
|
||||
may be dropped, because dropping it removes data.
|
||||
8. **Drop.** On the operator's yes, the delivery after it removes the module's requirement of a database, and the
|
||||
database is dropped by the store's module, through its tool for it, which is built there first if it has none.
|
||||
9. **Titles.** The move's tool then writes the form-only versions of § 2.2, one per record with file-named sections.
|
||||
|
||||
## 9. The slices after the first
|
||||
|
||||
| Slice | Delivers | Done when the operator sees | Tracked by |
|
||||
@@ -576,7 +202,6 @@ The records leave the store once, and the database is dropped only after a check
|
||||
| 2 | the decision records and research efforts leave this repository; path mentions become "ADR NNNN" and "research NNN"; playbooks 01 and 02 changed | this repository holds no decision record, and `get` answers every one | issue 426 |
|
||||
| 3 | designs and the glossary's entries as records, moved in one delivery, and each design's state worked out by the knowledge base (§ 9.1 to § 9.3; ADR 0423, ADR 0424) | `get` answers design 47 with its sections and links, a word's entry answers from the knowledge base, and a design's state follows its issues' deliveries without anyone writing it | issue 428 |
|
||||
| 4 | meeting notes, drawings and blog posts | not decided; needs its own decision | issue 429 |
|
||||
| 5 | the records on the bus, written and reviewed by section, the move off the store and the titles (§ 1, § 2, § 4, § 5, § 8.1; ADR 0592), kept as the first ledger (ADR 0597), delivered after the ledger's build (ADR 0595 §10), then mesh-knowledge, with claude-code's `judged` event beside | the acceptance tests of the fifth slice in § 10, live; the knowledge base requires no database | the issues its hand-off opens (playbook 04) |
|
||||
|
||||
Numbers from the issue tracker, which `reserve` needs, are the tracker's own slice, tracked by issue 424. This
|
||||
repository keeps no copy of the records, generated or otherwise (issue 435, answered by the operator). Playbooks
|
||||
@@ -674,50 +299,6 @@ milestones and nothing of how a delivery runs; it only listens for the announcem
|
||||
| An answer stays within its bound | the module's test over the full import of `main` at a recorded commit: every `ask`, `get` and `holds` of a fixed question list |
|
||||
| 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 |
|
||||
| A record is kept as a ledger instance, a section's text once in its operation, and read by no other module | one integration test that starts the module whole against a real bus server: a record of three sections and a second version changing one give one head, two `submit` operations and four `write-section` operations, the changed section's text found only in its operation; another module's account reading the ledger's history, snapshot or state, the head among them, is refused (ADR 0597 How it is checked, clauses 9, 11, 12) |
|
||||
| Search answers from the index as it did from the store | a unit test over a fixed set of records and a fixed question list; the integration test restarts the module and asks again |
|
||||
| A draft is written by section and has one author, and a take-over tells the previous one | unit tests of each verb of § 4, of a refusal naming the author, and of `take-over` emitting `taken-over`; a take-over refused while the operator's Approve is waited for |
|
||||
| A ledger that keeps its history | in the integration test: the history at its cap refuses a new operation and keeps its oldest; a draft's written section is in the history before `submit` and named by no version after `drop`; a section title over 200 bytes is refused |
|
||||
| A long record is judged whole, one part at a time | unit tests: the frame never passes 16 KiB and says what it cut, with twenty clauses of 2000 bytes and a 40 KiB Context; every question stays under 60 KiB with a 40 KiB section and that frame, and the head part with 40 KiB of clauses; a section over 40 KiB is refused saying split it; an unchanged section keeps its review, a changed frame asks it again |
|
||||
|
||||
**The fifth slice's tests, one per failure path** (ADR 0592; the review by architectural domain's testing ruling). Unit tests are
|
||||
table-driven; one integration test starts the module whole against the real bus server. The module's count today is
|
||||
1 integration and 104 unit tests; these add about 20 table cases and stay within the budget of 50 and 500.
|
||||
|
||||
| Failure path | Test |
|
||||
|---|---|
|
||||
| two writes race on one draft | unit: the second compare-and-set loses, is retried, and is refused with the reason past its tries |
|
||||
| `submit` races `take-over` | unit: one wins; the other is refused naming the new author or the new version |
|
||||
| two reservations race | unit: two numbers, or one refused; never one number twice |
|
||||
| a write cut between the history and the head | the ledger's integration test (design 25 § 12.7): the call answered that the operation is taken; the next `apply` derives the head. The module's own integration test: stopped after `apply`, restarted, it sends the event once |
|
||||
| a failure before the operation is appended | unit: the call is refused, and no head, event or version follows later |
|
||||
| a change through the bus during the move | unit: read back from the database, so a rollback of the delivery reads a current database (ADR 0592 §14) |
|
||||
| a section changed after the operator's Approve | unit: the record is asked again; a version with every section unchanged is not (ADR 0592 §21) |
|
||||
| a head behind its history | unit: health unhealthy naming the lag; the ledger's `rebuild` clears it |
|
||||
| a text does not hash to its digest | unit: `apply` refuses the `write-section`, and a read refuses it, naming the digest |
|
||||
| a full ledger state | integration: `submit` answers taken with the head behind; health unhealthy naming the cap |
|
||||
| a full ledger history | integration: `submit` refused with the head unchanged |
|
||||
| a judge timeout for one part | unit: that part stays asked and the version proposed, saying which; the others keep their verdicts |
|
||||
| a restart with parts asked | integration: the parts in `asked` are collected again and judged |
|
||||
| `reviewed` once per version | unit: one `reviewed` after the last `part-reviewed`, carrying every verdict |
|
||||
| told once | unit: a whole-set finding told once across a restart, again only after its mark is cleared |
|
||||
| a second run of the move | unit: changes nothing and says so |
|
||||
| the move's check | unit: a changed review, a missing finding and a missing ask, each named |
|
||||
| writes during the switch | unit: every writing verb refused with its reason; reading verbs answer |
|
||||
| an older judge | the indexer's judge test (ADR 0595 §8): a manifest asking for a ledger in `requires` refused by a judge that does not know the template, accepted by one that does |
|
||||
| the bus module's forms | the bus module's test (design 25 § 12), against a real bus server: the ledger, states and stream created for their module, `reviews` heard by a module granted it in `readers` and by no other; the condition at 75 percent raised and cleared below |
|
||||
| a reference | unit: "record › title › row" resolves to the section; a nested title is refused |
|
||||
| a form-only version | unit: accepted when only names and links changed and each target resolves; refused otherwise |
|
||||
| an unproven caller | unit: `open`, `write`, `submit`, `propose` and `take-over` refuse it |
|
||||
| the index behind | unit: health unhealthy while the index's sequence is behind the ledger's state; `ask` refuses "index not built" |
|
||||
| a forward delivery after a rollback | unit: an acceptance and a verdict made by the older module are found by the check at start, the writing verbs refused meanwhile, the changes copied and checked, then writes served; a record changed on both sides is told to the operator |
|
||||
| a restart between `apply` and the database write | unit: the module is stopped after `apply` and before the database write, restarted, and the database matches the history; health showed it behind meanwhile |
|
||||
| the database refuses past its tries | unit: `database-behind` names the record; the drop is refused while it stands; the catch-up tool clears it |
|
||||
| a restart between `apply` and its event | unit: the module is stopped after `apply` of a `review`, restarted, and sends one `reviewed`; a second start sends none |
|
||||
| the move as operations | unit: each record's versions copied as `write-section` and `submit` operations in order; the derived head equals the database's; a second run appends nothing |
|
||||
| the start | integration: the time to a built index measured and reported, failing above 40 seconds, the estimate's top |
|
||||
| A section is a part with a title | a table test, one case per refused name and text of § 2.2; a form-only version whose text changed beyond its links is refused |
|
||||
| Every bucket has a tool, and health says the index | the manifest test compares tools and events with the code; a unit test of the health before and after the index is built |
|
||||
|
||||
**The third slice's acceptance tests** (§ 9.1 to § 9.3; research 062's direction), what its build proves before it
|
||||
is done:
|
||||
@@ -739,77 +320,7 @@ is done:
|
||||
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 catalog or the instruction text pointing at a removed path.
|
||||
|
||||
**The fifth slice's acceptance tests** (§ 1, § 2, § 4, § 5, § 8.1; ADR 0592), what its build proves before it is
|
||||
done:
|
||||
|
||||
1. The move's check, run live, names no difference between the database and the bus: 656 decision records and 80
|
||||
research efforts (the counts of 2026-11-02, or the counts on the day), every version, clause and link, and the
|
||||
digest of every section of every version.
|
||||
2. The operator is told that result and asked before the database is dropped; after the yes and the delivery, the
|
||||
module's manifest requires no database and the store holds none for it.
|
||||
3. A session writes a research effort of more than 100 KiB one section at a time from one node; a session on another
|
||||
node takes the draft over with a reason, the first session hears `taken-over`, and the second submits it.
|
||||
4. Every changed section of that effort is judged on its own, each question under 60 KiB, and the effort is
|
||||
accepted once every part approves.
|
||||
5. A section named like a file, a text holding a local path, and a section over 40 KiB are each refused, the last
|
||||
saying split it.
|
||||
6. After the titles' form-only versions, no record holds a section named like a file, `history` shows each as a
|
||||
change of form, and `get` of "research 062 › Direction" answers that section.
|
||||
7. The fill tool shows each bucket and stream against its cap; the second version of a record with one changed
|
||||
section adds one `write-section` and one `submit`, not one operation per section.
|
||||
8. During the move's window a `write` is refused saying the knowledge base is moving; after the switch a restart
|
||||
still serves from the bus; the health names the source and the check's result.
|
||||
9. A part's review waiting on the judge survives a restart of the module, and one past 30 minutes reaches the
|
||||
operator once.
|
||||
10. Every test of the table above passes in the one check of the pull requests, within the test budget.
|
||||
11. As a ledger (ADR 0597 How it is checked, clauses 11 and 12): `get` answers the same digest for every record
|
||||
before and after the move; `ask` answers from bodies read from their operations; `mesh-knowledge.reviewed` is
|
||||
emitted once per review; the module's manifest asks for no stream `versions`, no object bucket `sections` and
|
||||
no state `names`, and the bus holds none for it once the move's check is clean; its tools and health name the
|
||||
ledger's repairs and lag, and no digest store; the consumers of `reviews` still hear it.
|
||||
|
||||
## What it leaves out
|
||||
|
||||
Semantic or vector search; a browser view (its reading of the buckets is announced, ADR 0592); line comments on a proposal (the judgement's findings name the section and
|
||||
Semantic or vector search; a browser view; line comments on a proposal (the judgement's findings name the section and
|
||||
clause instead); and the rules of how-we-build, which are not decision records. Each needs a decision of its own.
|
||||
|
||||
## Reviewed by architectural domain
|
||||
|
||||
The fifth slice was reviewed by architectural domain on 2026-11-02 (four reviewers, two architectural domains each; the knowledge base's own
|
||||
review of ADR 0592's fourth version beside), and the lead ruled on every finding. What was found, and what answers it:
|
||||
|
||||
| Architectural domain | Found | Answered by |
|
||||
|---|---|---|
|
||||
| security | unproven callers could write or take over; an Approve did not bind the sections shown; readers were named outside ADR 0363's grant (the knowledge base's review of version 6); a proposal waiting on the operator could be taken over; readers' grants and a body's match with its name were unsaid; a draft had no bound | ADR 0592 §4, §8, §9, §17, §19, §20, §21; § 2.1, § 4 |
|
||||
| seams | asked parts lived only in memory; told marks and reservations had no form on the bus; the judge's ticket is polled; who creates buckets and grants was unsaid; the judge is a module's tool, not a seat | ADR 0592 §2, §7, §16, §23; § 1, § 5 (the judge's seat a note on issue 1956), § 8.1 |
|
||||
| running qualities | three writes could stop halfway; caps had no figures; a usual revision judged every section again, with no limit on parallel calls; start time unknown | ADR 0592 §10, §22, §25; § 1, § 2.1 (caps), § 5 |
|
||||
| health | a stale index, a missing body, a full bucket and the move's state were not visible; waiting reviews and idle drafts were not counted | ADR 0592 §25, §33; § 1.1 |
|
||||
| events and logs | per-part verdicts would have broken every wait on `reviewed`; events did not say what they touched; a take-over's reason and each section's author were lost after 30 days | ADR 0592 §3, §27, §34; § 1.1 (the events table) |
|
||||
| testing | each failure path lacked a test; the budget was not counted | ADR 0592's How it is checked; § 10 (the failure-path table) |
|
||||
| upgrade | writes during the switch, a rollback after it, and the order of the three deliveries were unsaid; the form-only check did not prove new link targets | ADR 0592 §13 to §16, §31; § 8.1, § 2.2 |
|
||||
| tools | unnamed objects, missing bodies, form-only versions, asked parts and told marks had no tool | ADR 0592 §32; § 1.1 (the tools table) |
|
||||
|
||||
The knowledge base's review of the fourth version found that the reference form read as nested sections: ADR 0592
|
||||
§30 and § 2.2 say sections never nest.
|
||||
|
||||
### The amendment of 2026-11-03
|
||||
|
||||
The amendment of 2026-11-03 to ADR 0595, ADR 0596 and ADR 0597 (designs 25 § 12, 32 § 4 and 55 together) was
|
||||
reviewed by the four reviewers by architectural domain side by side (playbook 01), over three rounds, and the lead
|
||||
ruled on every finding. What was found, and what answers it:
|
||||
|
||||
| Domain | Found | Answered by |
|
||||
|---|---|---|
|
||||
| security | the author could be words the module chose; a person's dangerous tools had no role, mark or path to the process; the caller travelled as a plain field | design 25 § 12.3 (marks, the process's own tool subject, the identity seat's signed token), § 12.4 (the tool runner stamps the caller it proved; the move's authors marked imported) |
|
||||
| seams | the bus module asked for its own events in the new form, which it would have to create itself (ADR 0595 §10); a person's tool had no join to the process; a verdict was written twice with no order | design 25 § 12.3 (its events on its old `streams` section; the process's own tool subject); design 55 § 2.1 (the `review` operation first, `reviews` caught up from it) |
|
||||
| running qualities | overdue creation was undefined and raised by nobody; the database's rollback write lived in memory; a forward delivery after a rollback could miss acceptances and verdicts; no size estimate of the heads | design 25 § 12.2 (five minutes, the watchman, durable consumers, `check` at start); design 55 § 8.1 step 6 (`database.sequence`, the check at every start before the drop), § 2.1 (the heads' estimate) |
|
||||
| health | the bus module's health would have fired on a quiet mesh; conditions had no clearing; `database-behind` had no clearing | design 25 § 12.2 and § 12.3 (each condition with what clears it); design 55 § 8.1 step 6 |
|
||||
| events and logs | an event lost when the module stopped after `apply`; the bus module's events had no reader | design 55 § 1 (each event sent with its operation's sequence as id, the state `sent`); design 25 § 12.3 (`history` of one entry) |
|
||||
| testing | the bus module had no test that starts it whole; live tests would have filled the live bus; the budget was not counted | design 25 § 12.7 (the nats module's integration test, live drift only by a manifest change, the budget paragraph); design 55 § 10 |
|
||||
| upgrade | the build order lacked the grants part's binding and two undo steps | design 25 § 12.6 |
|
||||
| tools | the ledger's repairs had no tool for the next owner; the bus module's events had no read tool | design 25 § 12.5 (served for every ledger owner), § 12.3 |
|
||||
|
||||
**Left to the operator**, because each changes an accepted record: a margin between raising and clearing the
|
||||
condition `full` (ADR 0595 §6 raises and clears it at 75 percent, so an entry sitting there flaps), and an event of
|
||||
its own for `drop` and `recreate`. **A correction of fact** for ADR 0595 §4: the bus module deletes through `recreate`
|
||||
as well as `drop`. Each is noted on issue 1966.
|
||||
|
||||
@@ -11,14 +11,7 @@ amendment, issues, build handoff, constitution sync) is documented there, and ag
|
||||
through them. Thin skills in `.claude/skills/` wrap these playbooks for invocation
|
||||
(`hq-new-research`, `hq-graduate`, `hq-new-issue`, `hq-diagnose`, `hq-amend-design`,
|
||||
`hq-handoff`, `hq-sync-constitution`, `hq-status`, `hq-cleanup`); each defers to its playbook as
|
||||
authoritative and adds only the mechanical scaffolding. Four reviewer agents in `.claude/agents/` carry
|
||||
playbook 01's *Review by architectural domain*, two architectural domains each:
|
||||
`review-security-and-upgrade`, `review-seams-and-tools`, `review-running-and-testing` and
|
||||
`review-health-and-events`; the lead starts all four side by side and rules on their findings. A fifth,
|
||||
`research-reader`, carries one reading task of playbook 01's step 5: code on main, counts from the manifests, a
|
||||
try in process. Four more carry playbook 07: `mesh-preparer` (the prepare step), `mesh-developer` (develop),
|
||||
`mesh-code-reviewer` (the review) and `mesh-watcher` (every wait). Every agent starts from the knowledge base and maps
|
||||
each interaction onto a mesh path; the lead writes only the handover, never an agent's way of working. A subagent runs in the session that started it, from hq: its way of working comes from its definition and hq's playbooks, its knowledge from the knowledge base; one given a coding task works in a worktree under hq's `.work/` and also reads the conventions of the repository it changes, and where those contradict hq, hq wins and the agent says so.
|
||||
authoritative and adds only the mechanical scaffolding.
|
||||
|
||||
## The development cycle
|
||||
|
||||
|
||||
Reference in New Issue
Block a user