docs/rfc/README.md's per-lifecycle tables are now generated between gen-rfc-index marker comments from each RFC's path (lifecycle/class), H1 title (optional 'RFC: ' prefix stripped), and filename date, sorted by date then filename — the one docs region every proposal wave edits and every concurrent branch conflicts on becomes derived state. scripts/rfc-index.ts owns the shared walker (closed lifecycle/class sets, structure rules, parseable-H1 requirement) and the renderer; gen-rfc-index.ts is the writer CLI; verify-rfc-classification.ts keeps the structure check and asserts the committed regions byte-match a fresh render (freshness subsumes the index-completeness check, since a generated-from-disk table is definitionally complete and correctly headed). A malformed H1 is a hard error in both directions, so the H1 is now load-bearing as the title source — the one nonconforming H1 (a status suffix duplicating the path) is normalized. Implements docs/rfc/implemented/process/2026-07-04-generate-rfc-index-tables.md (moved from proposed/ and amended to the shipped mechanics); the classification RFC's verify-only stance carries the supersession cross-link per implemented/AGENTS.md.
3.1 KiB
RFC: Generate the RFC index tables
Status: implemented
Problem
docs/rfc/README.md's per-lifecycle/per-class tables list facts that are fully derivable: an RFC's path encodes lifecycle and class, its filename encodes the first-proposed date, and its H1 carries the title. A hand-maintained copy of those facts is also the repo's highest-contention docs hotspot: every proposal wave appends rows to the same few lines, so concurrent RFC branches conflict precisely there while agreeing everywhere else, and each conflict is resolved by hand-merging rows whose content the filesystem already knows. The classification RFC originally kept the index hand-written for curation's sake — but the curated part of the README is the prose, and the prose never conflicts; only the mechanical tables do.
Decision
Keep the curated prose; generate the tables. scripts/rfc-index.ts is the shared source of truth — the tree walker (owning the closed lifecycle/class sets and the structure rules, including a parseable-H1 requirement) and the renderer (rows from H1 title with any RFC: prefix stripped, plus the filename date, sorted by date then filename, grouped as ### {Class} sections in canonical class order). Two thin consumers share it:
scripts/gen-rfc-index.ts(pnpm run gen-rfc-index) rewrites the three marker-delimited regions in the README (<!-- gen-rfc-index:begin {lifecycle} -->…end), one per## {Lifecycle}section, leaving everything outside the markers untouched.scripts/verify-rfc-classification.ts(adoc-syncmember) checks structure and asserts the committed regions byte-match a fresh render — thegen-cordis-catalog/verify-cordis-catalogpattern. Freshness subsumes the index-completeness check: a generated-from-disk table is definitionally complete and correctly headed.
Adding, moving, or deleting an RFC means editing only the RFC file and running the generator; the classification RFC's rejected-alternatives record carries the supersession cross-link.
Why not the verifier-only model?
It catches mistakes but still makes every proposal edit a shared hotspot, and a failed verifier is strictly more annoying than a generator for a purely mechanical row: the author has already named and placed the file; the index copy adds no information. This is the same hand-list-versus-derivation judgment the package-inventory proposal applies to tsconfig references and knip stanzas — applied to the one list that demonstrably conflicts.
Consequences
- The generated regions are explicit: marker comments make script ownership obvious to reviewers, and the generator refuses to run on a structurally invalid tree.
- A malformed or missing H1 is a hard error in both the generator and the gate — the H1 is now load-bearing as the index title source.
- Concurrent RFC branches resolve index conflicts by rerunning the generator, never by hand-merging rows.