Files
hq/03-DESIGN/01-to-be/55-the-knowledge-base.md
T
mesh-tools 6be8f6eada
mesh/merge-gate head 6be8f6eada67 merged into trunk b7e22be7f55f — pass: [release feat/57-config-and-provisioning, judged by its controller cf897e89fe31] t…
mesh/repo-check head 6be8f6eada67 merged into trunk b7e22be7f55f — pass: [release feat/57-config-and-provisioning, judged by its controller cf897e89fe31] i…
mesh/delivery delivered
Feature 57 (config and provisioning): the record of its designs, playbooks and glossary words, synced with main
2026-10-16 07:22:11 +00:00

219 lines
15 KiB
Markdown

---
layer: to-be
status: designed
code: []
updated: 2026-10-12
decisions:
- "ADR 0290"
- "ADR 0321"
---
# 55 — The knowledge base
**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,
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.
**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.
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).
## 1. The parts
```
control-node
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
│ served by the control-node's tool runner
├ events proposed, reviewed, accepted, changed, imported, drafted, confirmed, returned
└ consumes the router's warrants: the operator's Approve for a record that needs it (§ 5)
mesh-issues (control-node) the counters for decision and research numbers (§ 6)
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
any agent session, on any node
mesh MCP server ── mesh_search, mesh_describe, mesh_call ──▶ the verbs, over the bus
```
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.
## 2. What one record holds
- `number` and `kind` (decision or research), its key together; `title`, `topic`, `status`, `date`, `deciders`;
- its **reasoning**, in sections: for a decision, context, the options weighed, consequences, how it is checked,
references; for a research effort, its overview and each of its documents by name; markdown, as written;
- 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.
| Link | From → to |
|---|---|
| `supersedes` | a record → a record it replaces whole |
| `changes` | a clause or a record → a clause it replaces part of |
| `extends` | a record → a record it builds on |
| `became` | a research effort → a decision record or design |
| `rests-on` | a decision → a decision it stands on |
| `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 |
## 3. Clauses
A clause holds its record's number and its own (`0276 §4`), its wording, a `status` and a `source`:
| Status | Meaning |
|---|---|
| `proposed` | its record is not accepted yet |
| `in force` | holds, and nothing later changes it |
| `changed` | a later clause replaces part of it; the `changes` link names which |
| `superseded` | its record is superseded, or a later clause replaces it whole |
| Source | Meaning |
|---|---|
| `written` | written as a clause by the record's author |
| `drafted` | written by the judgement from a prose `Decision` section at the import; never served as in force |
| `confirmed` | drafted, then confirmed by a second, independent judgement (§ 7) |
## 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:
- a link names a record or clause that does not exist;
- a decision has no clause, or a clause has no bold lead;
- a record it supersedes or changes is not accepted, or one it cites as live is superseded without naming the record
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 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`:
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
channel that proves who answered (ADR 0274);
the knowledge base accepts it only on the warrant for Approve, which it keeps with the record.
On acceptance the record and its clauses move to `accepted` and `in force`, every clause its `changes` and
`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).
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
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.
## 6. Numbers
`reserve` takes a kind and asks the issue tracker for the next number of that sequence (issue 424, ADR 0275 rule 8).
The number is spent: a record that is never accepted keeps it as `withdrawn`, with who reserved it and when. The
knowledge base keeps no counter.
## 7. Drafting and confirming clauses at the import
For a decision whose `Decision` section is prose, the import asks the agent's judgement
(ADR 0278)
for its clauses: the section in, numbered statements out, each one sentence, each saying nothing the section does not.
They are stored `drafted`. A second judgement, in a separate call that sees only the record and the drafts, answers
per clause whether the record says exactly that; an accepted clause becomes `confirmed`, a rejected one stays
`drafted` with the reason. Which clause of an older record a `supersedes-in-part` changes is judged and confirmed the
same way. When every record of a topic has been through both, the operator is told once per topic, with the counts
confirmed and still drafted. `return` with a record's number and a reason sends it back to be drafted again.
**A drafted clause may be changed, and a linked clause keeps its number** (ADR 0321, issue 494). A later record's
`changes` may name a drafted clause, confirmed or not, as it may one in force or changed; only a superseded or proposed
clause is refused. On that record's acceptance the drafted clause is marked `changed`, naming the clause that changed
it, keeps its number and stays `drafted`: it is still never served as in force, and `get` and `ask` say both. Because
a redraft numbers a record's clauses anew, `return` refuses a record any of whose clauses another record changed or
links by number, in a version served or proposed, naming each such link; a record that changes the drafted clause is
the way to correct it. A record no other record links by clause is returned as before.
## 8. The import
`import` takes a commit of this repository (and `dry-run`) and writes every decision record and research effort
whole, at its own number, as its first version, naming the commit. It creates what is absent, never overwrites, never
advances a counter; a second run changes nothing and says so. Records written in this repository after the import,
while the first slice does not take writes yet, are taken in by running it again.
**Refresh.** `import` with `refresh` re-reads, at the commit given, every record whose versions were all written by
the import, a refresh, or the knowledge base's own drafting and confirming of clauses, with nothing waiting over
it, and writes the corrected record as a new version naming why; a record anyone else wrote a version of is
never touched. Clauses already drafted and confirmed are kept when the Decision section's text is unchanged, and
drafted again when it changed. It repairs what an earlier import misread (issue 487) and is idempotent: a second run changes
nothing. `dry-run` lists what it would change. Once this repository holds no records, `import` refuses rather than
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.
## 9. The slices after the first
| Slice | Delivers | Done when the operator sees | Tracked by |
|---|---|---|---|
| 1 | this document: the module, the import, clauses, answers, `propose`, its own review and acceptance | the acceptance sentence above, live, and a record proposed, reviewed and accepted without a pull request | issue 425 |
| 2 | the decision records and research efforts leave this repository; path mentions become "ADR NNNN" and "research NNN"; playbooks 01 and 02 changed | this repository holds no decision record, and `get` answers every one | issue 426 |
| 3 | designs, playbooks and glossary entries as kinds | `holds` answers for a design's decisions and a glossary word | issue 428 |
| 4 | meeting notes, drawings and blog posts | not decided; needs its own decision | issue 429 |
Numbers from the issue tracker, which `reserve` needs, are the tracker's own slice, tracked by issue 424. This
repository keeps no copy of the records, generated or otherwise (issue 435, answered by the operator).
## 10. How it is checked
| Rule | Checked by |
|---|---|
| A proposal that breaks a per-record rule is refused, naming every reason | the module's table test, one case per rule of § 4 |
| A record is accepted only after the knowledge base's own judgement approves it, and, when it needs the operator, only on the warrant | the module's tests: a proposal whose judgement answers changes, or cannot be had, stays proposed; a `needs operator` record unaccepted without the warrant, accepted with it |
| A decision is not accepted cited by nothing | the module's test: a decision with no home and no record linking it gets changes from the review, with no judgement asked; with a home, or once a record citing it is accepted, it is accepted |
| A whole-set rule that fails is loud, and leaves the module healthy | the module's test: a correction that leaves a decision cited by nothing tells the operator once, as a warning naming the rule and the record, not again after a restart, and clears it once a record cites it; the module's health stays healthy throughout |
| Every version is kept and comparable | the module's test: three versions, `history` lists them, the difference of the first and third is exact |
| The import changes nothing on a second run, and loses nothing | the module's test over a fixture repository; and the comparison of § 8 on issue 426 |
| A drafted clause is never served as in force | the module's test: `holds` over a record with only drafted clauses answers none, and `ask` marks them |
| A drafted clause may be changed, and a linked clause is never renumbered | the module's tests: a record changing a drafted clause is accepted, and the clause is then `changed`, keeps its number, stays drafted and is not served; `return` of a record whose clause another record changed or links by number is refused naming the link, and writes nothing |
| 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 |
## What it leaves out
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.