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)