Merge pull request 'ADR 0290 and 0291: the knowledge base, and an issue whole in the issue tracker (research 045)' (#306) from decisions/0290-0291-the-knowledge-base-and-issues-whole-in-the-tracker into main

This commit was merged in pull request #306.
This commit is contained in:
2026-10-10 23:26:51 +00:00
11 changed files with 519 additions and 14 deletions
+1
View File
@@ -27,6 +27,7 @@ indistinguishable from one that cannot.
| `live-citation` | a governing document citing a **superseded** record names its replacement in the same paragraph | `01-to-be/README.md` citing ADR 0022 as live guidance |
| `supersession` | if A says it was superseded by B, B says it supersedes A | ADR 0012 never declared that it superseded 0011 |
| `partial-supersession` | a record's `supersedes-in-part:` names records that exist, each carrying a dated *Superseded in part* note naming it under its heading; and every such note is named back by its superseder's field | 11 of 21 partial supersessions had no note on the record they change, and 6 more were said only in prose ([research 045](../../01-RESEARCH/045-a-knowledge-base-the-mesh-writes/00-overview.md)) |
| `clauses` | an accepted record dated from 2026-10-11 writes its `## Decision` section as numbered clauses with a bold lead ([ADR 0290](../../02-DECISIONS/0290-the-knowledge-base-holds-each-records-structure-and-clauses-and-hq-keeps-its-reasoning.md) rule 3) | 226 of 263 decision records wrote their decision as prose, so the knowledge base must draft their clauses |
| `numbering` | the number in the filename is the number in the heading | — |
| `topics` | every record names a topic of the reading order in `02-DECISIONS/README.md` | — |
| *(index.py)* | the reading order is written, every record's topic is one of it, and the README stores no list of records ([ADR 0248](../../02-DECISIONS/0248-the-decision-index-is-generated-when-it-is-read-and-never-stored.md)) | a stored list made every two open decisions conflict ([issue 297](../../04-ISSUES/297-two-open-decisions-always-conflict/00-report.md)); it failed on the README of `main` before the list was removed |
+27
View File
@@ -332,6 +332,32 @@ def check_partial_supersession(failures, records):
)
CLAUSES_FROM = "2026-10-11"
DECISION_SECTION = re.compile(r"^## Decision[^\n]*\n(.*?)(?=^## |\Z)", re.M | re.S)
CLAUSE = re.compile(r"^\d+\. \*\*", re.M)
def check_clauses(failures, records):
"""A record dated from 2026-10-11 writes its decision as numbered clauses (ADR 0290 rule 3).
The knowledge base reads a decision's clauses from the merged text, so a clause's wording is the reviewed
one. 226 of 263 records written before then are prose, and their clauses had to be drafted and confirmed
instead; a new record is not added to them.
"""
for number, record in records.items():
front = record["front"]
if front.get("status") != "accepted" or str(front.get("date", "")) < CLAUSES_FROM:
continue
section = DECISION_SECTION.search(record["text"])
if not section or not CLAUSE.search(section.group(1)):
failures.add(
"clauses",
rel(record["path"]),
"dated from 2026-10-11, and its `## Decision` section has no numbered clause "
"('1. **…**'); write the decision as numbered clauses with a bold lead",
)
def check_topics(failures, records):
"""Every record names a topic of the reading order.
@@ -475,6 +501,7 @@ def main():
check_live_citations(failures, records)
check_supersession_symmetry(failures, records)
check_partial_supersession(failures, records)
check_clauses(failures, records)
check_numbering(failures, records)
check_topics(failures, records)
check_status_against_code(failures)
+16 -5
View File
@@ -566,7 +566,8 @@ asking, never the act. **Decided by** ADR 0152, 0175, 0234, 0259, 0272.
## The record — how this repository works
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 and the issue tracker. **Decided by** ADR 0019, 0080, 0244, 0275, 0284.
**Recorded in** this repository, the issue tracker and the knowledge base. **Decided by** ADR 0019, 0080, 0244, 0275,
0284, 0290, 0291.
- **research effort**, **graduation**, **hand-off**, **playbook** — an investigation in `01-RESEARCH/`;
its closing into a decision and a design; a design given to a code repository; a documented workflow
@@ -574,10 +575,18 @@ Not a domain of the mesh but of the way it is built, listed because its words me
- **decision record** — one numbered file in `02-DECISIONS/`, never rewritten: **superseded** by a later
record, or corrected by a marked **progressive insight**. Always qualified in this repository: bare
*record* has other meanings (see [Homonyms](#homonyms)).
- **clause** — one numbered statement a decision record makes, with its own status (`proposed`, `in force`,
`changed`, `superseded`): a later record may change one clause and leave the rest
([ADR 0290](../02-DECISIONS/0290-the-knowledge-base-holds-each-records-structure-and-clauses-and-hq-keeps-its-reasoning.md)). Not a *rule*, which is a line of
`00-META/how-we-build.md`.
- **knowledge base** — the role that holds the structure of the decision records and research efforts (number,
status, topic, typed links) and each decision's clauses, 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)). This
repository keeps the reasoning. Not **the record**, the literal reader of this repository.
- **design** — a document in `03-DESIGN/`: **as-is** (what runs) or **to-be** (what is being built).
- **issue** — a question about the mesh that someone must answer, numbered and tracked by the issue tracker;
one at the level of design or governance also has a report in `04-ISSUES/` under the same number
([ADR 0275](../02-DECISIONS/0275-the-mesh-numbers-and-tracks-its-issues-in-a-module-of-its-own-and-hq-keeps-only-the-narrative-of-a-design-or-governance-issue.md)).
- **issue** — a question about the mesh that someone must answer, numbered and tracked by the issue tracker, which
holds it whole: its report, diagnosis and resolution included ([ADR 0291](../02-DECISIONS/0291-an-issue-lives-whole-in-the-issue-tracker.md));
the folders in `04-ISSUES/` are imported there and removed.
An **incident** is a past event that taught a rule.
- **issue tracker** — the role that holds the mesh's issues and hands out their numbers: the seat `issue-tracker`,
held by the module `mesh-issues` ([53 — The issue tracker](../03-DESIGN/01-to-be/53-the-issue-tracker.md)).
@@ -633,8 +642,10 @@ renaming one sense, the old sense moves to a *Not:* line and becomes mechanical.
| | the OCI registry | **artifact store** | Change and delivery |
| | a node's own copy of its last declaration | **last declaration** | Placement |
| record | a numbered decision | **decision record** | The record |
| | the knowledge base agents search first | **the record** (the `records` module) | Operator and conversation |
| | the literal reader of this repository, searched first for a symptom | **the record** (the `records` module) | Operator and conversation |
| | the upgrade policy that sends nowhere | `record` | Module |
| knowledge base | the module holding records' structure and clauses | **knowledge base** | The record |
| | the predecessor's operational memory, in older playbooks (issue 430) | none: it no longer exists | — |
| check | a pull request's two statuses | **merge check**, **merge gate**, **repository check** | Change and delivery |
| | a delivery group's verdict | **composed check** | Change and delivery |
| | the controller's own examination | **self-check** | Health and repair |
@@ -1,5 +1,5 @@
---
status: active
status: graduated
initiated: 2026-10-10
touches:
- 02-DECISIONS/0019-how-this-repository-works.md
@@ -13,7 +13,10 @@ touches:
- 00-META/checks/
- 00-META/glossary.md
- the records module's tools
became: []
became:
- 02-DECISIONS/0290-the-knowledge-base-holds-each-records-structure-and-clauses-and-hq-keeps-its-reasoning.md
- 02-DECISIONS/0291-an-issue-lives-whole-in-the-issue-tracker.md
- 03-DESIGN/01-to-be/55-the-knowledge-base.md
---
# 045 — A knowledge base the mesh writes: decisions, guidelines, policies
@@ -8,6 +8,11 @@ reconstructed: false
# 19. How this repository works
> **Superseded in part — 2026-10-11, by [ADR 0291](0291-an-issue-lives-whole-in-the-issue-tracker.md).**
> `04-ISSUES` is no longer a part of this repository: an issue lives whole in the issue tracker, its report,
> diagnosis and resolution included, and the folders are imported there and removed (issue 427). The other three
> folders, and everything else here, stand.
> **Superseded in part — 2026-10-07, by [ADR 0248](0248-the-decision-index-is-generated-when-it-is-read-and-never-stored.md).**
> The paragraph "the index is written, not only generated on demand" no longer holds: a stored list was
> a line every decision pull request added to one file, so two open at once always conflicted (issue
@@ -8,6 +8,12 @@ reconstructed: false
# 275. The mesh numbers and tracks its issues in a module of its own, and hq keeps only the narrative of a design or governance issue
> **Superseded in part — 2026-10-11, by [ADR 0291](0291-an-issue-lives-whole-in-the-issue-tracker.md).**
> Rule 2's folder in hq for a design or governance issue, and the rejection of option 2 (everything in the tracker),
> no longer hold: an issue lives whole in the tracker, its report, diagnosis and resolution included, and hq keeps
> no issue content. The tracker as the truth for number, status and links, the counter, and every other rule stand
> as decided here.
## Context
[Research 042](../01-RESEARCH/042-the-mesh-keeps-its-own-issues/00-overview.md) measured, on 2026-10-10, how this
@@ -0,0 +1,160 @@
---
topic: how we work
status: accepted
date: 2026-10-11
deciders: jochen
reconstructed: false
extends: 02-DECISIONS/0153-the-record-is-read-by-a-module-and-the-console-lists-it.md
---
# 290. The knowledge base holds each record's structure and clauses, and hq keeps its reasoning
## Context
[Research 045](../01-RESEARCH/045-a-knowledge-base-the-mesh-writes/00-overview.md) measured, on 2026-10-11, how an
agent learns what the mesh decided ([findings](../01-RESEARCH/045-a-knowledge-base-the-mesh-writes/01-findings.md)).
The operator gave the purpose the same day: the decisions and research should become shared knowledge that agents
reach through tools, instead of reading markdown files, and a session should no longer have to start in this
repository's folder to know what was decided.
**An answer costs too much.** "What holds about the merge window" took 26 search hits in 11 documents and about 85 KB
of reading, some 21,000 tokens. The decision index, generated by the records module
([ADR 0248](0248-the-decision-index-is-generated-when-it-is-read-and-never-stored.md)), is 77 KB, more than one
tool answer may be. The records module matches a phrase literally and knows nothing of which record still holds
([ADR 0153](0153-the-record-is-read-by-a-module-and-the-console-lists-it.md)).
**What a decision decided is not data.** 226 of 263 decision records write their `Decision` section as prose; 37
write it as numbered statements with a bold lead (198 in all). A later record that changes one statement of an older
one could say so only in prose or by naming the whole record: 21 `supersedes-in-part` links had no reverse, and 6
more records said it in prose alone, until hq pull request 305 declared all of them in both records and added a check.
The older record's status still reads `accepted`, and a reader must read both to know what holds.
**The records change after they are written, and that is reviewed.** 189 of 271 decision records were edited after
the commit that created them, every edit through a pull request with a human approval and the repository's checks.
**Numbers collide.** 16 decision numbers were taken twice by sessions working at once between 2026-09-25 and
2026-10-10. [ADR 0275](0275-the-mesh-numbers-and-tracks-its-issues-in-a-module-of-its-own-and-hq-keeps-only-the-narrative-of-a-design-or-governance-issue.md)
rule 8 already decided that the issue tracker hands out decision and research numbers; it is not built, and issue
424 tracks it.
### Against GENESIS
- **The mesh brokers everything the work needs, knowledge included** ([mission](../00-META/mission.md)): an agent asks
the mesh what holds, by capability, instead of knowing which folder to read.
- **The mesh remembers** ([effect](../00-META/effect.md)): what was decided, and why, is available to whoever needs it
next, on any node, in any folder.
- **Failure must be loud**: a write that would leave the records inconsistent is refused when it is made, not found by
a check afterwards.
## Considered Options
### Where the truth lives
1. **hq stays the truth; a module indexes it and opens pull requests for writes** (the research's option A).
Rejected: it keeps review, but a link about one statement of a decision stays prose, and every inconsistency is
found after it is written, which is the cause of what the research measured.
2. **A store is the truth for everything, and hq is its export** (option B). Rejected: it would rebuild review, line
comments, history and diff for prose, which git and the forge already give, for records that are edited after they
are written as often as these are.
3. **Divided by layer: the store holds each record's structure and its clauses; hq keeps its reasoning.** Adopted.
It is what the research recommended when the operator asked on 2026-10-11 what a clean start would pick, and the operator chose it, and the division ADR 0275
already made for issues, carried down to the statement a decision makes.
### The unit
1. **The record.** Rejected: a later record changes one statement of an older one far more often than the whole.
2. **The clause: one numbered statement a decision makes, with its own status.** Adopted. The research called it a
*rule*; *rule* already names a line of [how-we-build](../00-META/how-we-build.md), and one word names one thing
([ADR 0244](0244-the-mesh-is-described-in-domains-and-one-word-names-one-thing.md)).
### Who confirms the clauses drafted for the 226 prose decisions (the research's open question 1)
1. **The operator, topic by topic.** Rejected: 226 records is days of reading for one person, for statements already
decided and reviewed once.
2. **Nobody: drafted clauses are served as they are.** Rejected: a summary nobody checked would speak as the record's
word, which is the work-that-reports-success-and-did-nothing fault.
3. **A second judgement, independent of the one that drafted them, confirms each record's clauses against its text;
the operator is told per topic and may send any record back.** Adopted, and confirmed by the operator on 2026-10-11. A clause keeps its source, so an answer says
whether a clause was written by the record's author or drafted and confirmed.
## Decision
1. **The knowledge base is a Go module, `mesh-knowledge`, with one instance on the control-node.** It declares and
claims one seat held once for the mesh, `knowledge-base`
([ADR 0126](0126-a-module-declares-its-own-seats.md)). Its first kinds are the **decision record** and the
**research effort**.
2. **The knowledge base is the truth for each record's structure**: its number, kind, title, topic, status, date,
deciders, and its typed links, each held in both directions. **hq is the truth for each record's reasoning**:
context, options weighed, consequences, research documents, as markdown reviewed in pull requests. A record's file
keeps its number in its name and heading.
3. **A decision record is made of clauses.** A clause is one numbered statement of the `Decision` section, with a
status: `in force`, `changed` by a clause of a later record, or `superseded`. A link that is about a clause names
the clause, not only the record. A record written from 2026-10-11 on writes its `Decision` section as numbered
clauses with a bold lead, and the knowledge base reads them from the merged text, so a clause's wording is always
the reviewed one. `records.py` refuses an accepted record dated from 2026-10-11 whose `Decision` section has no
numbered clause.
4. **A record is accepted when its pull request merges.** The knowledge base hears the forge's merge on the bus,
reads the merged record through the records module, and moves the record and its clauses to `accepted` and
`in force`, and each clause it changes or supersedes accordingly. The merge, with its human approval and both
required checks, is the warrant; there is no second approval beside it. A structural write that would change what
an accepted record says, without a pull request, is refused.
5. **Numbers come from the issue tracker's counters**, as ADR 0275 rule 8 decided: the knowledge base's `reserve`
asks the tracker for the next decision or research number, and a reserved number is spent whether or not its
record lands. Until the tracker hands them out, tracked by issue 424, a session takes the highest number on `main`
and on every open branch, plus one, as the shared instruction section *writing-decision-records* says.
6. **An answer is the shape agents need, bounded.** The verbs are `ask` (records and clauses bearing on a question,
ranked, each saying whether it holds or what changed it), `get` (one record: clauses, links both ways, what changed
it since, its reasoning only when asked), `holds` (the clauses in force for a topic or a record), `links`,
`reserve`, `propose`, `import` and `return` (send a record's drafted clauses back to be drafted again). Every answer names the commit of this repository it was read at and stays under
a size bound that `ask`, `get` and `holds` state; the default is 4 KiB.
7. **State is in a database in the store**, provided to the module, not in buckets: `ask` needs ranked full-text
search, which the store's server does and a bucket does not. This narrows nothing in
[ADR 0201](0201-a-module-keeps-its-current-state-in-key-value-buckets-it-declares-and-reaches-through-the-runtime.md):
what the module keeps is a record set to search, not current state to watch. **Every change is an event the
module declares** ([ADR 0041](0041-events-are-a-relationship.md)), carrying what changed and who changed it.
8. **The import reads this repository at a commit and can run again.** It creates what is absent, never overwrites,
never advances a counter, and a second run changes nothing and says so. A record whose clauses are written as
numbered statements gets them as `written`. A prose record gets them `drafted` by the agent's judgement
([ADR 0278](0278-the-issue-tracker-checks-every-new-issue-for-a-duplicate-through-a-judgement-the-agent-makes-without-tools.md)),
then `confirmed` by a second judgement that sees only the record and the drafts. The operator is told per topic and
may send a record back. An answer marks a clause that is drafted or confirmed as such, and never serves a drafted
clause as in force.
9. **The records module stays the literal reader of this repository.** `records_search` for a symptom's literal text
is unchanged; the knowledge base answers what holds. Both reach every session through the mesh MCP server, and a
session needs this repository's folder only to write a record's reasoning.
10. **It is built in slices, each tracked by an issue.** The first is the module for decisions and research, its
import and its answers, tracked by issue 425. The structure's fields then leave hq's frontmatter, kind by kind,
once the import is proven to hold the same values, tracked by issue 426. Designs, playbooks and glossary entries
follow the same way, tracked by issue 428. Meeting notes, drawings and blog posts come last and are not decided
here, tracked by issue 429. The design is
[to-be 55](../03-DESIGN/01-to-be/55-the-knowledge-base.md).
## Consequences
- An agent on any node asks what holds and gets the clauses in force, the records they come from and what changed
them, in one call, instead of reading several records whole.
- A later record that changes one statement of an older one says which, and the older clause's status changes when
the later record merges; the dated note under the older record's heading stays, for a reader who follows a citation.
- Writing a decision changes in one way: its `Decision` section is numbered clauses, from this record on. This record
and [ADR 0291](0291-an-issue-lives-whole-in-the-issue-tracker.md) are written that way.
- Until issue 426 lands, a record's status and links are written in two places: hq's frontmatter, which the hq checks
read, and the knowledge base, which reads them from hq at every merge. hq stays the source until then, so the two
cannot disagree for longer than one merge.
- What got harder: one more module the mesh's own process depends on, and a reader of a decision needs the mesh to be
reachable to learn its clauses' status. The file in hq stays readable alone.
## How it is checked
- **Clauses in a new record** (rule 3): `records.py`, check `clauses`, on every pull request of this repository.
- **What the module does** (rules 2, 4, 6, 7, 8): the module's tests and its live acceptance, listed in
[to-be 55](../03-DESIGN/01-to-be/55-the-knowledge-base.md) §9, built under issue 425.
- **Numbers** (rule 5): the issue tracker's tests of its counters, built under issue 424; until then the
`numbering` and duplicate checks of `records.py` and `cycle.py` find a collision on `main`.
## References
- [Research 045](../01-RESEARCH/045-a-knowledge-base-the-mesh-writes/00-overview.md) and its
[findings](../01-RESEARCH/045-a-knowledge-base-the-mesh-writes/01-findings.md).
- [To-be 55 — The knowledge base](../03-DESIGN/01-to-be/55-the-knowledge-base.md).
- hq pull request 305: every partial supersession declared in both records, and the `partial-supersession` check.
- Issues 424, 425, 426, 428 and 429.
@@ -0,0 +1,107 @@
---
topic: how we work
status: accepted
date: 2026-10-11
deciders: jochen
reconstructed: false
extends: 02-DECISIONS/0275-the-mesh-numbers-and-tracks-its-issues-in-a-module-of-its-own-and-hq-keeps-only-the-narrative-of-a-design-or-governance-issue.md
supersedes-in-part:
- 02-DECISIONS/0019-how-this-repository-works.md
- 02-DECISIONS/0275-the-mesh-numbers-and-tracks-its-issues-in-a-module-of-its-own-and-hq-keeps-only-the-narrative-of-a-design-or-governance-issue.md
---
# 291. An issue lives whole in the issue tracker
## Context
[ADR 0275](0275-the-mesh-numbers-and-tracks-its-issues-in-a-module-of-its-own-and-hq-keeps-only-the-narrative-of-a-design-or-governance-issue.md)
made the issue tracker the truth for every issue's number, status and links, and kept a folder `04-ISSUES/NNN-slug/`
in this repository for a design or governance issue, holding its narrative. It rejected moving everything into the
tracker because "the public record of design questions, which hq exists to be, would be lost".
The operator decided on 2026-10-11, during [research 045](../01-RESEARCH/045-a-knowledge-base-the-mesh-writes/00-overview.md),
that an issue's content belongs in the issue tracker, not in this repository.
**What the folders hold, measured on `main` on 2026-10-11:** 361 folders, 1.9 MB of markdown. A folder's median is
4.4 KB and the largest 25.9 KB, well under the 256 KiB a tracker value may hold
([ADR 0201](0201-a-module-keeps-its-current-state-in-key-value-buckets-it-declares-and-reaches-through-the-runtime.md)
§6). Every folder has a report; 107 also have a diagnosis, 9 a resolution and 1 a progress file. 250 files of this
repository mention a path into `04-ISSUES/` 765 times. The highest folder is 364; every issue opened since is in the
tracker alone.
**Two places for one issue cost the reader.** An agent reads an issue's status and links from the tracker and its
story from a file, by a path the tracker gives. Since ADR 0275 an issue with a folder and one without are read
differently, and which one has a folder is an author's judgement at the moment it was opened.
**What the public record keeps.** A design or governance question that matters ends in a decision record, and decision
records stay here, public, with their reasoning
([ADR 0290](0290-the-knowledge-base-holds-each-records-structure-and-clauses-and-hq-keeps-its-reasoning.md)). What
the public repository loses is the story of how a question was found, not the answer.
### Against GENESIS
- **The mesh owns the truth** ([mission](../00-META/mission.md)): an issue is in one place, written through one
module.
- **The mesh remembers** ([effect](../00-META/effect.md)): an issue's report, diagnosis and resolution stay, in the
tracker, kept as written with their history; the decision it led to stays here.
## Considered Options
### Where an issue's content lives
1. **Keep ADR 0275's split.** Rejected by the operator: one issue in two places, and a reader who must know which.
2. **Everything in the tracker; hq keeps no issue content.** Adopted.
3. **Everything in the tracker, and hq keeps a generated public copy of every resolved design or governance issue.**
Rejected for now: it keeps a second copy, and the public reader who needs the outcome has the decision record. This
was the research's open question 2; the operator confirmed no export on 2026-10-11. An export, if it is wanted
later, is a decision of its own.
## Decision
1. **The issue tracker is the truth for an issue whole**: its number, status, links, notes, and its **documents**,
the report, a diagnosis and a resolution. A document is kept with its author and date, revised in place with its
history kept; a note stays appended and never edited.
2. **This repository keeps no issue content.** Once the tracker holds an issue's documents (issue 427), no new folder
is opened under `04-ISSUES/`, `issue-tracker.open` loses its `record` argument, and playbook 03 and `AGENTS.md`
say so; until then playbook 03 stands as it is written.
3. **The 361 folders are imported into the tracker, and then removed.** The import reads every folder through the
records module at a commit and writes each document onto the issue of the same number, creating what is absent and
never overwriting; a second run changes nothing and says so. The folders are removed in one pull request only
after every issue's documents are compared with its folder at that commit.
4. **A mention of an issue is its number.** The 765 path mentions become "issue N" in the same pull request that
removes the folders, and `records.py` then refuses a new path into `04-ISSUES/`. The knowledge base and the
tracker resolve a number.
5. **No issue content is exported to this repository.** The public record of a design question is the decision
record it led to.
6. **The work is tracked by issue 427**: the documents in the tracker, the import, the mentions rewritten, the folders
removed, and playbook 03 changed to match. Until it lands, every folder stays and playbook 03 is followed as it is
written.
## Consequences
- One issue, one place: an agent reads an issue whole with `issue-tracker.get`.
- The public repository no longer shows how a design question was found. The question's answer, the decision record,
stays public.
- The tracker's values grow from a few hundred bytes to some kilobytes each; the largest measured fits 10 times over.
- Reviewing an issue's diagnosis moves from a pull request to the tracker's history of that document. An issue's
content was never a decision; what is decided still goes through a pull request.
- This supersedes in part ADR 0275 (its rule 2's folder for a design or governance issue, and its rejection of moving
everything into the tracker) and ADR 0019 (the `04-ISSUES` folder as a part of this repository). Each carries a
dated note naming this record.
## How it is checked
- **No new folder under `04-ISSUES/`** (rule 2): from the pull request that removes the folders (issue 427),
`records.py` refuses a new path into `04-ISSUES/` (rule 4), and the issue tracker's `open` no longer offers
`record`.
- **The import loses nothing** (rule 3): before the folders are removed, every issue's documents are compared with its
folder at the import's commit, and the comparison's result is written on issue 427.
- **An issue read whole** (rule 1): the issue tracker's tests of its documents, built under issue 427, as the row
for them in [to-be 53](../03-DESIGN/01-to-be/53-the-issue-tracker.md) §7 says.
## References
- [Research 045](../01-RESEARCH/045-a-knowledge-base-the-mesh-writes/00-overview.md), its
[findings](../01-RESEARCH/045-a-knowledge-base-the-mesh-writes/01-findings.md) §9.
- [To-be 53 — The issue tracker](../03-DESIGN/01-to-be/53-the-issue-tracker.md), whose §3 and §8 this changes.
- Issue 427.
+13 -6
View File
@@ -2,7 +2,7 @@
layer: to-be
status: in-progress
code: [mesh-catalog modules/mesh-issues, mesh-catalog modules/claude-code, mesh-catalog modules/messenger, mesh-sdk]
updated: 2026-10-10
updated: 2026-10-11
decisions:
- 02-DECISIONS/0275-the-mesh-numbers-and-tracks-its-issues-in-a-module-of-its-own-and-hq-keeps-only-the-narrative-of-a-design-or-governance-issue.md
- 02-DECISIONS/0278-the-issue-tracker-checks-every-new-issue-for-a-duplicate-through-a-judgement-the-agent-makes-without-tools.md
@@ -10,6 +10,7 @@ decisions:
- 02-DECISIONS/0281-an-issue-knows-whether-its-fix-is-delivered-and-a-fix-stuck-after-its-merge-is-loud.md
- 02-DECISIONS/0284-an-agent-moves-an-issue-to-fixing-under-its-own-name-and-an-issue-is-resolved-only-from-fixing.md
- 02-DECISIONS/0285-an-issue-tracks-a-condition-by-its-key-and-every-message-about-the-condition-says-the-issue-and-where-it-stands.md
- 02-DECISIONS/0291-an-issue-lives-whole-in-the-issue-tracker.md
---
# 53 — The issue tracker
@@ -17,10 +18,11 @@ decisions:
**The mesh keeps its issues in a module of its own, `mesh-issues`, one instance on the control-node, holding the seat
`issue-tracker`. It hands out every
issue's number from one counter that two callers can never both advance, holds each issue's status and links, refuses
a change the development cycle would refuse, and announces every change on the bus. hq keeps a folder only for a design
or governance issue, under the tracker's number.**
a change the development cycle would refuse, and announces every change on the bus. An issue lives whole in the tracker, its report, diagnosis and
resolution included; hq keeps no issue content.**
([ADR 0275](../../02-DECISIONS/0275-the-mesh-numbers-and-tracks-its-issues-in-a-module-of-its-own-and-hq-keeps-only-the-narrative-of-a-design-or-governance-issue.md),
from [research 042](../../01-RESEARCH/042-the-mesh-keeps-its-own-issues/00-overview.md).)
from [research 042](../../01-RESEARCH/042-the-mesh-keeps-its-own-issues/00-overview.md);
[ADR 0291](../../02-DECISIONS/0291-an-issue-lives-whole-in-the-issue-tracker.md) for an issue whole in the tracker.)
An **issue** is a question about the mesh that someone must answer. The **issue tracker** is the module that holds
issues, named `mesh-issues`; the seat it holds is `issue-tracker`. The glossary holds both words.
@@ -98,7 +100,10 @@ the issue's worker, who works on it, as the worker states it
- `notes`: appended, each with who wrote it, when, and the text;
- `links`, typed: to another issue (`duplicates`, `relates-to`), a pull request, a decision record, a design, a
condition key (the issue **tracks** that condition, § 12) or a delivery;
- `record`: the path of the issue's folder in hq, when it has one;
- `documents`: the report, and a diagnosis and a resolution when written, each with its author and date, revised in
place with its history kept ([ADR 0291](../../02-DECISIONS/0291-an-issue-lives-whole-in-the-issue-tracker.md));
- `record`: the path of the issue's folder in hq, while one exists; none is opened any more, and the folders are
imported into `documents` and removed (§ 8, slice 6);
- `severity` (critical, major, minor), `needs` (none, decision, operator), `suggested-owner`, `delivered` (the
pull requests of `fixed-by` the delivery said are delivered) and `triage` (the triage's judgement as made), and the link
kind `depends-on` (§ 10).
@@ -194,6 +199,7 @@ worker; one that stops moves the issue on, or is stale a day later.
| An issue's delivery line follows the delivery; ready to verify once; a stuck fix said once and cleared | the rows of [ADR 0281](../../02-DECISIONS/0281-an-issue-knows-whether-its-fix-is-delivered-and-a-fix-stuck-after-its-merge-is-loud.md)'s "How it is checked" |
| The triage fills only what is empty, links no cycle, and an issue is ready only when its dependencies' fixes are delivered | the rows of [ADR 0279](../../02-DECISIONS/0279-the-issue-tracker-triages-every-new-issue-and-an-issue-is-ready-when-what-it-depends-on-is-delivered.md)'s "How it is checked" |
| The duplicate check acts only on a clear duplicate, never on an older or a resolved issue, once per issue, loudly when it cannot judge | the rows of [ADR 0278](../../02-DECISIONS/0278-the-issue-tracker-checks-every-new-issue-for-a-duplicate-through-a-judgement-the-agent-makes-without-tools.md)'s "How it is checked" |
| An issue's documents are kept whole, with author, date and history, and the import of hq's folders loses nothing ([ADR 0291](../../02-DECISIONS/0291-an-issue-lives-whole-in-the-issue-tracker.md)) | the module's tests of `documents` and of the import, one case per document kind; the comparison of every issue with its folder before the folders are removed, written on issue 427. Tracked by issue 427 |
| Live | on the mesh, an agent calls `issue-tracker.open` and gets the next number; two sessions on two nodes open at the same moment and get two different numbers; `list` shows both |
## 8. The slices after the first
@@ -201,10 +207,11 @@ worker; one that stops moves the issue on, or is stale a day later.
| Slice | Delivers | Done when the operator sees |
|---|---|---|
| 1 | The `mesh-issues` module: `open`, `get`, `list`, `move`, `note`, `link`; a counter by compare-and-set in the SDK; playbook 03 takes its numbers from it | an agent opens an issue and gets the next number; two agents opening at the same moment never get the same one |
| 2 | Decision and research numbers from the tracker, each its own key in `counters`; a number is reserved when a branch starts | two agents writing a decision at once get two numbers, and no hq pull request is renumbered |
| 2 | Decision and research numbers from the tracker, each its own key in `counters`; a number is reserved when a branch starts; the knowledge base's `reserve` asks for them ([to-be 55](55-the-knowledge-base.md) § 5). Tracked by issue 424 | two agents writing a decision at once get two numbers, and no hq pull request is renumbered |
| 3 | `import`: every issue folder in hq read through the records module and written as an issue with its own number, idempotent and showing what it would change before it does (built and run 2026-10-10; one pull request changed playbook 03 and `cycle.py` and added dated notes to ADR 0019, 0080 and 0275, and once every folder was checked against the tracker a second removed the status keys from every report, as § 3 says); then the four consolidation passes of research 042 (fixed, duplicates, owners, grouping; the duplicates pass is the duplicate check's `groom`, § 9, judged rather than by shared words), each a list confirmed before it is applied through `move`, `link` and `note`, and an audit of `fixed-by` against the forge | the not-closed list comes from one call and shrinks by what was already fixed or duplicated, each with its reason |
| 4 | Conditions and pull requests linked: an issue links a condition by its key, and every message about the condition says the issue and where it stands (§ 12; the controller does not show the link, [ADR 0285](../../02-DECISIONS/0285-an-issue-tracks-a-condition-by-its-key-and-every-message-about-the-condition-says-the-issue-and-where-it-stands.md)); a merged pull request notes every issue it names; after a week of that, the controller opens one issue per `healer-wanted` condition key by itself | a condition's message names its issue, and a merge notes the issues it names |
| 5 | A read-only view onto the `issues` bucket over the bus | the operator reads issues in a browser without an agent |
| 6 | An issue whole ([ADR 0291](../../02-DECISIONS/0291-an-issue-lives-whole-in-the-issue-tracker.md)): the `documents` of § 3; an import of the 361 folders of hq into them, repeatable, keeping each issue's number and dates; once every issue is compared with its folder, one pull request rewrites the 765 path mentions in hq to "issue N", removes the folders, makes `records.py` refuse a new path into `04-ISSUES/`, and changes playbook 03; `open` loses `record`. Tracked by issue 427 | `issue-tracker.get` answers an issue's report and diagnosis, and hq has no `04-ISSUES/` |
Once slice 1 runs, each slice after it is opened as an issue in the tracker, so the tracker carries its own work.
+177
View File
@@ -0,0 +1,177 @@
---
layer: to-be
status: designed
code: []
updated: 2026-10-11
decisions:
- 02-DECISIONS/0290-the-knowledge-base-holds-each-records-structure-and-clauses-and-hq-keeps-its-reasoning.md
---
# 55 — The knowledge base
**The mesh keeps the structure of its decision records and research efforts in a module of its own,
`mesh-knowledge`, one instance on the control-node, holding the seat `knowledge-base`. It holds each record's number,
status, topic and typed links, and each decision's clauses with their own status, and answers what holds in one
bounded call. This repository keeps each record's reasoning, reviewed in pull requests; a record is accepted when its
pull request merges.**
([ADR 0290](../../02-DECISIONS/0290-the-knowledge-base-holds-each-records-structure-and-clauses-and-hq-keeps-its-reasoning.md),
from [research 045](../../01-RESEARCH/045-a-knowledge-base-the-mesh-writes/00-overview.md).)
A **clause** is one numbered statement a decision record makes, with its own status. The **knowledge base** is the
module that holds records' structure and clauses; 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 the
commit of this repository it was read at.
This document designs the first slice in detail, tracked by issue 425. The slices after it are in
[§ 8](#8-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, clauses, links, sources; ranked full-text search
├ verbs ask, get, holds, links, reserve, propose, import, return
│ served by the control-node's tool runner
├ events imported, proposed, accepted, changed, drafted, confirmed, returned
└ consumes the forge's merge event for hq: read the merged records (§ 4)
records (any node) records_read at a commit: the text of a record
mesh-issues (control-node) the counters for decision and research numbers (§ 5)
claude-code (same node) claude_code_judge: draft and confirm clauses (§ 6)
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 and holds no checkout: it reads this repository through the records
module's `records_read` and `records_list` at the commit a merge names, 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`, and
the `path` of its file in this repository;
- `commit`: the commit of this repository the record was last read at;
- for a decision, its **clauses** (§ 3);
- its **links**, typed, each stored once and read from both ends:
| 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 |
| `corrects` | a dated progressive insight → the record it stands in |
| `cites` | any record → any record or issue it links in its text |
`changes` at the level of a clause is new. A merged record that declares `supersedes-in-part` gives a `changes` link
to the older record; which of the older record's clauses it changes is read from the newer record's text by the
judgement of § 6 and held as `drafted` until confirmed, like a drafted clause.
## 3. Clauses
A clause holds its record's number and its own (`0276 §4`), its wording, a `status` and a `source`:
| Status | Meaning |
|---|---|
| `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 |
| `proposed` | its record's pull request is not merged |
| Source | Meaning |
|---|---|
| `written` | read from a `Decision` section written as numbered clauses with a bold lead |
| `drafted` | written by the judgement from a prose `Decision` section; never served as in force |
| `confirmed` | drafted, then confirmed by a second, independent judgement (§ 6) |
From 2026-10-11 every accepted record writes its clauses (`records.py`, check `clauses`). The 226 records written
before then in prose get theirs drafted and confirmed at the import.
## 4. Acceptance follows the merge
The knowledge base consumes the forge's merge event for this repository. For each decision record or research
overview the merge adds or changes, it reads the file at the merge's commit through the records module and:
1. writes or updates the record's structure from its frontmatter, and its clauses from its `Decision` section;
2. moves a record from `proposed` to `accepted` when its frontmatter says `accepted`;
3. for each `supersedes` and `supersedes-in-part` the record declares, moves the older record or the drafted target
clauses to `superseded` or `changed`, and emits `changed` for each;
4. emits `accepted`, naming the commit.
A merge that changes nothing the knowledge base holds changes nothing. A record read that fails is retried, and a merge
whose records could not be read after the retries raises a condition naming the commit: a merge the knowledge base did
not take is loud, not silent.
`propose` writes a new record's structure as `proposed` before its pull request exists, with the number `reserve`
gave; nothing proposed is ever served as in force. A structural change to an accepted record without a merge is
refused, with the reason that what an accepted record says changes only through a pull request.
## 5. Numbers
`reserve` takes a kind and asks the issue tracker for the next number of that sequence (issue 424, ADR 0275 rule 8),
and answers it. The number is spent: a record that never lands leaves it as a gap, and the knowledge base keeps who
reserved it and when, as `withdrawn` once its branch is closed unmerged. The knowledge base keeps no counter.
## 6. Drafting and confirming clauses
For a decision whose `Decision` section is prose, the import asks the agent's judgement
([ADR 0278](../../02-DECISIONS/0278-the-issue-tracker-checks-every-new-issue-for-a-duplicate-through-a-judgement-the-agent-makes-without-tools.md))
for its clauses: the record's `Decision` section in, numbered statements out, each one sentence, each quoting nothing
the section does not say. They are stored `drafted`.
A second judgement, made in a separate call that sees only the record's text and the drafts, answers per clause
whether the record says exactly that. A clause it accepts becomes `confirmed`; one it rejects stays `drafted` with
the reason. When every record of a topic has been through both, the operator is told once per topic, with the count
confirmed and the count still drafted, through the router. `return` with a record's number and a reason sends a
record back to be drafted again.
## 7. Answers
| Verb | Takes | Answers |
|---|---|---|
| `ask` | a question in words; optionally a kind, a topic, a size bound | records and clauses bearing on it, ranked by the store's full-text search: number, kind, status, title, and each clause with its status and what changed it |
| `get` | a number and kind; optionally `reasoning` | one record: its clauses, its links both ways, what changed it since; the reasoning's text only with `reasoning` |
| `holds` | a topic or a record | the clauses in force, each with its record and the `changes` that led to its present wording |
| `links` | a number and kind | its typed links, both directions |
| `reserve` | a kind | the next number (§ 5) |
| `propose` | a kind, a number from `reserve`, a title, a topic, and its links | the structure written as `proposed` |
| `import` | optionally `dry-run`, a commit | every decision and research effort at that commit, as § 4 reads one, with prose decisions drafted (§ 6); create-if-absent, never an overwrite; run again, changes nothing and says so |
| `return` | a number and a reason | sends a record's drafted or confirmed clauses back to be drafted again |
Every answer names the commit it was read at. `ask`, `get` and `holds` answer at most 4 KiB unless a larger bound is
given, and say when they cut and how to ask for the rest. A clause that is `drafted` is marked so in every answer and
never listed under `holds`.
## 8. The slices after the first
| Slice | Delivers | Done when the operator sees | Tracked by |
|---|---|---|---|
| 1 | this document: the module, the import, the clauses, the answers, acceptance on merge | the acceptance sentence above, live | issue 425 |
| 2 | the frontmatter fields the knowledge base holds leave this repository, kind by kind, and the per-record rules of `records.py` and `cycle.py` move into its write verbs | a decision record's file carries no status or links, and the hq checks still pass | 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 slice 1 needs for `reserve`, are the tracker's own slice, tracked by issue 424.
## 9. How it is checked
| Rule | Checked by |
|---|---|
| A record merged in hq is held, with its clauses, at the merge's commit | the module's test over a real bus and a fixture repository: a merge event, then `get` names that commit; and live, the first decision merged after the module is delivered |
| A merge the knowledge base could not take is loud | the module's test: records unreadable after the retries raise a condition naming the commit |
| The import changes nothing on a second run | the module's test: two imports at one commit, the second reports nothing created |
| 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 structural change to an accepted record without a merge is refused | the module's test, one case per field |
| An answer stays within its bound and names its commit | 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 new record writes its clauses | `records.py`, check `clauses`, on every hq pull request |
| Live | the acceptance sentence, asked from a session outside this repository |
## What it leaves out
Semantic or vector search; a browser view; writing a record's reasoning (that stays a pull request in this
repository); and the rules of how-we-build, which are not decision records. Each needs a decision of its own.
+2 -1
View File
@@ -52,8 +52,9 @@ document is written and this one's status becomes `implemented`.
| [`50-split-dns-on-a-machine-with-a-vpn-client.md`](50-split-dns-on-a-machine-with-a-vpn-client.md) | **In progress.** A machine whose VPN client pushes servers of its own runs a resolver of its own, on a node seat, that routes the VPN's domains over its link and every other name to the mesh's resolvers; the VPN client's module carries the adapter | [ADR 0247](../../02-DECISIONS/0247-a-machine-with-a-vpn-client-routes-names-by-domain-through-a-resolver-of-its-own.md), [ADR 0223](../../02-DECISIONS/0223-the-mesh-has-two-resolvers-and-a-machine-lists-only-them.md) |
| [`51-the-registries-keep-what-is-named.md`](51-the-registries-keep-what-is-named.md) | **In progress.** The artifact store, the package registry and each machine's container runtime get tools that say what they hold, what names each thing and what could go; every removal is a dry run unless a person asks and says why, and what decides is always a record | [ADR 0251](../../02-DECISIONS/0251-the-registries-say-what-they-hold-and-keep-what-is-named-and-a-machine-keeps-the-image-it-runs-and-the-one-before.md), [ADR 0189](../../02-DECISIONS/0189-the-store-keeps-what-the-records-name.md) |
| [`52-shares-and-mounts.md`](52-shares-and-mounts.md) | **In progress.** A node shares one folder at a time through `nfs-server`, which provides it as a provision to the private network only, mapping every client to the folder's owner; a node that asks mounts it through `mounts`, a mount unit and an automount per share, so nothing mounts or fails at boot; a device that comes and goes is an occasional source whose absence is a state | [ADR 0263](../../02-DECISIONS/0263-a-share-is-a-provision-a-node-mounts-it-on-demand-and-only-nodes-that-ask-get-it.md), [ADR 0051](../../02-DECISIONS/0051-shared-data-is-the-operators.md), [ADR 0092](../../02-DECISIONS/0092-an-operator-delivers-a-pair-credential.md) |
| [`53-the-issue-tracker.md`](53-the-issue-tracker.md) | **Designed.** The mesh keeps its issues in one module, `issues`, on the control-node: every number from one counter advanced by compare-and-set, the cycle's issue rules refused at the write, an event per change; hq keeps a folder only for a design or governance issue | [ADR 0275](../../02-DECISIONS/0275-the-mesh-numbers-and-tracks-its-issues-in-a-module-of-its-own-and-hq-keeps-only-the-narrative-of-a-design-or-governance-issue.md) |
| [`53-the-issue-tracker.md`](53-the-issue-tracker.md) | **Designed.** The mesh keeps its issues in one module, `issues`, on the control-node: every number from one counter advanced by compare-and-set, the cycle's issue rules refused at the write, an event per change; an issue lives whole in the tracker, hq keeping no issue content ([ADR 0291](../../02-DECISIONS/0291-an-issue-lives-whole-in-the-issue-tracker.md)) | [ADR 0275](../../02-DECISIONS/0275-the-mesh-numbers-and-tracks-its-issues-in-a-module-of-its-own-and-hq-keeps-only-the-narrative-of-a-design-or-governance-issue.md), [ADR 0291](../../02-DECISIONS/0291-an-issue-lives-whole-in-the-issue-tracker.md) |
| [`54-the-controllers-records-on-the-bus.md`](54-the-controllers-records-on-the-bus.md) | **Designed.** The controller keeps five record mirrors on the bus (nodes, assignments, walks, builds, settings): each a bucket written from its store only when the record changed, with one event per write, and audited against the store every self-check run, a difference raised as `mirror-drifted` before the healer H6 repairs it | [ADR 0289](../../02-DECISIONS/0289-the-controller-mirrors-its-records-into-buckets-written-only-on-a-change-and-audited-against-its-store.md) |
| [`55-the-knowledge-base.md`](55-the-knowledge-base.md) | **Designed.** The mesh keeps its decision records' and research efforts' structure in one module, `mesh-knowledge`, on the control-node: number, status, topic, typed links both ways, and each decision's clauses with their own status; a record is accepted when its pull request merges; agents ask what holds in one bounded call, and hq keeps the reasoning | [ADR 0290](../../02-DECISIONS/0290-the-knowledge-base-holds-each-records-structure-and-clauses-and-hq-keeps-its-reasoning.md) |
## Not yet written