Docs Validation Plan
Documentation is a product contract, so it is guarded like code: with deterministic, file-walking checks that exit non-zero and print actionable findings. This page describes the validation suite that runs today, states honestly what each guard enforces (which is sometimes narrower than the rule it defends), and points at the docs program work that hardens it. Every check walks files, compares against an in-repo source of truth, and fails loudly; none touch the network.
For the three-tier source-of-truth model these checks defend, see the Documentation Contract Architecture.
Guards Wired Today
Six guards are wired. One is the generated-drift guard, run standalone; the other five run under npm run check:docs. Both gen-docs:check and check:docs are part of the global npm run check chain.
Generated-docs drift (npm run gen-docs:check)
- Guards: that every Tier-2 generated file is a byte-exact projection of its Tier-1 source.
- How it works: runs each
scripts/gen-*-docs.tsgenerator in--checkmode (plusscripts/check-spec-drift.tsand the AI-context manifest generator), regenerating the output in memory and failing if the committed file differs. This is the mechanism behind theGENERATED FILE: do not edit by hand.banner and the real drift guard for the whole generated surface. - Status: implemented and in the global chain.
Scaffold existence (npm run check:docs-contract-scaffold)
- Guards: that the documentation contract scaffolding exists and is structurally intact: every required scaffold file (the four docs-system pages, all
docs/product/v1/pages, and alldocs/ai-context/v1/files), a set of load-bearing exact H1 titles (index.mdH1 "Gessa Product Documentation (v1)",README.mdH1 "Gessa AI Context (v1)",llms.txtfirst line "# Gessa", and the four docs-system H1s), and thatai-context-manifest.jsonparses as JSON and carries the literalengineVersionstringv1with afilesarray. - How it works: a deterministic file walk (
scripts/check-docs-contract-scaffold.ts) that asserts each required path exists, parses the manifest, and verifies each critical heading appears verbatim; exits non-zero listing any missing path, invalid manifest, or mismatched title. - Enforcement gap: it checks the manifest against the literal string
v1, not againstENGINE_VERSION_MAJOR. It is an existence and shape gate, not a dynamic version check. - Status: implemented and, because it runs inside
check:docs, in the global chain.
Relative links (npm run check:docs-links)
- Guards: that every relative Markdown link in the hand-authored surface (
docs/docs-system,docs/product/v1,docs/ai-context/v1) resolves to a real path on disk. - How it works:
scripts/check-docs-links.tswalks the.mdand.txtfiles, extracts each link, skips external and pure-anchor links, strips titles and anchor fragments, and fails on any target that does not exist. - Status: implemented and in the global chain via
check:docs.
Markdoc tags (npm run check:docs-markdoc)
- Guards: that every custom Markdoc tag used in prose is one of the eleven, is well-formed (required attributes present, no unknown attributes, enumerated values in range), and, for a binding tag, resolves into its generated file. Tags shown inside fenced code blocks are skipped.
- How it works:
scripts/check-docs-markdoc.tsparses each page with Markdoc, walks the tree, and checks each prose tag against theTAG_SCHEMASandBINDING_TAGSexports inscripts/markdoc/tags.ts. - Enforcement gap: a binding tag resolves by naive substring
.includes()over the generated file, so anameoridthat is a substring of unrelated text resolves even when there is no matching table row. Theversiontag'scoordinateandhashare free strings that no check validates againstpackages/engine-version. - Status: implemented and in the global chain via
check:docs.
Example markers (npm run check:docs-examples)
- Guards: that every fenced
ts,tsx,typescript,js,javascript, orjsonblock in the hand-authored surface carries anormarker in the three lines directly above the opening fence. Blocks intext,bash,sh,mermaid,yaml, ormarkdocare exempt. - How it works:
scripts/check-docs-examples.tsscans line by line for fences and their languages and fails on an unmarked marked-language block. - Enforcement gap: this is a marker-presence check only. It does not compile, type-check, or byte-compare any block against a source file; the marker is a promise the guard does not verify.
- Status: implemented and in the global chain via
check:docs.
Public claims (npm run check:docs-claims)
- Guards: over
docs/product/v1anddocs/ai-context/v1, that a fixed list of banned over-claim phrases never appears in prose, and that a{% contract %}or{% proof %}tag does not carry an accepting status while the v1 readiness ledger has zero accepted rows. - How it works:
scripts/check-docs-claims.tsscans the claim roots for the banned phrases and, while the ledger has no accepted rows, parses each page and fails on acontractwith an accepting status or aproofwith statuspresent. - Enforcement gap: the accepting-status rule has an accepted-rows bypass. It counts accepted rows in
docs/cycles/v1-engine-readiness-loop/FEATURE_LEDGER.jsonand, the moment the first row is accepted, stops checking tag status entirely. It protects only the pre-launch window and does not tie a specific claim to the specific ledger row that would justify it. The banned-phrase list stays in force regardless. - Status: implemented and in the global chain via
check:docs.
What the Docs Program Hardens
The docs program is the cycle that turns the guards above from shape checks into contract checks. Its planned additions and upgrades:
Docs version equals engine version (check:docs-version-freeze)
- Guards: the flagship invariant that nothing enforces today. It asserts that the export manifest, the version archives (
versions.json), the changelog, and the since-ledger agree withENGINE_VERSIONandreleases.json, that the docs folder set equalsv${ENGINE_VERSION_MAJOR}, and that a removed coordinate carries a deletion tombstone. - Status: added by the docs program; wired into the chain and into
deploy.sh.
Closure in both directions (check:docs-closure)
- Guards: code to docs (every registry entry's anchor resolves as a heading in its generated file at the pinned version) and docs to code (every identifier-shaped token in prose resolves to a registry id, every Markdoc binding tag resolves to a heading rather than a substring, and no integer sits next to a catalog noun outside a count tag).
- Status: added by the docs program in warn mode with a sorted shrink-only baseline, then flipped to error for new and edited nodes.
Examples that earn their marker (check:docs-examples, upgraded; docs:examples:nightly)
- Guards: that a
verbatimblock byte-matches its cited symbol span, that areplayexample passes preflight, and, on a nightly lane, that acompileblock type-checks against the SDK and that tutorials run in Playwright against the QA stack. - Status: upgrades today's marker-presence check into a content check.
Binding resolution by row, not substring (check:docs-markdoc, hardened)
- Guards: that a
component,action,capability, ormcp-toolreference resolves to an actual table row or heading in its generated file, and that aversioncoordinate resolves againstpackages/engine-version. - Status: replaces the substring resolution described above.
Claims tied to ledger rows (check:docs-claims, extended)
- Guards: that a capability claim names the specific readiness-ledger row that justifies it, rather than relying on the global accepted-rows bypass. The banned-phrase list is retained.
- Status: extended by the docs program alongside the parity-page binding resolver.
Registry-side DocContract parsing
- Guards: that no surface loads without a purpose, anchor, stability, and audience.
DocContractSchema(added topackages/engine-version) is spread into the five registries, and each registry's import fails when an entry omits a contract field. - Status: landed as warn behind the closure baseline, then flipped per registry when its baseline reaches zero.
Behavioral tier stays current (check:docs-dossier-currency)
Dossiers under docs/spec/dossiers are snapshots of engine behavior, so they are policed for staleness instead of regenerated. Each unit carries a symbols twin of every citation and a receipt with the sha256 of every cited source file. The gate fails hard when a cited symbol no longer resolves or a unit has no receipt, and reports a unit as stale when a cited file changed since its stamp. The doctor collects the strict form; check:docs-version-freeze refuses to pin a release while any unit is stale or broken. Units re-trace against a paused tree at the release point, then re-stamp with --stamp <unit> (decision D26 in the docs program).
Zero em dashes (check:docs-em-dash)
The corpus trees carry no U+2014, code fences included, because a fence is copied verbatim by readers and by the examples gate. Chained into check:docs with a self-test; the engineering record under docs/cycles is out of scope by design.
Wiring Discipline
Checks join the build incrementally, never as a big-bang flip.
gen-docs:checkis already part ofnpm run check. Any new generator registers a--checkmode and is appended to thegen-docs:checkchain (and togen-docs).- A new content check lands first as a standalone
scripts/check-*gate that can be run in isolation, is validated deterministic and green on the current tree, and only then is appended to the global chain. Warn-mode gates carry a sorted shrink-only baseline so two lanes burning down different entries both pass. check:docs(which runs the five hand-authored-surface guards) is in the global chain today. The earlier plan to keepcheck:docs-contract-scaffoldout of the global chain no longer holds: it runs insidecheck:docsand therefore insidenpm run check.
Status
Six guards run today: gen-docs:check plus the five under check:docs (contract-scaffold, links, markdoc, examples, claims), all in the global npm run check chain. Their enforcement is honest but narrow in three places: binding tags resolve by substring, example markers are not verified against source, and the public-claims status rule bypasses once the ledger accepts a row. The docs program hardens each of these and adds the docs-version freeze that no check enforces today. The Markdoc Schema Plan defines the tags several of these checks validate, and the Documentation Contract Architecture defines the three-tier model they defend.