feat(610): generated where-did-it-go index on each stripped file
Live docs point into the corpus BY DATE -- "see `decisions.md` 2026-07-10" -- about 32 such references across 12 files, plus the same form in historical issue comments. The split would dangle every one of them. Each stripped file now ends with a generated "Records formerly in this file" index: date, title, and a link to the record's new path. A reader following a date pointer lands on the file it names and resolves from there. That is far cheaper and less error-prone than rewriting 32 references by hand, and it also covers the issue-comment breadcrumbs, which cannot be rewritten at all. Caught while verifying it: the generated `## Records formerly in this file` heading is itself an H2, so the record parser counted one legacy-unmigrated record per stripped file -- the notice went 0 -> 6. Same treatment as the existing `## Index` section: skip it by name. SKIP_HEADINGS moved to decisions_lib as the single source of truth, since three modules now need it. Found by reading the validator's notice output on a trial migration, not by inspection -- the corpus still validated OK, so nothing else would have flagged it.
This commit is contained in:
@@ -93,6 +93,34 @@ def _rationale_body(rec: dl.Record) -> str:
|
||||
return "\n".join(lines).strip()
|
||||
|
||||
|
||||
_MOVED_HEADING = "## Records formerly in this file"
|
||||
|
||||
|
||||
def _moved_index(records: list[dl.Record]) -> str:
|
||||
"""A generated 'where did it go' index appended to each stripped file.
|
||||
|
||||
Live docs point into the corpus BY DATE — "see `decisions.md` 2026-07-10" — roughly 32 such
|
||||
references across 12 files. The split would dangle every one of them. This index keeps them
|
||||
resolvable: the reader lands on the file the pointer names and finds the date, the title, and
|
||||
the record's new path. Cheaper and far less error-prone than rewriting 32 references by hand,
|
||||
and it degrades gracefully for the historical issue comments that use the same form.
|
||||
"""
|
||||
if not records:
|
||||
return ""
|
||||
lines = [
|
||||
_MOVED_HEADING,
|
||||
"",
|
||||
"Each record below moved to its own file under `records/` (ersatztv#610); the rationale is",
|
||||
"unchanged. Resolve by **key** — that is the stable identity. A date-based pointer from",
|
||||
"another doc or an old issue comment should land here and then follow the link.",
|
||||
"",
|
||||
]
|
||||
for r in sorted(records, key=lambda x: (x.heading, x.key or "")):
|
||||
dest = record_path(r).relative_to(dl.TOPIC_DIR)
|
||||
lines.append(f"- {r.heading} — [`{r.key}`]({dest})")
|
||||
return "\n".join(lines)
|
||||
|
||||
|
||||
def _preamble(path: Path, records: list[dl.Record]) -> str:
|
||||
"""Everything in `path` that is not inside a record — kept, never deleted."""
|
||||
text = path.read_text(encoding="utf-8")
|
||||
@@ -117,13 +145,15 @@ def plan() -> tuple[list[tuple[dl.Record, Path]], dict[Path, str]]:
|
||||
for src in sources:
|
||||
if dl.RECORDS_DIR in src.parents or dl.has_frontmatter(src.read_text(encoding="utf-8")):
|
||||
continue # already migrated
|
||||
recs = [r for r in dl.parse_file(src) if r.heading not in ("Index", "Active catalog", "Contents")]
|
||||
recs = [r for r in dl.parse_file(src) if r.heading not in dl.SKIP_HEADINGS]
|
||||
keyed = [r for r in recs if r.key]
|
||||
if len(keyed) != len(recs):
|
||||
unkeyed = [r.heading for r in recs if not r.key]
|
||||
raise SystemExit(f"{src}: {len(unkeyed)} record(s) without a key, cannot file by key: {unkeyed}")
|
||||
moves += [(r, record_path(r)) for r in keyed]
|
||||
preambles[src] = _preamble(src, recs)
|
||||
body = _preamble(src, recs)
|
||||
idx = _moved_index(keyed)
|
||||
preambles[src] = f"{body.rstrip()}\n\n{idx}" if idx else body
|
||||
return moves, preambles
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user