Declare every partial supersession in both records and check it, so a reader who follows a citation to the older record learns what no longer holds
mesh/merge-gate pass: the change touches no module of the mesh's graph
mesh/repo-check pass: its merge-check.sh passed
mesh/delivery superseded: a newer head of the same pull request

This commit is contained in:
2026-10-11 00:47:07 +02:00
parent 6a408428d6
commit fd0b3b9976
36 changed files with 165 additions and 34 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 |
+48
View File
@@ -264,6 +264,53 @@ 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
)
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.
"""
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 _, by in PARTIAL_MARK.findall(record["text"]):
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 +453,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.*
@@ -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
@@ -9,6 +9,11 @@ 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
@@ -9,9 +9,9 @@ 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.
@@ -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
@@ -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
@@ -9,6 +9,11 @@ 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
@@ -9,14 +9,18 @@ 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,7 +5,7 @@ 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
- 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
---
@@ -16,10 +16,9 @@ extends: 02-DECISIONS/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
@@ -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
---
@@ -14,6 +14,12 @@ 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 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
+14
View File
@@ -73,6 +73,18 @@ 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. 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 +104,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
---
```