Collapse docs/adr/ and docs/rfc/ into a single docs/rfc/ with proposed/, implemented/, and rejected/ subfolders. Every file is renamed to yyyy-mm-dd-topic-title.md, where the date is when the topic was first proposed (from git history). ADRs and RFCs that covered exactly the same topic are merged (property-based testing, session persistence); the umbrella RFC 005 stays split across its three implemented decisions, and RFC 006's deferred part-3 (API extractor reports) splits into its own proposed RFC. All cross-references become machine-checkable relative links instead of bare "ADR NNNN" / "RFC NNN" prose. Add a verify-md-links doc-sync gate (scripts/verify-md-links.ts) that checks every relative Markdown cross-link resolves, wired into doc-sync alongside verify-md-wrap. This makes the reorganization self-verifying: the same change that rewrote ~forty inter-doc links adds the check that proves none dangle. Document the cross-link convention in a new docs/AGENTS.md and record the gate as an implemented RFC. doc-sync, typecheck, lint, and the full test suite (667) all pass.
3.7 KiB
RFC: Doc-sync enforcement
Status: implemented (accepted 2026-06-14)
Context
AGENTS.md promises that docs and code stay strictly in sync, but the promise was verified by eyeball. Review caught drift twice — a cookbook example contradicting the type policy, and a README citing the wrong registerAdapter call. Out-of-sync docs are worse than no docs, and this codebase is built primarily by agents that follow gates far more reliably than prose (mechanical quality gates). Two classes of doc drift are mechanically checkable: code blocks that no longer compile, and the event-taxonomy table that duplicates the interface Events declarations.
Decision
Two gates, mirroring the existing scripts/ style (tsx ESM, one job each):
doc-typecheckextracts every fenced```tsblock fromREADME.md,docs/**, andpackages/*/README.md, writes them to a temp project, and compiles withtsc --noEmit. The temp tsconfig copies only resolution-relevant options and the workspacepathsmap fromtsconfig.typecheck.json(vendor → builtlib, harness →src) — resolving vendor tolibis essential, or tsc type-checks raw vendor source and floods the run. A block that is a deliberate sketch opts out with an explicit```ts ignore-checkinfo string; the script reports the opt-out ratio and fails if it exceeds half, so the escape hatch can't quietly become the norm.verify-event-taxonomyextracts the event names from theinterface Eventsblocks acrosspackages/*/srcand from the taxonomy table indocs/architecture.md, and asserts the two sets match exactly. Verify, don't generate: the table keeps its hand-written Mode/Purpose columns; only the set of names is checked. (Landing this surfaced three events the table had been missing —tools/change,llm/adapter-change,system-prompt/change.)
Both run via a shared doc-sync package.json script that the lefthook pre-push hook and CI both invoke (mechanical quality gates: hooks and CI call the same scripts, so the gate fires locally before a push — not only after it). They run after pnpm run typecheck (which emits the vendor lib/ that doc-typecheck resolves against). API-extractor golden reports (the deferred API-extractor-reports proposal) were deliberately deferred — low value for an internal monorepo where reviewers already see the source diff, and a heavy, finicky dependency.
Amendment (2026-06-17): a third gate, verify-md-wrap, was later folded into doc-sync. It parses each in-scope Markdown file (README.md, docs/**, packages/*/README.md, plus AGENTS.md / packages/AGENTS.md) with mdast-util-from-markdown + GFM and fails on any paragraph node spanning more than one source line, enforcing the AGENTS.md "Markdown is not hard-wrapped" convention. Same verify-don't-generate principle: it reports hard-wraps and never rewrites, so it adds no formatting churn. doc-sync is now three gates.
Consequences
- Doc drift in the checkable classes now fails the pre-push hook and CI instead of waiting for a reviewer to notice. This is an instance of the "mechanical gates over prose" principle.
- Making doc snippets compile costs a few stub imports/
declares; theignore-checkratio must stay low or the gate is theater (the ratio guard enforces this). - The taxonomy check is name-only — a wrong Mode or Purpose column still needs human review. Generating the table from source was considered and rejected as more machinery than the problem warrants.
- API reports remain available to revisit if the packages are ever published externally.