mesh/merge-gate pass: builds mesh-knowledge → novox; no bus step; every machine composes with the change as it did without (4 of 4 compose)
mesh/repo-check pass: its merge-check.sh passed, each part in its toolchain: merge-check.sh (go), merge-check-typescript.sh (typescript)
mesh/delivery covered: a later merge that contains it was delivered: novox/mesh-catalog@1b7f79f76177 (merged as 73367192 into main, walk plan-17917161183…
180 lines
13 KiB
Markdown
180 lines
13 KiB
Markdown
# mesh-knowledge — the knowledge base
|
|
|
|
The mesh's decision records and research efforts, held whole in one module (novox/hq ADR 0290, to-be 55;
|
|
the first slice, hq issue 425). One instance on the control-node holds the seat `knowledge-base`, once for the
|
|
mesh. Each record keeps its number, kind, title, topic, status, date and deciders. It also keeps its typed links,
|
|
read from both ends, and its reasoning in sections. A decision also keeps its **clauses**: each numbered statement
|
|
it makes, with its own status and source. Every version of every record is kept, with who made it, when and why.
|
|
|
|
An agent asks what holds and gets the clauses in force, the records they come from, what changed them since, and
|
|
how to read the reasoning, all in one call under 4 KiB. A record is proposed and checked when it is written, then
|
|
reviewed by the knowledge base itself, in a judgement of its own, then accepted. When its author says it needs the
|
|
operator, it is accepted only on the operator's Approve, given on a channel that proves who answered.
|
|
|
|
## The verbs (seat `knowledge-base`)
|
|
|
|
| Verb | Takes | Answers |
|
|
|---|---|---|
|
|
| `ask` | `question` (at most 500 characters), `bound` | the records and clauses bearing on the question, ranked; each clause says whether it holds, what changed it, or that it is only drafted; each record says what changed it since and how to read its reasoning |
|
|
| `get` | `number`, `kind`, `reasoning`, `section`, `version`, `bound` | one record: clauses, links both ways, what changed it since, versions, reviews, the operator's ask; its reasoning only when asked |
|
|
| `holds` | `topic` or `number`, `bound` | the clauses in force for a topic of the reading order or for one record; never a drafted clause |
|
|
| `links` | `number`, `kind` | the record's typed links, out and in |
|
|
| `history` | `number`, `kind`, `from`, `to`, `bound` | every version; with `from` and `to`, the difference between the two, line by line |
|
|
| `reserve` | `kind`, `for` | the next decision or research number, asked of the issue tracker (`issue-tracker.reserve`); spent whether or not its record is accepted |
|
|
| `propose` | the record whole | proposed, or refused naming every rule it breaks; an accepted record changes only by a progressive insight (`insight`). Each version proposed is then reviewed by the knowledge base itself (below) |
|
|
| `import` | `dry-run`, `commit` | hq's decision records and research efforts at the commit the records module stands at, each whole at its own number |
|
|
| `return` | `number`, `reason` | drafted clauses sent back to be drafted again |
|
|
|
|
`ask`, `get`, `holds` and `history` stay within `bound` bytes (4096 unless asked for, at most 65536) and say what they cut
|
|
and how to read the rest. The module's own tool `knowledge_health` answers its health (below).
|
|
|
|
**Who acted** is the caller as the runtime proves it, never an argument: no verb takes one. The tool runner takes
|
|
the caller's bus user from the caller-named subject a call arrived on and hands it through the SDK
|
|
(`stdio.CallerOf`, hq issue 365). A call on the subject that names no caller (served beside it for one release, hq
|
|
issue 464), or through a runtime from before issue 365, is recorded as by `unproven`. In this module the caller is
|
|
read in one place, `callerOf` in `main.go`.
|
|
|
|
The rules about callers:
|
|
|
|
- A proposal is revised only by its author, as the runtime proves them.
|
|
- A number reserved by a proven caller is proposed only by that caller.
|
|
- A record that has a version whose writer was unproven is never reviewed, and so never accepted: who wrote it
|
|
cannot be told, and no one revises it, so its author proposes it again under a new number.
|
|
|
|
No agent reviews a record: the mesh proves which machine called, not which agent on it (hq issue 365), so the
|
|
review is the knowledge base's own (ADR 0290 rule 4).
|
|
|
|
## What a record holds
|
|
|
|
A clause is `proposed`, `in force`, `changed` (a later clause replaces part of it, named in `changed-by`) or
|
|
`superseded`. Its source is one of three:
|
|
|
|
- `written`: written as a clause by its author;
|
|
- `drafted`: drafted from a prose Decision section by the agent's judgement at the import, and never served as in
|
|
force;
|
|
- `confirmed`: drafted, then confirmed by a second, separate judgement that saw only the record and the drafts.
|
|
|
|
The links are `supersedes`, `changes` (to a clause: `0276 §4`), `extends`, `became` (from a research effort to a
|
|
decision record or design) and `cites` (to any record, an issue, or a design). Each link is stored once, on the
|
|
record it goes out from.
|
|
|
|
## Writing a record
|
|
|
|
1. `reserve` a number for its kind.
|
|
2. `propose` the record whole. It is refused, naming every reason at once, when it breaks one of the **29 rules
|
|
that judge one record or its prose**. They are hq's checks moved to the write; `PerRecordRules` in `rules.go`
|
|
lists each one with the check it comes from:
|
|
- 25 on the structure: kind, reserved number, title, topic, date, deciders, research status, at least one
|
|
clause, clause numbering, bold lead, clause size, the Context, Consequences and "How it is checked" sections,
|
|
a research effort's overview, link kind, link target, link fit, no link to itself, what it extends, what it
|
|
supersedes or changes is accepted, a changed clause is in force, a superseded record cited beside its
|
|
superseder, a graduated effort says what it became, and the links in its text resolve;
|
|
- 4 on the prose: no word the glossary retired, no daemon's line, a deferral names its tracking issue, and a
|
|
progressive insight in the dated, marked form.
|
|
|
|
A rule holds a record from the day hq's checks began to hold it (`RuleFrom` in `rules.go`):
|
|
- retired words from 2026-10-07 and daemons' lines from 2026-10-09 for a research effort; for a decision record
|
|
both from 2026-10-11, since hq's words check never read a decision record;
|
|
- deferrals from 2026-10-09;
|
|
- the clauses from 2026-10-11;
|
|
- the decision record's sections and the live-citation rule, which no hq check held on a decision record,
|
|
from 2026-10-11, the day the knowledge base takes the write.
|
|
|
|
A progressive insight keeps its record's date, so a correction to an older record is not refused by a rule
|
|
that never held it, and it keeps the links the accepted record holds. A new record is dated no earlier than the day its number was reserved.
|
|
3. Revise it by proposing again; every version is kept.
|
|
4. The knowledge base reviews every version proposed itself. It asks the agent module's judge
|
|
(`claude-code.claude_code_judge` on this node) one question, in a separate call with no tools, that holds only
|
|
the record, the records it links and the rules, and answers `approve` or `changes` with findings. The background
|
|
work asks it at once, before the next step of any drafting, one judgement at a time.
|
|
- On `changes` the record stays proposed, the findings kept with the version they judged (`get` shows them).
|
|
Its author revises it by proposing it again, and that version is reviewed again.
|
|
- A judgement that cannot be had (the judge unreachable, or an answer that is not `approve`, or `changes` naming
|
|
no finding) leaves the record proposed and says so on it (`get`'s `review`). It is never taken as an
|
|
approval. It is asked again after 10, then 20 minutes; after 3 tries the record says its author proposes it
|
|
again to have it reviewed again, and the module is unhealthy, naming it, so the mesh raises a condition.
|
|
An ask of the operator that cannot be made counts as a try the same way.
|
|
- A review whose outcome the database refused to keep is held back for that record alone (10, then 20
|
|
minutes, 3 tries, logged as given up), so it never blocks the records behind it.
|
|
- A progressive insight waiting for its review is revised only by its proven author.
|
|
- A record revised while it is judged is not decided on the version judged; the newest is judged next.
|
|
- On approve the record and its clauses are accepted and in force. Every clause it changes moves to
|
|
`changed`, and every record it supersedes moves to `superseded`, each as a new version of that record. A record marked `needs-operator` is first asked of the
|
|
operator (an ask that cannot be published is taken back, and the record is reviewed again later): an ask with
|
|
Approve and Decline, both at the level approve, each bound to this version's digest. It is
|
|
accepted only on the router's warrant for Approve, read back from the router's own record and held to the ask
|
|
composed again. A record changed after it was asked finds the digests differ, and nothing is accepted. The
|
|
warrant is kept with the record.
|
|
5. After every acceptance, the **4 rules that judge the whole record set** run:
|
|
- `cited`: a decision accepted here is cited by another record or names a home in hq;
|
|
- `supersession`: both ends of a supersession agree;
|
|
- `partial`: both ends of a change agree;
|
|
- `rests-on-live`: no accepted record extends a superseded one.
|
|
|
|
A failure is kept as a finding and makes the module unhealthy, so the mesh raises a condition naming the rule
|
|
and the records. The acceptance stands.
|
|
|
|
## The import
|
|
|
|
`import` reads hq through the records module at the commit its checkout stands at. With `commit`, it refuses a
|
|
checkout standing elsewhere. It writes every decision record and research effort whole, as version 1, naming the
|
|
commit and path it came from. It creates only what is absent and never overwrites. It never asks for a number, so
|
|
no counter moves. A second run changes nothing and says so. `dry-run` writes nothing. From a terminal,
|
|
`mesh-knowledge dry-run <hq checkout>` gives the same dry run over a checkout on disk, against an empty knowledge
|
|
base.
|
|
|
|
A decision whose Decision section writes numbered statements with a bold lead gets them as `written` clauses. One
|
|
written as prose (226 of main's 278 on 2026-10-11) gets none at the import. A background worker then takes one
|
|
judgement at a time (`claude-code.claude_code_judge` on this node): it drafts the clauses, then confirms them in a
|
|
separate call. A clause the second judgement rejects stays `drafted`, with the reason. hq's `supersedes-in-part`
|
|
named a record, not a clause: which clause it changes is judged and then confirmed the same way, and only then is
|
|
the older clause marked `changed`. When every prose record of a topic has been through both judgements, the
|
|
operator is told once (`operator-channel.notify`, a warning) with the counts. `return` sends a record back to be
|
|
drafted again.
|
|
|
|
## Where it keeps its records
|
|
|
|
In a database in the store: the module requires a `postgres-database` provision named `knowledge`, as
|
|
`claude-licence-manager` does, and reaches it through the address in `${dir:state}/database.url`. Ranked search is
|
|
the database's full-text search (`ask`). The schema is numbered migrations (`cmd/mesh-knowledge/migrations/`), and
|
|
the consolidated schema for a fresh database is `schema.sql`. A test holds the two equal on a real postgres.
|
|
|
|
## Its processes
|
|
|
|
- **the bundle**: the verbs, `knowledge_health`, the operator's answers heard, and once a minute (and at once when
|
|
a record is proposed) the router's record read, reservations never proposed withdrawn, the reviews of proposals,
|
|
and the drafting worker. It never changes the schema, and
|
|
refuses to serve while the schema is behind.
|
|
- **`prepare`** (run once): brings the schema to this version.
|
|
- **`keep`** (judged by its unit): judges the whole record set every five minutes and keeps the findings.
|
|
- **`watch`** (judged by `knowledge_health`, run as the machine's account, the one the runtime runs the bundle
|
|
as, so the tool can read what it writes): every minute, reads whether the database serves and what the
|
|
findings say, and leaves its verdict in `${dir:state}/health.json`. That is the file the health tool answers
|
|
from. A verdict older than three minutes is unhealthy.
|
|
|
|
## Settings
|
|
|
|
| Setting | Default | What it does |
|
|
|---|---|---|
|
|
| `judge-model` | `sonnet` | the model the review, drafting and confirming judgements ask |
|
|
| `drafting` | `on` | `off` stops drafting and confirming clauses |
|
|
| `reserved-for` | `168h` | how long a reserved number waits for its proposal before it is withdrawn |
|
|
|
|
## What it needs at release
|
|
|
|
- assignment to the control-node, and a postgres provider for its `postgres-database` provision;
|
|
- the issue tracker serving `issue-tracker.reserve` (hq issue 424, merged as mesh-catalog#204) on the mesh;
|
|
- the runtime proving the caller (hq issue 365: mesh-tools#27 and mesh-controller#224 delivered), or every
|
|
proposal stays unreviewed;
|
|
- the agent module on the control-node holding a licence, or every review waits and says so;
|
|
- the import run once, then checked against hq record by record (hq issue 426).
|
|
|
|
## Tests
|
|
|
|
`go test ./...` in this directory. The store tests run over memory and over a real postgres. That postgres is
|
|
either `MESH_TEST_POSTGRES` or a private server the tests start from the machine's `initdb` on a socket of its own,
|
|
listening on no port. A machine with neither skips the postgres half and says so. The fixtures are real hq files:
|
|
`testdata/hq` holds eleven records at the commit in its `COMMIT` file, and `testdata/hq-main.tar.gz` holds the
|
|
whole of main at `testdata/hq-main.commit`. The bus test starts the bundle as the runtime does and plays the
|
|
runtime over a real NATS server.
|