Merge pull request 'Declare every partial supersession in both records, and check it' (#305) from fix/loose-links-between-decision-records into main

This commit was merged in pull request #305.
This commit is contained in:
2026-10-10 22:53:42 +00:00
79 changed files with 239 additions and 83 deletions
+1
View File
@@ -26,6 +26,7 @@ indistinguishable from one that cannot.
| `rests-on` | `decisions:` and `extends:` name records that exist and are **accepted** | the class behind both incidents |
| `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)) |
| `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 |
+69
View File
@@ -264,6 +264,74 @@ def check_supersession_symmetry(failures, records):
)
PARTIAL_MARK = re.compile(
r"^> \*\*Superseded in part — (\d{4}-\d{2}-\d{2}), by \[ADR (\d{4})\]\(", re.M
)
# The form these notes had before the check: it named the later record but nothing read it.
NARROWED_MARK = re.compile(r"^> \*\*Narrowed, not replaced — [^\n]*\[ADR \d{4}\]\(", re.M)
def check_partial_supersession(failures, records):
"""A record superseded in part says so under its heading, and its superseder names it.
A partial supersession was written only in the newer record's prose six times, and 11 of the
21 `supersedes-in-part:` links had no note on the record they change, so a reader who arrived
at the older record by a citation read a rule that no longer held. Both directions are
checked: the field on the newer record and the dated note on the older one, naming each other.
The note is dated by the newer record's `date:`, and the older form of the note, which named a
later record and was read by nothing, is refused so it cannot be a way round this check.
"""
for number, record in records.items():
claimed = record["front"].get("supersedes-in-part", [])
if isinstance(claimed, str):
claimed = [claimed] if claimed else []
for entry in claimed:
match = ADR_FILE.match(os.path.basename(entry))
if not match or match.group(1) not in records:
failures.add("partial-supersession", rel(record["path"]), f"no such record: {entry}")
continue
older = records[match.group(1)]
marks = [by for _, by in PARTIAL_MARK.findall(older["text"])]
if number not in marks:
failures.add(
"partial-supersession",
rel(older["path"]),
f"ADR {number} supersedes this in part; this record carries no "
f"'> **Superseded in part — YYYY-MM-DD, by [ADR {number}](…).**' note",
)
for line in NARROWED_MARK.findall(record["text"]):
failures.add(
"partial-supersession",
rel(record["path"]),
f"a note names a later record in the old form ({line.strip()[:60]}…); write it as "
f"'> **Superseded in part — YYYY-MM-DD, by [ADR NNNN](…).**' and declare it in that "
f"record's `supersedes-in-part:`",
)
for date, by in PARTIAL_MARK.findall(record["text"]):
if by in records and date != str(records[by]["front"].get("date", "")):
failures.add(
"partial-supersession",
rel(record["path"]),
f"the note for ADR {by} is dated {date}; ADR {by} is dated "
f"{records[by]['front'].get('date', 'nowhere')}",
)
if by not in records:
failures.add("partial-supersession", rel(record["path"]), f"note names no such record: ADR {by}")
continue
names = records[by]["front"].get("supersedes-in-part", [])
if isinstance(names, str):
names = [names] if names else []
if record["name"] not in [os.path.basename(str(n)) for n in names]:
failures.add(
"partial-supersession",
rel(records[by]["path"]),
f"ADR {number} says this supersedes it in part; this record's "
f"`supersedes-in-part:` does not name it",
)
def check_topics(failures, records):
"""Every record names a topic of the reading order.
@@ -406,6 +474,7 @@ def main():
check_rests_on(failures, records)
check_live_citations(failures, records)
check_supersession_symmetry(failures, records)
check_partial_supersession(failures, records)
check_numbering(failures, records)
check_topics(failures, records)
check_status_against_code(failures)
@@ -8,6 +8,11 @@ reconstructed: false
# 6. The substrate and the control plane
> **Superseded in part — 2026-10-07, by [ADR 0244](0244-the-mesh-is-described-in-domains-and-one-word-names-one-thing.md).**
> The seven contexts below no longer describe the mesh: it is described in ten domains, and "domain" replaces
> "context" in this sense; *observability* becomes Health and repair. This record keeps its word, and the
> glossary says how to read it. Everything else decided here stands.
*Consolidated 2026-08-28 from six records. Extended 2026-08-29, by building it: the language, and
what must be running before the control plane starts — which this record had left not established
and could not have settled the way it was asking.*
@@ -4,7 +4,7 @@ status: accepted
date: 2026-08-26
deciders: jochen
reconstructed: false
extends: 0009-modules-and-the-graph.md
extends: 02-DECISIONS/0009-modules-and-the-graph.md
---
# 8. A context owns its store, exclusively
+6 -1
View File
@@ -8,6 +8,11 @@ reconstructed: false
# 9. Modules and the graph
> **Superseded in part — 2026-10-06, by [ADR 0226](0226-the-private-network-is-assigned-by-its-own-name-and-the-proxy-names-its-public-issuer.md).**
> The section *what a domain module turns out to be* no longer describes what runs: `networking` is retired,
> every machine is assigned the private network's own module, and the catalogue holds no domain module. The
> mechanism, a module with requirements and no files, and the rest of this record stand as decided here.
*Consolidated 2026-08-28 from six records.*
## Everything is a module
@@ -38,7 +43,7 @@ hand.
### What a domain module turns out to be, and why it is not the one refused above
> **Narrowed, not replaced — 2026-10-06, by [ADR 0226](0226-the-private-network-is-assigned-by-its-own-name-and-the-proxy-names-its-public-issuer.md).** The catalogue no longer
> **Superseded in part — 2026-10-06, by [ADR 0226](0226-the-private-network-is-assigned-by-its-own-name-and-the-proxy-names-its-public-issuer.md).** The catalogue no longer
> holds a domain module: `networking` was retired, and every machine is assigned the private network's
> own module. A module with requirements and no files stays a thing a module may be; "why it is worth
> having" below describes what this record decided for `networking`, not what runs.
@@ -4,7 +4,7 @@ status: accepted
date: 2026-08-24
deciders: jochen
reconstructed: false
extends: 0017-a-test-defends-a-decision.md
extends: 02-DECISIONS/0017-a-test-defends-a-decision.md
---
# 18. A picture of a system is read from the system, never from what asked for it
+1 -1
View File
@@ -4,7 +4,7 @@ status: accepted
date: 2026-09-03
deciders: jochen
reconstructed: false
extends: 0009-modules-and-the-graph.md
extends: 02-DECISIONS/0009-modules-and-the-graph.md
---
# 40. What a module is
@@ -4,7 +4,7 @@ status: accepted
date: 2026-09-03
deciders: jochen
reconstructed: false
extends: 0040-what-a-module-is.md
extends: 02-DECISIONS/0040-what-a-module-is.md
---
# 41. Events are a relationship, the lighter sibling of provisioning
@@ -4,7 +4,7 @@ status: accepted
date: 2026-09-03
deciders: jochen
reconstructed: false
extends: 0041-events-are-a-relationship.md
extends: 02-DECISIONS/0041-events-are-a-relationship.md
---
# 42. The shape of an event on the wire
@@ -4,7 +4,7 @@ status: accepted
date: 2026-09-04
deciders: jochen
reconstructed: false
extends: 0041-events-are-a-relationship.md
extends: 02-DECISIONS/0041-events-are-a-relationship.md
---
# 43. A module's broker account is scoped by what it emits and consumes
@@ -4,7 +4,7 @@ status: accepted
date: 2026-09-04
deciders: jochen
reconstructed: false
extends: 0027-a-provision-names-what-the-consumer-is-coupled-to.md
extends: 02-DECISIONS/0027-a-provision-names-what-the-consumer-is-coupled-to.md
---
# 44. A public name is provisioned, not registered by hand
@@ -4,11 +4,16 @@ status: accepted
date: 2026-09-04
deciders: jochen
reconstructed: false
extends: 0005-the-node-host.md
extends: 02-DECISIONS/0005-the-node-host.md
---
# 45. A machine's firewall is the sum of what its modules listen on
> **Superseded in part — 2026-09-28, by [ADR 0138](0138-an-assignment-binds-an-endpoint-and-says-how-far-it-reaches.md).**
> That a listen's `from` "is the whole of public-versus-internal" no longer holds: an assignment states each
> endpoint's reach, a statement that also governs names and certificates, and `from` is the packet filter's
> share of it. The firewall derived and applied on the node, not a provider, stands as decided here.
## Context
The reverse proxy is a *provider*: a module `requires` the `route` capability and a running proxy
@@ -4,7 +4,7 @@ status: accepted
date: 2026-09-04
deciders: jochen
reconstructed: false
extends: 0027-a-provision-names-what-the-consumer-is-coupled-to.md
extends: 02-DECISIONS/0027-a-provision-names-what-the-consumer-is-coupled-to.md
---
# 46. A module's configuration is its assignment's, not its manifest's
@@ -4,7 +4,7 @@ status: accepted
date: 2026-09-04
deciders: jochen
reconstructed: false
extends: 0043-a-module-broker-account-is-scoped-by-emits-and-consumes.md
extends: 02-DECISIONS/0043-a-module-broker-account-is-scoped-by-emits-and-consumes.md
---
# 47. A module runs its code as its own process, with its own account
@@ -4,7 +4,7 @@ status: accepted
date: 2026-09-05
deciders: jochen
reconstructed: false
extends: 0030-data-outlives-the-mesh-that-declared-it.md
extends: 02-DECISIONS/0030-data-outlives-the-mesh-that-declared-it.md
---
# 51. Shared data is the operator's, and a module is granted access to it
@@ -4,7 +4,7 @@ status: accepted
date: 2026-09-05
deciders: jochen
reconstructed: false
extends: 0005-the-node-host.md
extends: 02-DECISIONS/0005-the-node-host.md
---
# 52. An init step is a container run once to completion, gating what follows
@@ -4,7 +4,7 @@ status: accepted
date: 2026-09-06
deciders: jochen
reconstructed: false
extends: 0052-a-step-that-runs-once-before-a-container.md
extends: 02-DECISIONS/0052-a-step-that-runs-once-before-a-container.md
---
# 53. A scheduled step is a container run on a recurring schedule
@@ -4,7 +4,7 @@ status: accepted
date: 2026-09-06
deciders: jochen
reconstructed: false
extends: 0050-model-access-is-vendor-agnostic.md
extends: 02-DECISIONS/0050-model-access-is-vendor-agnostic.md
---
# 54. Model usage is a vendor-neutral record, produced by the adapter, at two grains
@@ -4,7 +4,7 @@ status: accepted
date: 2026-09-07
deciders: jochen
reconstructed: false
extends: 0050-model-access-is-vendor-agnostic.md
extends: 02-DECISIONS/0050-model-access-is-vendor-agnostic.md
---
# 55. Model access is answered by a licence, or by a node that hosts the model
@@ -4,14 +4,14 @@ status: accepted
date: 2026-09-09
deciders: jochen
reconstructed: false
extends: 0007-connectivity.md
extends: 02-DECISIONS/0007-connectivity.md
---
# 66. Public routing is name-agnostic, its names are resolved inside the mesh, and an internal authority can certify them
> **Narrowed, not replaced — 2026-10-03.** One clause of the decision below no longer holds: *publishing
> a granted name into internal resolution, mesh-wide*. A public name now resolves publicly, and only
> names under the mesh's own suffix get a private answer — [ADR 0191](0191-the-meshs-resolver-holds-only-the-meshs-own-names.md).
> **Superseded in part — 2026-10-03, by [ADR 0191](0191-the-meshs-resolver-holds-only-the-meshs-own-names.md).**
> One clause of the decision below no longer holds: *publishing a granted name into internal resolution,
> mesh-wide*. A public name now resolves publicly, and only names under the mesh's own suffix get a private answer.
> Inside the mesh a route is reached and certified by its internal name
> ([ADR 0151](0151-a-routes-internal-name-is-composed-under-the-node-that-serves-it.md)). The label, the
> node's public domain and their composition stand as decided here.
+1 -1
View File
@@ -4,7 +4,7 @@ status: accepted
date: 2026-09-10
deciders: jochen
reconstructed: false
extends: 0006-the-substrate-and-the-control-plane.md
extends: 02-DECISIONS/0006-the-substrate-and-the-control-plane.md
---
# 67. Genesis is a pivot: a temporary control plane installs the registry that makes it permanent
+1 -1
View File
@@ -4,7 +4,7 @@ status: superseded
date: 2026-09-12
deciders: jochen
reconstructed: false
extends: 0016-the-lab.md
extends: 02-DECISIONS/0016-the-lab.md
superseded-by: 02-DECISIONS/0149-the-live-mesh-is-the-test-bed.md
---
@@ -4,7 +4,7 @@ status: accepted
date: 2026-09-12
deciders: jochen
reconstructed: false
extends: 0009-modules-and-the-graph.md
extends: 02-DECISIONS/0009-modules-and-the-graph.md
---
# 69. A module is a repository and a path within it
@@ -4,7 +4,7 @@ status: accepted
date: 2026-09-12
deciders: jochen
reconstructed: false
extends: 0067-genesis-is-a-pivot.md
extends: 02-DECISIONS/0067-genesis-is-a-pivot.md
---
# 70. The catalogue owns the module graph, and genesis builds rather than carries
@@ -4,7 +4,7 @@ status: accepted
date: 2026-09-12
deciders: jochen
reconstructed: false
extends: 0070-the-catalogue-owns-the-module-graph.md
extends: 02-DECISIONS/0070-the-catalogue-owns-the-module-graph.md
---
# 71. Genesis clones from a mesh, and checks what it got
@@ -4,7 +4,7 @@ status: accepted
date: 2026-09-12
deciders: jochen
reconstructed: false
extends: 0070-the-catalogue-owns-the-module-graph.md
extends: 02-DECISIONS/0070-the-catalogue-owns-the-module-graph.md
---
# 72. Two graphs, and a build chain that orders itself
@@ -4,7 +4,7 @@ status: accepted
date: 2026-09-13
deciders: jochen
reconstructed: false
extends: 0070-the-catalogue-owns-the-module-graph.md
extends: 02-DECISIONS/0070-the-catalogue-owns-the-module-graph.md
---
# 73. The installer carries a builder, and the registry stays where it is
@@ -4,7 +4,7 @@ status: accepted
date: 2026-09-15
deciders: jochen
reconstructed: false
extends: 0039-what-the-sdk-holds-and-refuses.md
extends: 02-DECISIONS/0039-what-the-sdk-holds-and-refuses.md
---
# 74. The mesh defines a module protocol; an SDK is an implementation of it
@@ -4,7 +4,7 @@ status: accepted
date: 2026-09-15
deciders: jochen
reconstructed: false
extends: 0014-no-npm-workspace.md
extends: 02-DECISIONS/0014-no-npm-workspace.md
---
# 75. An artifact store is a provision; a package registry is a different one
@@ -4,7 +4,7 @@ status: accepted
date: 2026-09-16
deciders: jochen
reconstructed: false
extends: 0075-two-stores-and-which-provides-what.md
extends: 02-DECISIONS/0075-two-stores-and-which-provides-what.md
---
# 76. The SDK is a published package, and the toolchain resolves it by version
@@ -4,7 +4,7 @@ status: accepted
date: 2026-09-16
deciders: jochen
reconstructed: false
extends: 0006-the-substrate-and-the-control-plane.md
extends: 02-DECISIONS/0006-the-substrate-and-the-control-plane.md
---
# 77. The parts are named controller, foundation, node — not control plane, substrate, master
@@ -4,7 +4,7 @@ status: accepted
date: 2026-09-16
deciders: jochen
reconstructed: false
extends: 0033-the-substrate-is-a-store-and-a-broker.md
extends: 02-DECISIONS/0033-the-substrate-is-a-store-and-a-broker.md
---
# 78. The store and the broker are ordinary modules
@@ -4,7 +4,7 @@ status: accepted
date: 2026-09-17
deciders: jochen
reconstructed: false
extends: 0078-the-store-and-broker-are-modules.md
extends: 02-DECISIONS/0078-the-store-and-broker-are-modules.md
---
# 79. The foundation seats are named after their servers
@@ -4,7 +4,7 @@ status: accepted
date: 2026-09-17
deciders: jochen
reconstructed: false
extends: 0019-how-this-repository-works.md
extends: 02-DECISIONS/0019-how-this-repository-works.md
---
# 80. The development cycle is checked, not trusted
@@ -4,7 +4,7 @@ status: accepted
date: 2026-09-17
deciders: jochen
reconstructed: false
extends: 0080-the-development-cycle-is-checked.md
extends: 02-DECISIONS/0080-the-development-cycle-is-checked.md
---
# 81. A decision nothing cites is not yet in the chain
@@ -4,7 +4,7 @@ status: accepted
date: 2026-09-17
deciders: jochen
reconstructed: false
extends: 0072-two-graphs-and-the-build-chain.md
extends: 02-DECISIONS/0072-two-graphs-and-the-build-chain.md
---
# 82. The registry is reached by name, and the overlay is its security
@@ -4,7 +4,7 @@ status: accepted
date: 2026-09-18
deciders: jochen
reconstructed: false
extends: 0010-delivery.md
extends: 02-DECISIONS/0010-delivery.md
---
# 83. One push leaves the mesh consistent
@@ -9,6 +9,11 @@ extends: 02-DECISIONS/0078-the-store-and-broker-are-modules.md
# 88. The foundation filters before anything listens
> **Superseded in part — 2026-09-22, by [ADR 0100](0100-a-node-in-use-is-adopted-before-it-is-converged.md).**
> On a node raised adopted, the base ruleset is not loaded: its duty, the store never reachable from outside,
> passes to a table of the mesh's that only refuses. On every other node the base ruleset, loaded before the
> store and broker are raised, stands as decided here.
## Context
Adopting the store and broker as ordinary modules
@@ -9,6 +9,10 @@ extends: 02-DECISIONS/0096-an-upstream-image-is-copied-between-registries.md
# 97. A vendor image is a declared build input, and a recipe fetches nothing undeclared
> **Superseded in part — 2026-10-08, by [ADR 0257](0257-a-copied-base-is-recorded-where-it-is-copied-kept-by-the-builds-that-stood-on-it-and-held-once-per-image.md).**
> A declared image is no longer copied "under the module's repository": it is copied into one repository per
> upstream image, whichever modules stand on it. Everything else decided here stands.
## Context
Three modules could not be built by the mesh's builder because their recipes reached for what
@@ -4,7 +4,7 @@ status: accepted
date: 2026-09-21
deciders: jochen
reconstructed: false
extends: 0052-a-step-that-runs-once-before-a-container.md
extends: 02-DECISIONS/0052-a-step-that-runs-once-before-a-container.md
---
# 99. A step that runs once names what it reads, and runs again when it changed
@@ -5,10 +5,17 @@ date: 2026-09-22
deciders: jochen
reconstructed: false
extends: 02-DECISIONS/0078-the-store-and-broker-are-modules.md
supersedes-in-part:
- 02-DECISIONS/0088-the-foundation-filters-before-anything-listens.md
---
# 100. A node in use is adopted before it is converged
> **Superseded in part — 2026-09-23, by [ADR 0105](0105-the-mesh-adopts-the-predecessors-tunnel-in-place.md).**
> The rule that the private network's range must not overlap a tunnel the predecessor still runs now applies
> only where the found tunnel is left running beside the mesh's. On the hub, where the mesh adopts the found
> tunnel in place, there is one tunnel and one range. Everything else decided here stands.
## Context
The mesh replaces a predecessor mesh that is running, on the same machines, with the services
@@ -5,6 +5,8 @@ date: 2026-09-23
deciders: jochen
reconstructed: false
extends: 02-DECISIONS/0100-a-node-in-use-is-adopted-before-it-is-converged.md
supersedes-in-part:
- 02-DECISIONS/0100-a-node-in-use-is-adopted-before-it-is-converged.md
---
# 105. The mesh adopts the predecessor's tunnel in place
@@ -4,7 +4,7 @@ status: accepted
date: 2026-09-25
deciders: jochen
reconstructed: false
extends: 0075-two-stores-and-which-provides-what.md
extends: 02-DECISIONS/0075-two-stores-and-which-provides-what.md
---
# 109. A package registry seat is one per ecosystem, not one for all of them
@@ -4,11 +4,16 @@ status: accepted
date: 2026-09-25
deciders: jochen
reconstructed: false
extends: 0009-modules-and-the-graph.md
extends: 02-DECISIONS/0009-modules-and-the-graph.md
---
# 110. A seat is held by one assignment, from a closed set, and it may deliver a provision
> **Superseded in part — 2026-09-27, by [ADR 0122](0122-a-seat-is-data-a-rename-is-a-database-update.md).**
> "The controller carries the set in code" no longer holds: the seats are rows of a table in the controller's
> store, seeded by a migration, and a seat is referenced by a stable id, its name only a label. The closed set,
> one holder per scope and a delivering seat as the single answer for its provision stand as decided here.
> **Narrowed, not replaced — 2026-09-27, on merging two lines of work.** This was marked superseded by
> [ADR 0126](0126-a-module-declares-its-own-seats.md), and that overstated it: 0126 says in as many
> words that *"everything 0110 decided about what a seat is stands untouched"*. What moved is where the
@@ -4,7 +4,7 @@ status: accepted
date: 2026-09-25
deciders: jochen
reconstructed: false
extends: 0069-a-module-is-a-repository-and-a-path.md
extends: 02-DECISIONS/0069-a-module-is-a-repository-and-a-path.md
---
# 111. A build source is on the mesh's git seat, or it is an external repository
@@ -4,7 +4,7 @@ status: accepted
date: 2026-09-25
deciders: jochen
reconstructed: false
extends: 0046-a-module-configuration-is-its-assignments-not-its-manifest.md
extends: 02-DECISIONS/0046-a-module-configuration-is-its-assignments-not-its-manifest.md
---
# 112. A module definition names no node, no mesh and no path: everything it needs is a requirement the mesh resolves
@@ -4,7 +4,7 @@ status: accepted
date: 2026-09-26
deciders: jochen
reconstructed: false
extends: 0113-the-vault-makes-every-secret.md
extends: 02-DECISIONS/0113-the-vault-makes-every-secret.md
---
# 114. A credential two parties hold rotates over two credentials; one a single party holds rotates in place, staged; and retiring a credential never removes what it reached
@@ -3,7 +3,7 @@ topic: what runs on it
status: accepted
date: 2026-09-26
deciders: jochen
extends: 0112-a-module-definition-names-no-node-mesh-or-path.md
extends: 02-DECISIONS/0112-a-module-definition-names-no-node-mesh-or-path.md
---
# 115. One assignment of a module per node: the module's name is the assignment's identity
@@ -4,7 +4,7 @@ status: accepted
date: 2026-09-26
deciders: jochen
reconstructed: false
extends: 0110-a-seat-is-a-module-assignment-from-a-closed-set.md
extends: 02-DECISIONS/0110-a-seat-is-a-module-assignment-from-a-closed-set.md
---
# 117. A machine's uplink is a seat: the mesh configures the manager, never the link
@@ -4,7 +4,7 @@ status: accepted
date: 2026-09-27
deciders: jochen
reconstructed: false
extends: 0102-the-mesh-writes-into-a-shared-file-never-over-it.md
extends: 02-DECISIONS/0102-the-mesh-writes-into-a-shared-file-never-over-it.md
---
# 118. Undeclaring removes what the mesh made, and gives a unit back the state it was found in
@@ -80,7 +80,7 @@ records the state it first found the unit in, and undeclaring returns the unit t
manager: running before the mesh arrived, running after it leaves.
- **A unit the mesh started is stopped again**, and one it enabled is disabled again — the packet
filter a converge loaded, which returning to adopted unloads.
> **Narrowed, not replaced — 2026-10-09, by [ADR 0269](0269-what-the-meshs-own-declaration-stopped-is-started-again-when-it-goes.md).**
> **Superseded in part — 2026-10-09, by [ADR 0269](0269-what-the-meshs-own-declaration-stopped-is-started-again-when-it-goes.md).**
> "Never started on the way out" has one exception. The host now records what the mesh declared beside
> what it found. A unit found running or enabled that the mesh's own declaration held stopped or disabled
> is started or enabled again when that declaration goes, but only while it is still exactly as the mesh
@@ -4,7 +4,7 @@ status: accepted
date: 2026-09-27
deciders: jochen
reconstructed: false
extends: 0105-the-mesh-adopts-the-predecessors-tunnel-in-place.md
extends: 02-DECISIONS/0105-the-mesh-adopts-the-predecessors-tunnel-in-place.md
---
# 119. A taken tunnel's predecessor is retired once the take is proven
@@ -4,7 +4,7 @@ status: accepted
date: 2026-09-27
deciders: jochen
reconstructed: false
extends: 0112-a-module-definition-names-no-node-mesh-or-path.md
extends: 02-DECISIONS/0112-a-module-definition-names-no-node-mesh-or-path.md
---
# 120. A roster fact carries its format as a template: the mesh owns the data, the module owns the format
@@ -4,19 +4,23 @@ status: accepted
date: 2026-09-27
deciders: jochen
reconstructed: false
extends: 0110-a-seat-is-a-module-assignment-from-a-closed-set.md
extends: 02-DECISIONS/0110-a-seat-is-a-module-assignment-from-a-closed-set.md
---
# 121. A system seat is named for its scope, and a module may define its own
> **Narrowed, not replaced — 2026-10-03.** *"`the-dns-port` → `node-dns-resolver`"* no longer holds:
> the serving role moves to mesh scope as `mesh-resolver`, one per mesh, and `node-dns-resolver` is
> retired ([ADR 0194](0194-the-mesh-has-one-resolver-and-every-node-asks-it-for-the-meshs-names.md)). The
> distinction this record kept — serving and asking are two roles, two seats — stands, and
> `node-resolver-config` is unchanged.
> **Superseded in part — 2026-10-03, by [ADR 0194](0194-the-mesh-has-one-resolver-and-every-node-asks-it-for-the-meshs-names.md).**
> *"`the-dns-port` → `node-dns-resolver`"* no longer holds: the serving role moves to mesh scope as
> `mesh-resolver`, one per mesh, and `node-dns-resolver` is retired. The distinction this record kept —
> serving and asking are two roles, two seats — stands, and `node-resolver-config` is unchanged.
> **The mechanism changed — 2026-10-02, by [ADR 0190](0190-a-seats-work-is-shared-by-its-holders-and-building-is-the-first-such-role.md).** The naming rule stands. The build role this record made mesh-scoped — *the mesh's single build machine* — is node-scoped now: `node-build-agent`, one holder per machine, every holder taking from one work queue.
> **Superseded in part — 2026-09-27, by [ADR 0122](0122-a-seat-is-data-a-rename-is-a-database-update.md).**
> "The renames are a coordinated migration, not a rename" no longer holds: a seat is a row in the controller's
> store, referenced by a stable id, so a rename is one update of its name, and only the claiming manifests are
> edited in the catalogue. The naming rule, the module-defined seats and the seats settled here stand.
## Context
[ADR 0110](0110-a-seat-is-a-module-assignment-from-a-closed-set.md) made seats a closed set the
@@ -5,8 +5,8 @@ date: 2026-09-27
deciders: jochen
reconstructed: false
supersedes-in-part:
- 0110-a-seat-is-a-module-assignment-from-a-closed-set.md
- 0121-a-system-seat-is-named-for-its-scope-and-modules-define-their-own.md
- 02-DECISIONS/0110-a-seat-is-a-module-assignment-from-a-closed-set.md
- 02-DECISIONS/0121-a-system-seat-is-named-for-its-scope-and-modules-define-their-own.md
---
# 122. A seat is data the controller owns, and a rename is a database update
@@ -5,6 +5,8 @@ date: 2026-09-28
deciders: jochen
reconstructed: false
extends: 02-DECISIONS/0045-a-machine-firewall-is-the-sum-of-what-it-listens-on.md
supersedes-in-part:
- 02-DECISIONS/0045-a-machine-firewall-is-the-sum-of-what-it-listens-on.md
---
# 138. An assignment binds an endpoint and says how far it reaches
@@ -9,9 +9,9 @@ extends: 02-DECISIONS/0066-public-routing-is-name-agnostic.md
# 151. A route's internal name is composed under the node that serves it
> **Narrowed, not replaced — 2026-10-03.** *"The roster publishes it as itself, once, at the serving
> node's address"* no longer holds: a public name is never given a private answer, and resolves publicly
> ([ADR 0191](0191-the-meshs-resolver-holds-only-the-meshs-own-names.md)). The internal name this record
> **Superseded in part — 2026-10-03, by [ADR 0191](0191-the-meshs-resolver-holds-only-the-meshs-own-names.md).**
> *"The roster publishes it as itself, once, at the serving node's address"* no longer holds: a public name
> is never given a private answer, and resolves publicly. The internal name this record
> composes is what that rests on, and stands.
## Context
@@ -5,8 +5,8 @@ date: 2026-10-03
deciders: jochen
reconstructed: false
supersedes-in-part:
- 0066-public-routing-is-name-agnostic.md
- 0151-a-routes-internal-name-is-composed-under-the-node-that-serves-it.md
- 02-DECISIONS/0066-public-routing-is-name-agnostic.md
- 02-DECISIONS/0151-a-routes-internal-name-is-composed-under-the-node-that-serves-it.md
---
# 191. The mesh's resolver holds only the mesh's own names; a public name resolves publicly
@@ -5,8 +5,8 @@ date: 2026-10-03
deciders: jochen
reconstructed: false
supersedes-in-part:
- 0121-a-system-seat-is-named-for-its-scope-and-modules-define-their-own.md
extends: 0191-the-meshs-resolver-holds-only-the-meshs-own-names.md
- 02-DECISIONS/0121-a-system-seat-is-named-for-its-scope-and-modules-define-their-own.md
extends: 02-DECISIONS/0191-the-meshs-resolver-holds-only-the-meshs-own-names.md
---
# 194. The mesh has one resolver, and every node asks it for the mesh's names
@@ -16,10 +16,9 @@ extends: 0191-the-meshs-resolver-holds-only-the-meshs-own-names.md
> server, each holding every node's internal domain from the same roster. One resolving module, the
> mesh's names held only by its holders, and the retirement of every per-node copy stand.
> **Narrowed, not replaced — 2026-10-03.** How a node asks is decided again by
> [ADR 0196](0196-a-node-asks-the-meshs-resolver-first-and-a-public-one-only-when-it-is-silent.md): every
> node and container asks `mesh-resolver` first and a public resolver only when it is silent. There is
> no `systemd-resolved` module and no runtime `dns` naming `mesh-resolver`, and step 2 of the migration
> **Superseded in part — 2026-10-03, by [ADR 0196](0196-a-node-asks-the-meshs-resolver-first-and-a-public-one-only-when-it-is-silent.md).**
> How a node asks is decided again there: every node and container asks `mesh-resolver` first and a public
> resolver only when it is silent. There is no `systemd-resolved` module and no runtime `dns` naming `mesh-resolver`, and step 2 of the migration
> reads as 0196 states it. Option 2 below was rejected for a laptop with its tunnel down resolving
> nothing; a public resolver listed second answers exactly then. The one resolver, its placement and
> the retirement of every per-node copy stand.
@@ -5,12 +5,12 @@ date: 2026-10-03
deciders: jochen
reconstructed: false
supersedes-in-part:
- 0194-the-mesh-has-one-resolver-and-every-node-asks-it-for-the-meshs-names.md
- 02-DECISIONS/0194-the-mesh-has-one-resolver-and-every-node-asks-it-for-the-meshs-names.md
---
# 196. A node asks the mesh's resolver first, and a public one only when it is silent
> **Narrowed, not replaced — 2026-10-05, by [ADR 0223](0223-the-mesh-has-two-resolvers-and-a-machine-lists-only-them.md).** The public resolver listed second goes: musl asks
> **Superseded in part — 2026-10-05, by [ADR 0223](0223-the-mesh-has-two-resolvers-and-a-machine-lists-only-them.md).** The public resolver listed second goes: musl asks
> every listed server at once and takes the first reply, so a public "no such name" for a mesh name
> won it, and every Alpine build on the home server failed. Every machine now lists the mesh's
> resolvers — two holders of `mesh-dns-resolver`, its own first on a holder — and nothing else. Every
@@ -4,7 +4,7 @@ status: accepted
date: 2026-10-03
deciders: jochen
reconstructed: false
extends: 0194-the-mesh-has-one-resolver-and-every-node-asks-it-for-the-meshs-names.md
extends: 02-DECISIONS/0194-the-mesh-has-one-resolver-and-every-node-asks-it-for-the-meshs-names.md
---
# 199. A module that answers names declares its zone, and a node's hosts file is one module's
@@ -5,7 +5,7 @@ date: 2026-10-05
deciders: jochen
reconstructed: false
supersedes-in-part:
- 0196-a-node-asks-the-meshs-resolver-first-and-a-public-one-only-when-it-is-silent.md
- 02-DECISIONS/0196-a-node-asks-the-meshs-resolver-first-and-a-public-one-only-when-it-is-silent.md
extends: 02-DECISIONS/0194-the-mesh-has-one-resolver-and-every-node-asks-it-for-the-meshs-names.md
---
@@ -5,7 +5,7 @@ date: 2026-10-06
deciders: jochen
reconstructed: false
supersedes-in-part:
- 0009-modules-and-the-graph.md
- 02-DECISIONS/0009-modules-and-the-graph.md
extends: 02-DECISIONS/0007-connectivity.md
---
@@ -9,6 +9,13 @@ extends: 02-DECISIONS/0227-the-core-holds-nine-rules-each-checked-and-is-built-t
# 234. The mesh holds a conversation with its operator, over channels that are seats, and an answer that performs an action is authorised by the controller
> **Superseded in part — 2026-10-09, by [ADR 0259](0259-an-ask-is-a-message-on-the-bus-any-channel-answers-it-and-the-asker-acts-on-a-warrant.md).**
> §8's controller holding an ask whose answer performs an action, its verbs `authorise request`,
> `authorise answer` and `authorisations`, and the controller performing no longer hold: the router holds the
> ask and issues a warrant, and the asker performs. The operator's identities (§3) and the factors' secrets
> (§8) are the router's own state, not the controller's. The tiers and proofs, the kinded benches, the content
> rule, the desk never authorising, long polling and every tier completable on the away channel stand.
## Context
**The output channel was built in its smallest form** ([ADR 0227](0227-the-core-holds-nine-rules-each-checked-and-is-built-to-them-in-six-phases.md),
@@ -9,7 +9,7 @@ extends: 02-DECISIONS/0227-the-core-holds-nine-rules-each-checked-and-is-built-t
# 237. A change is judged against the mesh that runs, before it merges, on the build seat
> **Narrowed, not replaced — 2026-10-06, by [ADR 0238](0238-a-commit-is-the-build-at-hand-one-commit-one-change-plan-checked-off-the-trunk-and-published-only-on-it.md).**
> **Superseded in part — 2026-10-06, by [ADR 0238](0238-a-commit-is-the-build-at-hand-one-commit-one-change-plan-checked-off-the-trunk-and-published-only-on-it.md).**
> Decision 4's *which* and *what* moved: a pull request is checked when the mesh's module graph says it
> reaches a module — the planner's own answer, not *a repository it builds a module from into that
> branch* — and every repository of the mesh runs its own `merge-check.sh` as a second status,
@@ -5,7 +5,7 @@ date: 2026-10-06
deciders: jochen
reconstructed: false
supersedes-in-part:
- 0237-a-change-is-judged-against-the-mesh-that-runs-before-it-merges-on-the-build-seat.md
- 02-DECISIONS/0237-a-change-is-judged-against-the-mesh-that-runs-before-it-merges-on-the-build-seat.md
extends: 02-DECISIONS/0237-a-change-is-judged-against-the-mesh-that-runs-before-it-merges-on-the-build-seat.md
---
@@ -5,7 +5,7 @@ date: 2026-10-06
deciders: jochen
reconstructed: false
supersedes-in-part:
- 0238-a-commit-is-the-build-at-hand-one-commit-one-change-plan-checked-off-the-trunk-and-published-only-on-it.md
- 02-DECISIONS/0238-a-commit-is-the-build-at-hand-one-commit-one-change-plan-checked-off-the-trunk-and-published-only-on-it.md
extends: 02-DECISIONS/0238-a-commit-is-the-build-at-hand-one-commit-one-change-plan-checked-off-the-trunk-and-published-only-on-it.md
---
@@ -5,6 +5,8 @@ date: 2026-10-07
deciders: jochen
reconstructed: false
extends: 02-DECISIONS/0006-the-substrate-and-the-control-plane.md
supersedes-in-part:
- 02-DECISIONS/0006-the-substrate-and-the-control-plane.md
---
# 244. The mesh is described in domains, and one word names one thing
@@ -5,7 +5,7 @@ date: 2026-10-07
deciders: jochen
reconstructed: false
supersedes-in-part:
- 0019-how-this-repository-works.md
- 02-DECISIONS/0019-how-this-repository-works.md
---
# 248. The decision index is generated when it is read, and never stored
@@ -5,7 +5,7 @@ date: 2026-10-08
deciders: jochen
reconstructed: false
supersedes-in-part:
- 0239-a-delivery-is-owned-by-the-mesh-delivery-module-and-runs-from-commit-to-delivered.md
- 02-DECISIONS/0239-a-delivery-is-owned-by-the-mesh-delivery-module-and-runs-from-commit-to-delivered.md
extends: 02-DECISIONS/0239-a-delivery-is-owned-by-the-mesh-delivery-module-and-runs-from-commit-to-delivered.md
---
@@ -9,6 +9,12 @@ extends: 02-DECISIONS/0234-the-mesh-holds-a-conversation-with-its-operator.md
# 253. A condition says itself to the operator in plain words beside its summary, says whether the operator is needed, and is answered from the notification
> **Superseded in part — 2026-10-08, by [ADR 0258](0258-a-desk-click-performs-only-an-acknowledgement-and-an-answer-is-a-one-time-ticket.md).**
> §6's reading of *acknowledge*, its promise to honour a tier "when one exists", and the consequence that the
> verb's own record says who chose it no longer hold: a desk click performs only an acknowledgement (Details,
> Silence for a week) until answers are authorised, and release, stop, start and restart are said in words.
> The plain words, the verdict, the quiet rule, the time zone and the one-line clearance stand as decided here.
## Context
The operator could not read the mesh's notifications
@@ -5,7 +5,7 @@ date: 2026-10-08
deciders: jochen
reconstructed: false
supersedes-in-part:
- 0252-a-module-puts-an-account-in-a-group-and-the-mesh-says-when-a-new-login-is-needed.md
- 02-DECISIONS/0252-a-module-puts-an-account-in-a-group-and-the-mesh-says-when-a-new-login-is-needed.md
extends: 02-DECISIONS/0236-a-build-is-judged-on-its-first-machine-and-put-back-by-something-other-than-itself-and-so-it-rolls-out-unattended.md
---
@@ -5,7 +5,7 @@ date: 2026-10-08
deciders: jochen
reconstructed: false
supersedes-in-part:
- 0097-a-vendor-image-is-a-declared-build-input.md
- 02-DECISIONS/0097-a-vendor-image-is-a-declared-build-input.md
extends: 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
---
@@ -5,6 +5,8 @@ date: 2026-10-08
deciders: jochen
reconstructed: false
extends: 02-DECISIONS/0253-a-condition-says-itself-to-the-operator-in-plain-words-beside-its-summary.md
supersedes-in-part:
- 02-DECISIONS/0253-a-condition-says-itself-to-the-operator-in-plain-words-beside-its-summary.md
---
# 258. A desk click performs only an acknowledgement, an answer is a one-time ticket, and a verdict is never weakened
@@ -5,7 +5,7 @@ date: 2026-10-09
deciders: jochen
reconstructed: false
supersedes-in-part:
- 0234-the-mesh-holds-a-conversation-with-its-operator.md
- 02-DECISIONS/0234-the-mesh-holds-a-conversation-with-its-operator.md
extends: 02-DECISIONS/0234-the-mesh-holds-a-conversation-with-its-operator.md
---
@@ -5,8 +5,8 @@ date: 2026-10-08
deciders: jochen
reconstructed: false
supersedes-in-part:
- 0162-a-merge-produces-a-tiered-plan-the-mesh-keeps.md
- 0238-a-commit-is-the-build-at-hand-one-commit-one-change-plan-checked-off-the-trunk-and-published-only-on-it.md
- 02-DECISIONS/0162-a-merge-produces-a-tiered-plan-the-mesh-keeps.md
- 02-DECISIONS/0238-a-commit-is-the-build-at-hand-one-commit-one-change-plan-checked-off-the-trunk-and-published-only-on-it.md
extends: 02-DECISIONS/0238-a-commit-is-the-build-at-hand-one-commit-one-change-plan-checked-off-the-trunk-and-published-only-on-it.md
---
@@ -5,6 +5,8 @@ date: 2026-10-09
deciders: jochen
reconstructed: false
extends: 02-DECISIONS/0118-undeclaring-gives-a-unit-back-the-state-it-was-found-in.md
supersedes-in-part:
- 02-DECISIONS/0118-undeclaring-gives-a-unit-back-the-state-it-was-found-in.md
---
# 269. What the mesh's own declaration stopped is started again when it goes
@@ -14,6 +14,13 @@ extends: 02-DECISIONS/0249-a-delivery-groups-order-rules-have-a-precedence-and-a
# 276. Merges are assembled in a rolling window, and each batch is delivered by one walk with one commit per repository
> **Superseded in part — 2026-10-10, by [ADR 0282](0282-a-fix-to-a-leaf-module-runs-healthy-on-every-node-within-five-minutes-of-its-merge-and-a-core-module-within-ten.md).**
> Decision 4's "one walk at a time", and decision 1's window for every merge, no longer hold: a lone merge is
> cut at once, and walks that share no repository, module or edge of the module graph run at once, one
> first-node judgement per node at a time. Two walks of one repository never at once, no walk superseded once
> it started, and the window for a merge that waits for its delivery group or arrives while a batch assembles
> stand as decided here.
## Context
Shortly after midnight on 2026-10-10 two pull requests of the catalogue were merged 18 seconds apart, the
@@ -5,6 +5,8 @@ date: 2026-10-10
deciders: jochen
reconstructed: false
extends: 02-DECISIONS/0276-merges-are-assembled-in-a-rolling-window-and-each-batch-is-delivered-by-one-walk-with-one-commit-per-repository.md
supersedes-in-part:
- 02-DECISIONS/0276-merges-are-assembled-in-a-rolling-window-and-each-batch-is-delivered-by-one-walk-with-one-commit-per-repository.md
---
# 282. A fix to a leaf module runs healthy on every node within five minutes of its merge, and a core module within ten
+15
View File
@@ -73,6 +73,19 @@ rule for the author and the reviewer, and the diff is where it is caught. Making
mean a record declaring the relationship in its frontmatter, which is a change to the record schema and
has not been decided.
**A supersession in part is the declared case, and it is machine-checked.** A record that replaces part of
an older one names it under `supersedes-in-part:`, and the older record carries, directly under its
heading and newest first, a note in the form
> **Superseded in part — YYYY-MM-DD, by ADR NNNN** (a link to it). What no longer holds, and what stands
> as decided here.
dated by the newer record's `date:`. `00-META/checks/records.py` (`partial-supersession`) fails when
either half is missing, when the note's date is not the newer record's, and on a note in the older form
*Narrowed, not replaced*. Where the note sits is the reviewer's to see in the diff. Saying "this narrows" or "this replaces" an older record in prose alone is not
enough: on 2026-10-11, 6 records did only that, and 11 of 21 declared links had no note on the record
they changed.
The records run in the order the decisions were taken, oldest first.
**Every decision is a record.** There is no ledger and no index file — if a decision is worth
@@ -92,6 +105,8 @@ deciders: name
reconstructed: true|false # true when the record was written after the fact from evidence
superseded-by: # 02-DECISIONS/NNNN-....md, when status is superseded
extends: # 02-DECISIONS/NNNN-....md, when this record widens an earlier one
supersedes: # a list of 02-DECISIONS/NNNN-....md this record replaces whole
supersedes-in-part: # a list of 02-DECISIONS/NNNN-....md of which this record replaces a part
---
```