ADR 0081: a decision nothing cites is not yet in the chain
Decisions were the one link the cycle checks skipped, and measuring found 19 of 70 records orphaned — the credential flow and the module-runtime cluster among them, which is how a stale premise about a settled decision survived in working memory. cycle.py now refuses an accepted record nothing cites; the 19 got true homes (design frontmatter, the playbook that implements 0021, META for the process records). The overview names the practice: spec-driven development with provenance. https://claude.ai/code/session_01D6qtiYU3P9jk3pnAXyAFyx
This commit is contained in:
@@ -17,6 +17,10 @@ What is enforced:
|
||||
"nothing, the capability existed" is an answer).
|
||||
research a known `status:`; a `graduated` overview says what it `became:`, and every
|
||||
target it names exists.
|
||||
decisions every accepted record is REACHABLE from the cycle: cited by a design doc's
|
||||
frontmatter, a research overview, an issue report, a 00-META document, or another
|
||||
record's extends/supersedes chain. A decision nothing points at is one nobody will
|
||||
find by following pointers -- which is how records go stale in people's heads.
|
||||
|
||||
Deliberately NOT enforced: `resolved` issues may leave `located-in` empty (a symptom that
|
||||
turned out not to be a defect has no owner), and as-is docs need no decisions (they
|
||||
@@ -135,10 +139,42 @@ def main():
|
||||
if not os.path.exists(os.path.join(ROOT, target)):
|
||||
bad(path, "became names %s, which does not exist" % target)
|
||||
|
||||
# ---- decisions -------------------------------------------------------------------
|
||||
records = {}
|
||||
for path in sorted(glob.glob(os.path.join(ROOT, "02-DECISIONS", "[0-9]*.md"))):
|
||||
front = frontmatter(path)
|
||||
records[os.path.basename(path)] = (path, (front or {}).get("status"))
|
||||
|
||||
cited = set()
|
||||
sources = (glob.glob(os.path.join(ROOT, "03-DESIGN", "*", "*.md"))
|
||||
+ glob.glob(os.path.join(ROOT, "01-RESEARCH", "*", "00-overview.md"))
|
||||
+ glob.glob(os.path.join(ROOT, "04-ISSUES", "*", "00-report.md"))
|
||||
+ glob.glob(os.path.join(ROOT, "00-META", "**", "*.md"), recursive=True))
|
||||
for path in sources:
|
||||
text = open(path, encoding="utf-8").read()
|
||||
if os.sep + "03-DESIGN" + os.sep in path:
|
||||
# A design doc's governing citations live in frontmatter; a prose mention is
|
||||
# commentary, not a home.
|
||||
m = re.match(r"^---\n(.*?)\n---", text, re.S)
|
||||
text = m.group(1) if m else ""
|
||||
for m in re.finditer(r"([0-9]{4}-[^\s\)\],#]+\.md)", text):
|
||||
cited.add(os.path.basename(m.group(1)))
|
||||
for name in records:
|
||||
front = frontmatter(records[name][0]) or {}
|
||||
for key in ("extends", "supersedes", "superseded-by"):
|
||||
v = front.get(key)
|
||||
if isinstance(v, str) and v:
|
||||
cited.add(os.path.basename(v))
|
||||
for name, (path, status) in records.items():
|
||||
if status == "accepted" and name not in cited:
|
||||
bad(path, "an accepted decision nothing in the cycle cites -- give it a home in a "
|
||||
"design doc's decisions:, a research became:, an issue, or 00-META")
|
||||
|
||||
checked = (
|
||||
len(glob.glob(os.path.join(ROOT, "03-DESIGN", "0*", "*.md")))
|
||||
+ len(glob.glob(os.path.join(ROOT, "04-ISSUES", "*", "00-report.md")))
|
||||
+ len(glob.glob(os.path.join(ROOT, "01-RESEARCH", "*", "00-overview.md")))
|
||||
+ len(records)
|
||||
)
|
||||
if failures:
|
||||
print("cycle: %d document(s) break the development cycle:" % len(failures))
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
# Process — overview
|
||||
|
||||
How work moves through HQ, and who may do what. Every other document in this folder is a
|
||||
How work moves through HQ, and who may do what. In industry terms this is **spec-driven
|
||||
development, with provenance**: the decision is the why, the design doc is the spec, `code:`
|
||||
names the implementation, and the lab beds are the conformance tests — and unlike the common
|
||||
form, the chain itself is checked ([ADR 0080](../../02-DECISIONS/0080-the-development-cycle-is-checked.md),
|
||||
[0081](../../02-DECISIONS/0081-a-decision-nothing-cites-is-not-yet-in-the-chain.md)). Every other document in this folder is a
|
||||
playbook: trigger, who runs it, steps, outputs. Engineers and agents follow the same
|
||||
playbooks; agents must not act outside them.
|
||||
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
# Playbook 05 — Constitution sync
|
||||
|
||||
Implements [ADR 0021](../../02-DECISIONS/0021-hq-is-the-source-of-the-constitution.md): HQ is
|
||||
the source of the constitution, and the knowledge-base page is derived, never edited.
|
||||
|
||||
**Trigger.** [`how-we-build.md`](../how-we-build.md) changed a rule that the mesh enforces at
|
||||
runtime.
|
||||
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
---
|
||||
topic: how we work
|
||||
status: accepted
|
||||
date: 2026-09-17
|
||||
deciders: jochen
|
||||
reconstructed: false
|
||||
extends: 0080-the-development-cycle-is-checked.md
|
||||
---
|
||||
|
||||
# 81. A decision nothing cites is not yet in the chain
|
||||
|
||||
## Context
|
||||
|
||||
[ADR 0080](0080-the-development-cycle-is-checked.md) made the development cycle checked — but its
|
||||
checks covered designs, issues and research, not the decisions themselves. Measuring showed why
|
||||
that matters: 19 of 70 records were cited by nothing — no design doc's `decisions:`, no research
|
||||
`became:`, no issue, no other record. Among them sat load-bearing decisions (the credential flow,
|
||||
the module-runtime cluster), and the cost had already been paid once in practice: a stale premise
|
||||
about an orphaned decision survived in working memory precisely because no pointer led to the
|
||||
record that had settled it.
|
||||
|
||||
## Decision
|
||||
|
||||
Every **accepted** decision must be reachable from the cycle: cited by a design doc's frontmatter
|
||||
(`decisions:` — a governing citation, not a prose mention), a research overview, an issue report,
|
||||
a `00-META` document, or another record's `extends`/`supersedes` chain.
|
||||
[`cycle.py`](../00-META/checks/cycle.py) refuses orphans. Proposed records are exempt — a record
|
||||
under consideration has no home yet — and superseded records are reachable through their
|
||||
supersession chain by construction.
|
||||
|
||||
The 19 orphans were given true homes in the same change: the module-runtime cluster
|
||||
(0044–0049, 0053–0055) into the connectivity, controller, protocol, writing and model-access
|
||||
designs; the build decisions (0072, 0076) into the building design; 0021 into the playbook that
|
||||
implements it; the process records were already reachable once `00-META` counted as a source.
|
||||
|
||||
Taken together with 0080, the practice has a name the industry will recognise:
|
||||
**spec-driven development, with provenance** — a decision is the *why*, the design doc is the
|
||||
spec, `code:` names the implementation, and the lab beds are the conformance tests. What the
|
||||
common form leaves implicit, the cycle makes checked: the spec itself must trace to a decision,
|
||||
and the decision must be findable from the work it governs.
|
||||
|
||||
## Consequences
|
||||
|
||||
Following pointers now reaches every accepted decision, so a cleared session (or a person) can
|
||||
trust the frontmatter graph as the whole map. The check is reachability, not truth: a citation
|
||||
placed wrongly still lies, and reading remains the job.
|
||||
|
||||
## References
|
||||
|
||||
- [ADR 0080](0080-the-development-cycle-is-checked.md) — the cycle this completes.
|
||||
- [`00-META/process/00-overview.md`](../00-META/process/00-overview.md) — the flow.
|
||||
@@ -166,5 +166,6 @@ python3 00-META/checks/index.py fail if stale
|
||||
- **0032** — [The local account owns the mesh; a surface delegates to a module](0032-the-local-account-owns-the-mesh.md) *(superseded)*
|
||||
- **0034** — [The local account owns the mesh, and a web application's login is not that](0034-the-local-account-owns-the-mesh.md)
|
||||
- **0080** — [The development cycle is checked, not trusted](0080-the-development-cycle-is-checked.md)
|
||||
- **0081** — [A decision nothing cites is not yet in the chain](0081-a-decision-nothing-cites-is-not-yet-in-the-chain.md)
|
||||
|
||||
<!-- index:end -->
|
||||
|
||||
@@ -10,6 +10,7 @@ decisions:
|
||||
- 02-DECISIONS/0006-the-substrate-and-the-control-plane.md
|
||||
- 02-DECISIONS/0008-a-context-owns-its-store.md
|
||||
- 02-DECISIONS/0019-how-this-repository-works.md
|
||||
- 02-DECISIONS/0046-a-module-configuration-is-its-assignments-not-its-manifest.md
|
||||
---
|
||||
|
||||
# The controller
|
||||
|
||||
@@ -17,6 +17,9 @@ decisions:
|
||||
- 02-DECISIONS/0007-connectivity.md
|
||||
- 02-DECISIONS/0006-the-substrate-and-the-control-plane.md
|
||||
- 02-DECISIONS/0066-public-routing-is-name-agnostic.md
|
||||
- 02-DECISIONS/0029-a-network-is-a-shape-because-an-action-cannot-be-undone.md
|
||||
- 02-DECISIONS/0044-a-public-name-is-provisioned-like-any-capability.md
|
||||
- 02-DECISIONS/0045-a-machine-firewall-is-the-sum-of-what-it-listens-on.md
|
||||
---
|
||||
|
||||
# Connectivity
|
||||
|
||||
@@ -8,6 +8,8 @@ updated: 2026-09-05
|
||||
decisions:
|
||||
- 02-DECISIONS/0024-model-access-is-a-provision.md
|
||||
- 02-DECISIONS/0009-modules-and-the-graph.md
|
||||
- 02-DECISIONS/0054-model-usage-is-recorded-at-two-grains.md
|
||||
- 02-DECISIONS/0055-model-access-is-answered-by-a-licence-or-a-node.md
|
||||
---
|
||||
|
||||
# 14 — Model access
|
||||
|
||||
@@ -11,6 +11,7 @@ decisions:
|
||||
- 02-DECISIONS/0006-the-substrate-and-the-control-plane.md
|
||||
- 02-DECISIONS/0005-the-node-host.md
|
||||
- 02-DECISIONS/0010-delivery.md
|
||||
- 02-DECISIONS/0036-bootstrap-ends-at-a-usable-mesh.md
|
||||
---
|
||||
|
||||
# Raising a mesh
|
||||
|
||||
@@ -11,6 +11,8 @@ decisions:
|
||||
- 02-DECISIONS/0039-what-the-sdk-holds-and-refuses.md
|
||||
- 02-DECISIONS/0069-a-module-is-a-repository-and-a-path.md
|
||||
- 02-DECISIONS/0009-modules-and-the-graph.md
|
||||
- 02-DECISIONS/0072-two-graphs-and-the-build-chain.md
|
||||
- 02-DECISIONS/0076-the-sdk-is-a-published-package.md
|
||||
---
|
||||
|
||||
# Building a module
|
||||
|
||||
@@ -11,6 +11,7 @@ decisions:
|
||||
- 02-DECISIONS/0039-what-the-sdk-holds-and-refuses.md
|
||||
- 02-DECISIONS/0043-a-module-broker-account-is-scoped-by-emits-and-consumes.md
|
||||
- 02-DECISIONS/0042-the-shape-of-an-event-on-the-wire.md
|
||||
- 02-DECISIONS/0047-a-module-runs-its-code-as-its-own-process-with-its-own-account.md
|
||||
---
|
||||
|
||||
# The module protocol
|
||||
|
||||
@@ -10,6 +10,7 @@ decisions:
|
||||
- 02-DECISIONS/0074-the-wire-is-specified-not-the-types.md
|
||||
- 02-DECISIONS/0040-what-a-module-is.md
|
||||
- 02-DECISIONS/0039-what-the-sdk-holds-and-refuses.md
|
||||
- 02-DECISIONS/0053-a-step-that-runs-on-a-schedule.md
|
||||
---
|
||||
|
||||
# Writing a module
|
||||
|
||||
Reference in New Issue
Block a user