9 Commits
Author SHA1 Message Date
jschoubben c2b162a9e8 Design 55: readers by grant, the take-over refused while an Approve waits, the grants part's own test, §30 for nesting, the glossary's word in the review table
mesh/delivery ready: it is merged on the Approve
mesh/check pass c2b162a9e8da on 2731c796f0d6 | unit 39 run, 0 kept, 1 skipped | integration 39 run, 0 kept, 1 skipped | composition passed
2026-11-02 19:58:01 +01:00
jschoubben 3122e8be84 Design 55: follow ADR 0593: the mesh-sdk release sdk1 does the controller's part (the grants part creates, grants and warns; the indexer's judge checks the forms) 2026-11-02 19:55:40 +01:00
jschoubben 36517018d3 Design 55: one more use of the chosen word 2026-11-02 19:55:40 +01:00
jschoubben b8ad9da784 Design 55: the review section uses the word the operator chose, architectural domain 2026-11-02 19:55:40 +01:00
jschoubben eaa98758b9 Design 55: follow ADR 0592's tenth version: submit commits when its version is stored, and the tests for the rollback's double write and the Approve bound to its sections 2026-11-02 19:55:40 +01:00
jschoubben b9a42eaf90 Design 55: follow ADR 0592's eighth version: readers by ADR 0363's grant, extended to the object bucket; each cap within its form's bound; clause numbers after the splits 2026-11-02 19:55:40 +01:00
jschoubben 6256fc8e3b Design 55: follow ADR 0592's sixth version and the review by aspect: whole-or-refused writes, caps, the move's window and rollback, the delivery order, the judge's judged event, events with what they carry, a test per failure path, and a section Reviewed by aspect
The glossary gains opened and drafted, so a draft's first step is never called drafted.
2026-11-02 19:55:40 +01:00
jschoubben 012d276dcc Design 55: follow ADR 0592's revision: a stream that keeps its history, a module's own object bucket, the frame's bound and the move's full check 2026-11-02 19:55:40 +01:00
jschoubben 5a9a5d8d1d Design 55: the knowledge base keeps its records on the bus and is written and reviewed by section (ADR 0592), so a long record is judged whole and a draft lives only in the knowledge base
The glossary gains section, draft, form-only version and search index, and the homonyms section and draft.
2026-11-02 19:55:40 +01:00
2 changed files with 437 additions and 40 deletions
+24 -2
View File
@@ -967,7 +967,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.
0284, 0290, 0291, 0423, 0424, 0592.
- **research effort**, **graduation**, **hand-off**, **playbook** — an investigation, held whole in the
knowledge base and named "research NNN";
@@ -985,7 +985,25 @@ 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.
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).
- **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/`.
@@ -1089,6 +1107,10 @@ 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 |
+413 -38
View File
@@ -2,34 +2,47 @@
layer: to-be
status: in-progress
code: [mesh-catalog, hq]
updated: 2026-10-18
updated: 2026-11-02
decisions:
- "ADR 0290"
- "ADR 0321"
- "ADR 0423"
- "ADR 0424"
- "ADR 0592"
- "ADR 0593"
---
# 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.
**The mesh keeps its decision records and research efforts whole in a module of its own, `mesh-knowledge`, one
instance on the control-node, holding the seat `knowledge-base`: each record's number, status, topic, typed links,
its clauses with their own status, and its reasoning, with every version kept. A record is proposed, checked when it
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,
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 and ADR 0592,
from research 045.)
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.
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.
**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.
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.
This document designs the first slice in detail, tracked by issue 425. The slices after it are in
This document designs the first slice in detail, tracked by issue 425, and the fifth, the records on the bus
(ADR 0592). The slices after the first 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).
@@ -40,17 +53,32 @@ 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
├ a database in the store records, versions, clauses, links; ranked full-text search
├ verbs ask, get, holds, links, history, reserve, propose, import, return
├ on the bus, written by it alone (§ 2)
│ state heads one key per record: its fields, clauses, links, sections as title and digest
│ state drafts one key per record being written: its author and its sections as title and digest
│ state asked one key per part asked of the judge and not yet answered: ticket, node, when
│ state names per digest: how many versions and drafts name it, and since when none has
│ state findings the whole-set findings standing now, and the told marks (told.<topic>)
│ stream versions one message per version: who, when, why, its mark, the head as it stood
│ stream reviews one message per part reviewed: the digest judged, the verdict, the findings
│ objects sections every section's body, once, named by its sha256 digest
├ in its memory the search index, built from the bus at start and kept by watching heads
├ verbs ask, get, list, holds, links, history, findings, progress,
│ reserve, open, write, remove, order, submit, drop, take-over, propose,
│ withdraw, retract, import, return
│ served by the control-node's tool runner
├ events proposed, reviewed, accepted, changed, imported, drafted, confirmed, returned
├ events reserved, opened, written, removed, ordered, submitted, dropped, taken-over, proposed,
│ part-reviewed, reviewed, accepted, changed, swept, moved, checked, formed,
│ 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 proposal (§ 5), draft and confirm clauses (§ 7)
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)
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
@@ -59,20 +87,86 @@ 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.
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 write is whole or refused, and `submit` commits when its version is stored** (ADR 0592 §10). Every change to
`heads`, `drafts`, `asked`, `names` and `findings` is a compare-and-set on the key's revision, retried at most three
times and then refused with the reason. The commit point of `submit` is the version message stored on `versions`: a
failure before it is stored refuses the call, and nothing is taken. Once it is stored, the call answers that the
version is taken; the head's update against its revision and the event are then completed at once, retried, and at
the next start for any head behind its last stored version. So nothing an author was told was refused takes effect
later. A reservation is a head created only if the key is absent, so two sessions reserving at once get two numbers
or one refusal.
**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 and the bodies of every current version into it, then keeps it current by watching
`heads`, noting the last revision of `heads` it took in. `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
`heads` 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):
| Tool | Reads or does |
|---|---|
| read a head | one key of `heads`, whole |
| read a version | one message of `versions`, with its sections' titles, digests and authors |
| read a section | one object of `sections`, by its digest, checked against it |
| 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 |
| sweep | lists the objects no version or draft names and since when; sweeps them now; dry run |
| integrity | checks every named digest has its object; restores a body from given text once its digest matches |
| 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 latest revision of `heads`, when a digest some version names has no object (counted, each named), 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 grants part's (§ 2.1).
**Events** (ADR 0592 §34) are kept 30 days; what a later reader needs is also in the version message. 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 |
| `swept` | the digest removed, since when nothing named it |
| `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.
## 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: 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;
- 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);
- for a decision, its **clauses** (§ 3);
- its **links**, typed, each stored once and read from both ends;
- its **versions**: every change is a new version with who made it, when, and why in one line; `history` lists them
and gives the difference between any two. A progressive insight is a version marked as one, dated, saying what stood
before.
before. A form-only version (§ 2.2) is marked `form`.
| Link | From → to |
|---|---|
@@ -84,6 +178,90 @@ the records module, as the issue tracker's import does.
| `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 in three of the bus's own forms, so that a new version costs only what changed (ADR 0592 §1 to §9):
```
heads (state) versions (stream) sections (object bucket)
┌─────────────────────┐ ┌──────────────────────────┐ ┌────────────────────────┐
│ decision.0290 │ │ decision.0290 version 7 │ │ 3f9a… "Context" text │
│ title, status, … │ │ who, when, why, mark │ │ 81c2… "Options" text │
│ clauses, links │ │ the head as it stood: │──────▶ │ 0d77… "Context" text, │
│ current: 8 │ │ Context 3f9a… │ │ as changed in 8 │
│ sections: │ ├──────────────────────────┤ │ … │
│ Context 0d77… │──────▶│ decision.0290 version 8 │──────▶ └────────────────────────┘
│ Options 81c2… │ │ Context 0d77… │ each body once, named by
└─────────────────────┘ │ Options 81c2… │ its sha256 digest
└──────────────────────────┘
```
- **The head** is one key in the state `heads`, `<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 and a digest. A key's value stays under 256 KiB
(ADR 0201). A reserved number is a head at `reserved`, with who reserved it, when, for what and when it expires.
- **A version** is one message on the stream `versions`: who wrote it (the caller the bus proved), when, why, its
mark, who wrote each section, the reason of any take-over since the version before, and the head as it stood. It never copies a section's body. The stream keeps its history: every message,
with no age limit, and when it reaches its cap it refuses a new message rather than dropping its oldest, with a
condition raised at 75 percent of the cap. A module's stream may declare that retention; before, a stream was kept at
most 30 days (ADR 0592 §6, changing ADR 0363 §1). The stream `reviews` keeps its history the same way. The
condition at 75 percent is raised by the grants part (mesh-grants), in one place, for every history stream and
object bucket of every module (ADR 0593 §10, changing ADR 0592 §16).
- **A section's body** is one object in the bucket `sections`, named by the sha256 digest of the body: a section
unchanged in a new version costs nothing. The module checks that a body hashes to its name each time it reads one.
The state `names` keeps, per digest, how many versions and drafts name it and since when none has; an object no
version and no draft names is removed a day after the last stopped naming it, each removal the event `swept`. The bucket is storage of the bus, as the object bucket of ADR 0486 is. Any module
may declare an object bucket of its own in its manifest, with a local name and a cap, created as its state buckets
are, kept with no age limit and refusing a new object when full (ADR 0592 §5).
- **A draft** is a key in the state `drafts` (§ 4). **A part's verdict** is a message on the stream `reviews`, and 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.
**Caps** (ADR 0592 §25). Each is set from the move's dry run, within its form's bound: a stream at most 8192 MiB
(ADR 0363), a state at most 64 MiB (ADR 0201), an object bucket at most 8192 MiB (ADR 0592 §5). The starting figures, from
today's 83 MB for 736 records with every version copying every section, are:
| Bucket or stream | Cap |
|---|---|
| `versions` (stream) | 1 GiB |
| `reviews` (stream) | 512 MiB |
| `sections` (object bucket) | 1 GiB |
| `drafts` (state) | 64 MiB |
| `heads`, `asked`, `names`, `findings` (state) | 64 MiB, ADR 0201's bound |
A write refused at a cap names the cap, and the module's health turns unhealthy naming it.
**Who may read each** follows the mesh's own forms, read-only (ADR 0592 §8, §9). `versions` and `reviews` are
restricted streams (ADR 0363): a module hears them only through its own `consumes` and a `grant` in mesh-knowledge's
trusted config value `readers`, which names the web page that shows records once it exists and nothing today. The
object bucket `sections` is read the same way, through a `reads` entry and a `grant` of `readers`: ADR 0592 extends
ADR 0363's grant to a module's object bucket, and the grants part composes a read-only grant on that one bucket
(ADR 0593 §10).
`heads` is read by a module that names it in `reads`; `drafts`, `asked`, `names` and `findings` by mesh-knowledge
alone. No secret is kept in any of them. A grant cannot limit a reader to some objects of a bucket
(ADR 0488), which is why reading is announced per bucket and never per record.
### 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`:
@@ -103,9 +281,47 @@ 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). `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:
`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 and digest. Each `write` stores the section's body in the
object bucket `sections` and its digest in the draft, so the knowledge base holds the only copy of a draft and a
session on any node can read it. 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:
- a link names a record or clause that does not exist;
- a decision has no clause, or a clause has no bold lead;
@@ -113,21 +329,62 @@ its links, and for each `changes` the clauses it changes. It is refused, with ev
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.
- 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.
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 `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`:
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:
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
@@ -138,18 +395,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, 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, 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).
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 `propose`, because no record can link a number only reserved: a record that cites a new
504). It is not refused at `submit`, 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, or by a progressive insight: a new version marked as one, which
is reviewed like a proposal.
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.
## 6. Numbers
@@ -194,6 +451,41 @@ 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):
1. **Order.** Two deliveries, in order (ADR 0593 §10, changing ADR 0592 §16): first the mesh-sdk release sdk1
(issue 1585), which carries the manifest's field types for `objects` and `retention: history`, their checks in
the indexer's judge, and in the grants part the creation of the object buckets and history streams, the
read-only grants of `readers` on them and the condition at 75 percent for every such bucket and stream in one
place; then mesh-knowledge. An indexer whose judge predates these keys refuses a manifest that has them, so
mesh-knowledge cannot be delivered before the release. 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 version's sections into `sections` by
digest, each version as a message on `versions`, each record's head into `heads`, 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 the bus,
that each body hashes to its name, 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, so a rollback of
the delivery reads a current database. A rollback is safe only until the drop.
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 |
@@ -202,6 +494,7 @@ field by field and section by section, and the result is written on issue 426.
| 2 | the decision records and research efforts leave this repository; path mentions become "ADR NNNN" and "research NNN"; playbooks 01 and 02 changed | this repository holds no decision record, and `get` answers every one | issue 426 |
| 3 | designs and the glossary's entries as records, moved in one delivery, and each design's state worked out by the knowledge base (§ 9.1 to § 9.3; ADR 0423, ADR 0424) | `get` answers design 47 with its sections and links, a word's entry answers from the knowledge base, and a design's state follows its issues' deliveries without anyone writing it | issue 428 |
| 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), delivered as the mesh-sdk release sdk1, 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
@@ -299,6 +592,45 @@ 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 in the bus's forms, a section once by its digest, and read only as announced | 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 version messages and four objects; a module with no `grant` in `readers` is refused the streams and the object bucket |
| 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 stream that keeps its history, and an object bucket of a module's own | in the integration test: a history stream at its cap refuses a new message and keeps its oldest; an object no version or draft names is removed after a day, one still named is kept; unit tests of registration refusing a name already taken |
| 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 stream and the head | unit: the call answered that the version is taken; a start completes the head from the stored version and sends the event once |
| a failure before the version is stored | 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 named digest has no object | unit: health unhealthy, naming it; the integrity tool restores it from given text |
| a body does not hash to its name | unit: the read refuses it, naming the digest |
| a full object bucket | integration: a write refused naming the cap; health unhealthy |
| a full history stream | 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 (release sdk1): a manifest with `objects` or `retention: history` refused by a judge that does not know them, accepted by one that does |
| the grants part's forms | the grants part's test (release sdk1), against a real bus server: an object bucket and a history stream created for their module; a read-only grant on that one bucket for a module named in `readers`, removed at the next composition once withdrawn; 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 revision is behind `heads`; `ask` refuses "index not built" |
| the start | integration: the time to a built index measured and reported |
| 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:
@@ -320,7 +652,50 @@ 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 object, not one 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.
## What it leaves out
Semantic or vector search; a browser view; line comments on a proposal (the judgement's findings name the section and
Semantic or vector search; a browser view (its reading of the buckets is announced, ADR 0592); 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.