From 55637ecb95520c477a2790d74ab30e1b9e12a066 Mon Sep 17 00:00:00 2001 From: jochen Date: Wed, 7 Oct 2026 23:38:34 +0200 Subject: [PATCH 1/2] ADR 0248: the decision index is generated when it is read, so two open decisions no longer conflict --- .claude/skills/hq-status/SKILL.md | 12 +- 00-META/checks/README.md | 8 +- 00-META/checks/index.py | 101 ++++--- 00-META/checks/records.py | 11 +- 00-META/process/00-overview.md | 7 +- .../0019-how-this-repository-works.md | 7 + ...erated-when-it-is-read-and-never-stored.md | 82 +++++ 02-DECISIONS/README.md | 283 ++---------------- .../00-report.md | 2 +- .../01-diagnosis.md | 13 + 10 files changed, 203 insertions(+), 323 deletions(-) create mode 100644 02-DECISIONS/0248-the-decision-index-is-generated-when-it-is-read-and-never-stored.md diff --git a/.claude/skills/hq-status/SKILL.md b/.claude/skills/hq-status/SKILL.md index d02e877a..af231f84 100644 --- a/.claude/skills/hq-status/SKILL.md +++ b/.claude/skills/hq-status/SKILL.md @@ -23,8 +23,12 @@ generated on demand and never written back to disk. 1. Read the frontmatter block from every file above — a grep across each tree is enough, no need to load bodies. 2. Render what was asked as Markdown tables. Group and sort sensibly. The **ADR index** is one - of these views: records ordered by number, with title, date and status, and reconstructed - ones marked. + of these views, and it is the only place the list of records is shown + ([ADR 0248](../../../02-DECISIONS/0248-the-decision-index-is-generated-when-it-is-read-and-never-stored.md)): + run `python3 00-META/checks/index.py --print` for the records grouped by topic in the reading + order `02-DECISIONS/README.md` writes, each with its status when not `accepted`. Asked for it by + number instead, render records ordered by number, with title, date and status, and reconstructed + ones marked. Off this checkout, the records module's `records_decisions` answers the same list. 3. Flag anything inconsistent at the end, as flags — do not silently correct the render: - a `graduated` or `abandoned` effort with an empty `became:` - an `implemented` design with an empty `code:` @@ -37,5 +41,7 @@ generated on demand and never written back to disk. - **Do not write a status file.** Central status files are explicitly rejected. The view is always generated, always ephemeral. This includes the ADR index — the hand-written one had - already drifted after a single addition, which is why it was removed. + already drifted after a single addition, which is why it was removed, and the generated one + written into `02-DECISIONS/README.md` made every two open decisions conflict (issue 297), which + is why that went too. `index.py` fails if the list is stored there again. - Do not infer status from prose. Trust only the frontmatter; if it is wrong, flag it. diff --git a/00-META/checks/README.md b/00-META/checks/README.md index e0562f7d..d10dde39 100644 --- a/00-META/checks/README.md +++ b/00-META/checks/README.md @@ -2,8 +2,8 @@ ``` python3 00-META/checks/records.py structure: links, citations, supersession, topics -python3 00-META/checks/index.py the reading order in 02-DECISIONS/README.md is current -python3 00-META/checks/index.py --write regenerate it +python3 00-META/checks/index.py every record's topic is in the reading order, and no list of records is stored +python3 00-META/checks/index.py --print the records, by topic, in reading order (generated, never written) python3 00-META/checks/words.py the glossary's retired words are not used, and no word is defined twice python3 00-META/checks/words.py --list tools the words the catalogue's copy must list ``` @@ -26,8 +26,8 @@ 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 | | `numbering` | the number in the filename is the number in the heading | — | -| `topics` | every record names a topic the index knows | — | -| *(index.py)* | the written reading order matches what the records say | — | +| `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 | | `status-vs-code` | a to-be document naming specific code is not still `designed` | **ten documents**, several with a *What was built* section, describing lab-proven code | ## What is deliberately not checked diff --git a/00-META/checks/index.py b/00-META/checks/index.py index 22a2f808..2b7bb586 100644 --- a/00-META/checks/index.py +++ b/00-META/checks/index.py @@ -1,16 +1,18 @@ #!/usr/bin/env python3 -"""Generate the decision index, and check the written one still matches. +"""The reading order of the decision records: its topics are written, its list is generated. A number identifies a record and never changes, so the folder listing is creation order rather -than reading order. The index is what carries the path — and it is written rather than only -generated on demand, because a reader on a forge sees the folder and not a command. +than reading order. The reading order is the six topics `02-DECISIONS/README.md` writes down, in +order, and each record's own `topic:`. The list of records under each topic is generated from +that frontmatter when somebody reads it, and never stored (ADR 0248, hq issue 297): a stored +list was a line every decision pull request added to one file, so any two open at once conflicted. -The objection to a written index is that it drifts. That objection is answered by checking it -rather than by refusing to write one, which is `how-we-build` §5: a rule states how it is -checked. + python3 00-META/checks/index.py check + python3 00-META/checks/index.py --print print the list, by topic, in reading order - python3 00-META/checks/index.py --write regenerate it - python3 00-META/checks/index.py fail if it is stale +It fails when the README's reading order is missing, when a record's topic is not one of its +topics, and when the README stores a list of records again — the index markers, or a line of +the generated list. The last one is what keeps the conflict from coming back. """ import glob @@ -20,20 +22,26 @@ import re import sys README = "02-DECISIONS/README.md" -START = "" -END = "" -# The reading order. Topics a record may belong to, in the order somebody would learn the system. -TOPICS = [ - ("the mesh", "What the mesh is"), - ("the tiers", "Its tiers, from the bottom up"), - ("what runs on it", "What runs on them, and how it gets there"), - ("building it", "How it is built"), - ("checking it", "How it is checked"), - ("how we work", "How we work"), +# A topic in the README's reading order, e.g. +# 1. **What the mesh is** — `topic: the mesh`. What it is for, ... +# The records module's `records_decisions` reads the same line, so the form is a contract. +TOPIC_LINE = re.compile(r"^\d+\.\s+\*\*(?P