Decisions

ADR-127: Docs as typed playbooks

Durable engineering content lives in git ([ADR-126](./126-durable-engineering-content-in-git.md)). The substrate is settled. The shape of the content within that substrate is not.

#127

ADR-127: Docs as typed playbooks

Status: Proposed Date: 2026-05-04

Related: ADR-067 (playbooks-unified-primitive), ADR-117 (module-guidelines), ADR-116 (gyrum-knowledge-base-rag-service), ADR-126 (durable-engineering-content-in-git), ADR-128 [planned] (cross-reference graph), ADR-129 [planned] (themes + retros + verification + feature-doc synthesis), ADR-130 [planned] (multi-axis tag taxonomy)

Context

Durable engineering content lives in git (ADR-126). The substrate is settled. The shape of the content within that substrate is not.

Today, ADRs live in dark-factory/docs/decisions/, runbooks in dark-factory/docs/runbooks/, feature-docs in dark-factory/docs/features/, retros as session-log addenda, postmortems wherever the operator wrote them. Each kind is a markdown file with informal frontmatter. Some ADRs declare Status: and Date:; some do not. Some runbooks open with a context paragraph; some jump straight to commands. Cross-references are prose links that may or may not resolve. Superseded ADRs may or may not carry Superseded by:. Stale runbooks read as authoritative because nothing flags them.

The fleet's runtime work has the opposite shape. Playbooks (ADR-067) declare inputs, outputs, validators, scaffolders. gyrum-validate-playbook rejects malformed YAML. gyrum-create-playbook <kind> scaffolds the right shape so authors never start from a blank file. Module guidelines (ADR-117) apply the same primitive to per-module rules: declared schema, structural checks, scaffolders, versioned files. The RAG layer (ADR-116) indexes typed corpora because typed corpora are mechanically queryable.

Docs are the last unstructured durable artefact in the system. Authors start from a blank file or a copy-paste of a recent example. Maintainers grep for citations because no graph exists. Synthesis from merged work into a feature-doc is hand labour because the upstream signals (verification evidence, retros, theme membership) have no shape that a synthesizer can read.

The principle this ADR names is the precondition for every later phase of the docs-as-playbooks EPIC (warp#1197): r1 scaffolder, r2 validator, r3 lifecycle hooks, r4 RAG indexing, r5 feature-doc synthesis.

Decision

Docs are typed artefacts. Each kind has a declared schema, a scaffolder, a validator, lifecycle hooks, and a place in the RAG index. Cross-references are tracked. Freshness is observable.

The doc kinds in scope:

Kind Path Purpose
adr <repo>/docs/decisions/NNN-<slug>.md A decision, its context, its consequences, alternatives considered
runbook <repo>/docs/runbooks/<slug>.md A procedure for an operator or agent to execute
feature-doc <repo>/docs/features/<slug>.md The narrative of a shipped feature, synthesized from upstream signals
retro <repo>/docs/retros/<slug>.md A structured retrospective on a shipped piece of work
postmortem <repo>/docs/postmortems/<slug>.md A structured post-incident analysis
playbook-spec <repo>/docs/proposals/<slug>.md A specification for a new playbook before it is implemented

Each kind carries declared frontmatter:

  • kind (one of the six above)
  • status (draft, active, superseded, archived)
  • date (creation date)
  • owner (operator or team)
  • tags (multi-axis taxonomy per ADR-130 [planned])
  • freshness_window (e.g. 90d, 1y, permanent) describing the half-life of the content
  • supersedes (list of doc ids this one replaces)
  • supersede_by (the doc id that replaces this one, set when status flips to superseded)
  • refs (canonical inbound and outbound references for the cross-reference graph)

The five enforcement layers, mirroring the playbook primitive (ADR-067) and the module guideline shape (ADR-117):

  1. Schema per kind (one schema file per kind, versioned, lives in devtools).
  2. Scaffolder (gyrum-create-doc <kind> <slug>) that emits the right shape so authors never start from a blank file.
  3. Validator (gyrum-validate-doc) that runs in gyrum-review-pr on PRs touching docs/decisions/**, docs/runbooks/**, docs/features/**. It rejects missing frontmatter, broken cross-references, em-dashes, AI-tells, freshness violations, and status invariants (a superseded doc must carry supersede_by; an active doc must not).
  4. Lifecycle hooks (gyrum-supersede-doc, gyrum-archive-doc, gyrum-doc-freshness) that write back-links atomically, audit archival moves, and file Warp tickets for docs past their freshness window.
  5. RAG index (ADR-116) that consumes the typed corpus, computes inbound citation counts, and surfaces orphan docs, over-cited canonical docs, and stale docs on a fleet dashboard.

Synthesis (r5 of the EPIC) cashes in this structure: a feature-doc draft is composed from typed upstream signals (verification evidence per warp#1156, retros per warp#1133, ADRs and runbooks tagged with the theme) rather than hand-aggregated from chat memory.

Consequences

What becomes easier

  • An author or agent never starts a new doc from a blank file. The scaffolder picks the next ADR number, populates the frontmatter, and opens the editor at the first content section.
  • The validator catches missing frontmatter, broken cross-references, em-dashes, and AI-tells at PR time. Drift fails the gate; it does not ship.
  • A superseded ADR carries a mechanical link to its replacement. Readers landing on a stale ADR are routed to the current one without grep.
  • Stale runbooks file Warp tickets when their freshness window expires. The signal is queueable, not silent.
  • The RAG index surfaces orphan docs (zero inbound cites, candidate for archive) and over-cited docs (top 20, signal of canonical status). Curation becomes mechanical.
  • Feature-doc synthesis becomes possible because the upstream signals (retros, verification evidence, theme tags) are themselves typed artefacts. The narrative is composed from structure, not from chat memory.

What becomes harder

  • Authoring a new doc requires picking a kind (a small cognitive cost compared to a blank file).
  • The validator may reject docs during the in-flight migration period. An audit-tracked override flag (--skip-doc-validation "<reason>") covers the migration window; the override lands in ~/.gyrum/admin-overrides.log per the feedback_admin_override_with_audit rule (admin override only when size is the sole blocker, audit log names why the unit cannot split).
  • Schema evolution requires a process. Frontmatter changes need a migration step that walks existing docs and rewrites them, or a versioned-schema scheme akin to the module-guideline @v<n> shape.
  • Synthesis (r5) depends on structured upstream signals being shipped first. Verification evidence (warp#1156) and retros (warp#1133) are prerequisites; r5 is blocked on both.

What we sign up to operate

  • Six per-kind schemas with versioning rules akin to module guidelines (ADR-117).
  • A validator wired into gyrum-review-pr with an audit-tracked override flag.
  • A scaffolder, three lifecycle CLIs, and a freshness cron.
  • An incremental RAG indexer that re-indexes on PR merge with a target staleness of one hour (ADR-116).
  • A migration period during which the validator runs in advisory mode (warns but does not block), then ratchets to enforce after one release cycle.

Alternatives considered

  • Keep docs free-form, rely on review judgement to catch drift. This is the status quo. The failure mode is observable: stale runbooks read as authoritative, superseded ADRs lose their replacement link, feature-docs are hand-written one-offs decoupled from the structured trail. Review judgement scales with attention; structure scales with mechanism. Ruled out as the steady state, even though it is the migration starting point.

  • Structure only ADRs, leave runbooks and feature-docs free-form. ADRs are the most cited kind, so this captures most of the cross-reference benefit. It does not capture the freshness benefit (runbooks are the kind that goes stale) or the synthesis benefit (feature-docs are the kind that benefits most from typed upstream signals). Ruled out as a partial fix that leaves the load-bearing kinds unstructured.

  • A separate "docs CMS" service outside git. Adds a third substrate to maintain. Breaks the ADR-126 principle that durable content lives in git. Doesn't compose with gyrum-review-pr, doesn't show up in branch diffs, doesn't follow the operator across machines. Ruled out: more substrates do not equal better.

  • Inline the schema in each doc body (e.g. a leading YAML block per section). Verbose, redundant per doc, and hard to evolve. The frontmatter-plus-validator shape is the same one the playbook primitive (ADR-067) and module guidelines (ADR-117) settled on; doing something different here would fragment the fleet's authoring shape. Ruled out by consistency.

Migration

The principle lands in this ADR. The implementation lands in five subsequent phases of warp#1197:

  1. r0 (this ADR). Names the principle. Subsequent phases cite it.
  2. r1 scaffolder. gyrum-create-doc <kind> <slug>, mirroring gyrum-create-playbook. Six per-kind templates.
  3. r2 validator. gyrum-validate-doc, wired into gyrum-review-pr. Initially advisory; ratchets to enforce after one release cycle. Audit-tracked --skip-doc-validation override for the migration window.
  4. r3 lifecycle hooks. gyrum-supersede-doc, gyrum-archive-doc, gyrum-doc-freshness.
  5. r4 RAG indexing. Cross-reference graph, inbound citation count, fleet dashboard surfaces (orphan, over-cited, stale, supersession).
  6. r5 feature-doc synthesis. gyrum-synthesize-feature-doc <theme-slug> consumes typed upstream signals and emits a draft for operator review.

Phases r0 through r3 unblock everyday doc authoring. Phases r4 and r5 cash in the structure for synthesis and curation.


Supersedes: none Superseded by: