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

This commit is contained in:
2026-11-02 19:55:40 +01:00
parent 6256fc8e3b
commit b9a42eaf90
+33 -28
View File
@@ -89,7 +89,7 @@ The module is written in Go against the SDK. It holds no checkout of this reposi
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** (ADR 0592 §9). Every change to `heads`, `drafts`, `asked`, `names` and `findings` is a
**A write is whole or refused** (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. `submit`
writes the message on `versions` first, then the head against its revision, then the event; a failed step refuses
the call. At start the module rebuilds any head behind its last version. A reservation is a head created only if
@@ -99,13 +99,13 @@ the key is absent, so two sessions reserving at once get two numbers or one refu
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 §10). The integration test measures the start; the estimate is 20 to 40 seconds and about
`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 §26):
(ADR 0592 §32):
| Tool | Reads or does |
|---|---|
@@ -124,7 +124,7 @@ Every piece of state has a tool that reads it, and every act the module does by
| 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 §27) is unhealthy, naming the cause, when a bucket or stream does not answer, when the index is
**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
@@ -132,7 +132,7 @@ waiting more than 30 minutes. A part waiting more than 30 minutes is asked of th
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 controller's (§ 2.1).
**Events** (ADR 0592 §28) are kept 30 days; what a later reader needs is also in the version message. Each carries:
**Events** (ADR 0592 §34) are kept 30 days; what a later reader needs is also in the version message. Each carries:
| Event | Carries |
|---|---|
@@ -176,7 +176,7 @@ None is named `drafted`, which stays the event of a clause the import's judgemen
### 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 §8):
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)
@@ -213,7 +213,8 @@ A record is kept in three of the bus's own forms, so that a new version costs on
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 §21). Each is set from the move's dry run, within ADR 0363's 8192 MiB; the starting figures, from
**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 |
@@ -222,13 +223,17 @@ today's 83 MB for 736 records with every version copying every section, are:
| `reviews` (stream) | 512 MiB |
| `sections` (object bucket) | 1 GiB |
| `drafts` (state) | 64 MiB |
| `heads`, `asked`, `names`, `findings` (state) | ADR 0201's 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** is announced in the manifest. `heads`, `versions`, `sections` and `reviews` may be read by a
module the manifest names, such as the web page that shows records once it exists, and every such grant is read-only;
`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
**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 controller composes a read-only grant on that one bucket.
`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
@@ -236,9 +241,9 @@ module the manifest names, such as the web page that shows records once it exist
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 §23).
target is a relative path or a file (ADR 0592 §29).
**A reference has one form** (ADR 0592 §24): the record, a section's title, then a row or clause inside that
**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.
@@ -250,7 +255,7 @@ names it changed, and is accepted without a judgement because a check proves onl
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 §25).
(ADR 0592 §31).
## 3. Clauses
@@ -272,7 +277,7 @@ 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 §14):
version (ADR 0592 §17):
| Verb | Does |
|---|---|
@@ -299,14 +304,14 @@ object bucket `sections` and its digest in the draft, so the knowledge base hold
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 §16). Only the author writes a draft or revises a proposal;
**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 §17): `open`, `write`, `submit`, `propose` and `take-over` refuse a caller
**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.
@@ -332,7 +337,7 @@ 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 §18 to §22, changing ADR 0290 §4): one judgement per part, each in a
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.
@@ -358,7 +363,7 @@ the mesh's inference layer once one exists; nothing in this design depends on wh
version 3 stays proposed, with the findings of Direction
```
**The parts wait on the bus** (ADR 0592 §19). Each part asked of the judge is a key in the state `asked` (the part,
**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
@@ -444,11 +449,11 @@ 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 §11 to §13):
(ADR 0592 §12 to §16):
1. **Order.** Three deliveries, in order: mesh-sdk (the manifest's field types for `objects` and
`retention: history`, and the catalog's check of them); then the controller (it creates the object buckets and
history streams, grants their readers read-only, and raises the condition at 75 percent for every such bucket and
history streams, composes the read-only grants of `readers` on them, and raises the condition at 75 percent for every such bucket and
stream in one place); then mesh-knowledge. An older controller refuses a manifest with these keys through the
catalog's check, so mesh-knowledge cannot be delivered before them. The claude-code module's `judged` event is
delivered beside, and is not needed for the move (§ 5).
@@ -673,14 +678,14 @@ review of ADR 0592's fourth version beside), and the lead ruled on every finding
| Aspect | Found | Answered by |
|---|---|---|
| security | unproven callers could write or take over; an Approve did not bind the sections shown; 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, §14, §16, §17; § 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, §13, §19; § 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 §9, §18, §21; § 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 §21, §27; § 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, §22, §28; § 1.1 (the events table) |
| 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 §12, §13, §25; § 8.1, § 2.2 |
| tools | unnamed objects, missing bodies, form-only versions, asked parts and told marks had no tool | ADR 0592 §26; § 1.1 (the tools 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
§24 and § 2.2 say sections never nest.