Generator emission of manual content-collection frontmatter
Context and Problem Statement
The 228 manual markdowns at docs/manual/ were generated by four parallel workers during the prior session (per commits 6328d03 / 0c3619d / fbf8d85 / 4c16c58 / b93deb2). Those workers emitted YAML frontmatter shaped for human readability, NOT for Astro content-collection Zod validation (which lands in ADR-0016).
For regenerability to hold (per ADR-0015 §“Source of truth”), the generator must emit collection-valid frontmatter when re-emitting the manual from updated TTLs. Otherwise: regenerate → Zod validation fails at build → manual edits needed → regenerability broken.
This ADR adds an opda-gen extension (opda-gen emit-manual or similar) that walks the source TTLs and emits the 228 markdowns with frontmatter matching the Zod schema declared in ADR-0016.
Per the implementation programme plan §4, this is Phase 5 — gated on Phase 2 (ADR-0017 components, which declare the entry consumption contract) + Phase 3 (ADR-0018 plumbing, which declares any frontmatter the plugins inject).
Implicit in ADR-0015 §“Bad” item (“docs/manual/ content needs frontmatter for Zod validation”) — this ADR owns that follow-up.
Decision Drivers
- The generator already runs at
tools/opda-gen/per ADR-0007 + ADR-0008 — the IA’s “docs-gen mode” is a natural extension. - Frontmatter shape must match ADR-0016’s Zod schema verbatim — the implementing worker reads that schema as the spec.
- Mechanical extraction from TTLs:
tier← which tier this entry belongs to (function of output path)module← which module TTL the entry derives fromkind← entry type per the Zod enum (entity/scheme/exemplar/tier-readme/module-readme/cross-cutting/per-module-deployment/derived-profile/overlay-deployment/operations)entityUri←opda:<LocalName>for entity / scheme / exemplar entries (extracted from TTLowl:Class/skos:ConceptScheme/ exemplar typing)sourceTtl← which TTL file holds the canonical sourcesourceOdr←dct:sourceURI from the TTL blocktitle←rdfs:label @enfor entity / scheme; H1 for the file otherwise
- Round-trip discipline:
opda-gen emit-manualshould be idempotent — running twice produces byte-identical output (per the existingci-byte-identitydiscipline). - Scope cap: frontmatter ONLY. This ADR does not change content. Content changes route through the IA spec amendment cycle.
Considered Options
- A — Extend
opda-genwithemit-manualsubcommand — chosen per programme plan. - B — Separate
opda-manual-gentool — duplicates the TTL-parsing infrastructure already in opda-gen. - C — Astro build-time script — couples regeneration to the Astro build; the generator pattern is to emit + commit ahead of build, not at build time.
- D — Leave frontmatter manually edited per file — defeats regenerability; rejected per ADR-0015.
Decision Outcome
Option A — opda-gen emit-manual --tier <name> and opda-gen emit-manual (umbrella) subcommands shipped at tools/opda-gen/src/opda_gen/emitters/manual.py; CLI wired in tools/opda-gen/src/opda_gen/cli.py.
G19a scope decision: option (c) — Tier READMEs, module READMEs, umbrella README, and VALIDATION-REPORT are excluded from generator scope. Their editorial body (including Phase 4’s “See also: Modelling section” blocks) is preserved intact. See implementation report §“Scope decision” for rationale.
Consequences
Positive:
- All 184 in-scope manual markdowns now carry collection-valid frontmatter matching the ADR-0016 Zod schema.
opda-gen emit-manualis idempotent: second run produces zero changes.- Phase 4 “See also: Modelling section” blocks in 4 tier READMEs are fully preserved.
- Astro build exits 0 with 386 pages and zero Zod validation errors post-emission.
Neutral:
- Tier READMEs and module READMEs remain without
kind/tier/modulefrontmatter fields in the generated output;src/lib/manual.tspath-derived helpers remain the source of truth for those entries. pyyamlis a new runtime dependency for the emitter; not yet declared inpyproject.toml(follow-up for validator).
Negative / open items:
physical-ontology/<module>/classes.mdand sibling files getkind: entityand a syntheticentityUri(e.g.opda:Classes) because they cover all classes in a module rather than a single entity. A future ADR could addkind: module-classes/kind: module-shapesenum values.
Confirmation
tools/opda-gen/src/opda_gen/emitters/manual.pyexists withemit_manual(output_dir, *, tier=None)API — CONFIRMEDopda-gen emit-manualCLI subcommand wired — CONFIRMED (verify viaopda-gen --help)opda-gen emitumbrella runsemit-manualat the end of the existing pipeline — CONFIRMEDopda-gen emit-manual --output docs/manualtwice → second run:0 files updated, 218 files skipped— CONFIRMED (idempotency)astro buildafter regeneration: exit 0, 386 pages, zero Zod validation errors — CONFIRMED- 31 tests in
tools/opda-gen/tests/test_manual.pycovering per-tier emission, frontmatter fields, byte-identity, merge discipline; 158 total passing — CONFIRMED - Implementation report:
docs/adr/implementation-reports/ADR-0020-implementation.md— CONFIRMED
Programme retirement
ADR-0020 acceptance is the final gate in the manual-Astro-integration programme. When this validation passes:
- ADR-0015 §Confirmation 10/10 green
- ADRs 0016–0020 all
status: accepted - Manual content regenerable from TTLs without manual frontmatter edits
- Site builds + deploys
Programme retires per docs/plan/manual-astro-integration.md §11.
More Information
- Programme plan:
docs/plan/manual-astro-integration.md— sequence + retirement criterion - Architectural decision (anchor): ADR-0015 §“Build-time markdown processor”
- Generator predecessor: ADR-0007 — Ontology generator specification + ADR-0008 — Generator implementation infrastructure
- Frontmatter Zod schema source: ADR-0016 §“JSON-driven content collections” (when authored)
- Existing CI discipline (pattern reused):
opda-gen ci-byte-identityper ADR-0008 §“CI workflow” - Out of scope: Content changes (route through IA spec amendment cycle); diagram regeneration (handled by
process-document.jsper the/diagrammingskill)
Comments
Loading comments…
Sign in to post a comment