Skip to content

Corpus Publication — ADR-113

Corpus Publication — ADR-113

One ruling. It discharges the “future work” that ADR-112 clause 3 and the Docs Platform whitepaper §4.3/§5 deliberately left unauthorised: rendering the corpus through the platform. Numbering: 112 is the highest allocated (093 never was), so this is ADR-113. Derivation record: docs-platform/Corpus_Publication_Disclosure_Review.md (findings CP-F-01CP-F-06); where that file and this record disagree, this record wins.


ADR-113 — Corpus Publication Is an Allowlisted, In-Place, Audience-Conditional Rendering

Status: Accepted · Date: 2026-08-08 · Depends-on 112 (the unified-plugin/wiring-only rule and the assert-on-built-output check discipline this ADR’s enforcement reuses) · interacts with 111

Context

The platform exists to render the corpus and currently renders a template placeholder. ADR-112 declined to authorise ingestion pending a disclosure review; that review ran 2026-08-08 and its findings shape every clause below. The standing constraint is the platform’s own rule — cite, never copy — because a copied corpus that drifts is this repository’s most-recorded defect class (W6).

Decision

  1. The corpus renders in place. An Astro content-layer loader reads the authorised files directly from docs/ at build time. No corpus file is ever copied, moved, or modified to serve the renderer — including frontmatter: page titles and slugs are derived at load time from each file’s first heading. A corpus file gaining renderer-serving frontmatter would be the platform becoming a second rulebook over docs/, which ADR-112’s enforcement section already forbids.

  2. Publication is governed by a file-enumerated manifesttools/docs-publication-manifest.mjs, a plain data module with no globs — consumed by both the loader and the CI check so the two cannot disagree (one copy, the _registry.py argument). A file absent from the manifest does not exist as far as the build is concerned; a new file in docs/ is unpublished by default. Adding a row is a reviewed act. (CP-F-03: docs/marketplace/ carries internal working prompts beside its whitepaper — a directory glob leaks on day one.)

  3. The authorised set, generated by the tier rule (a file renders only if the docs/README.md authority ranking classes it as specification, registry, blueprint tier, or decision record):

    • docs/vtt/: the VTT whitepaper; Quantity_Registry.md; architecture_blueprints.md; Future_Frontend_Roadmap.md; ui_ux_mockups.md with its 12 referenced .jpg files as one unit (images copied at build as a generated artefact — generated and never committed, like every generated artefact here). Corrected 2026-08-08 while pinning the manifest: this clause said 14, repeating a count recorded at the 2026-08-06 restructure. Measured: 13 .jpg on disk, 12 referenced by the page, and git history shows no .jpg was ever deleted — the 14 was never true. The unreferenced 13th (vtt_marketplace_checkout_1785895290875.jpg) was deleted by owner ruling the same day — an unreferenced asset is dead weight — so the unit is the page plus its 12 referenced images, on disk and in the manifest alike.
    • docs/marketplace/: the Marketplace whitepaper; Marketplace_Architecture.md.
    • docs/studio/: Studio_Architecture.md.
    • docs/docs-platform/: the Docs Platform whitepaper. Authorised and not currently rendered (found at the first real build, 2026-08-08): the file documents the scrubber’s marking grammar, so its rendered page carries the literal marker token and trips the --check belt — the belt scanning every shipped byte cannot tell grammar documentation from a leaked region, which is its design. Weakening the belt is forbidden without the ADR-045 argument; clause 5’s rule for a file the pipeline cannot ship whole applies, and the page stays out of the manifest until the owner rules on the collision (candidates: a documented per-file belt exemption with its own argument, or restructuring how the whitepaper writes the grammar). The three shipped API pages already phrase their provenance notes to avoid the token — the collision is real and was designed around once before. Ruled, same day: the belt is absolute — no per-file exemption. The whitepaper’s §4 now describes the token (the at-sign glyph immediately followed by the word internal) instead of spelling it, the literal form lives only in the scrubber’s source and tests, and the file is restored to the manifest and renders with the rest of the corpus.
    • docs/decisions/: every record file in that directory, without exception — the one tier published wholesale rather than file by file. Sequence gaps (no R2, no R10, no ADR-093) are filing history, not missing pages. Corrected 2026-08-08: this clause counted (“all twenty-one record files”) and the count went stale within one working session. R21 and R22 were written, merged and silently unpublished, because the manifest is hand-enumerated and nothing checked it against the directory. That is precisely the consequence this ADR predicted of itself — “a document can now be written, merged, and invisibly absent from the site… the release-notes habit has to” catch it — and a habit is not an enforcement point (P3). The clause is now count-free and docs-publication-manifest.test.mjs asserts the directory against the manifest, so the gap is a red suite rather than a discovery.
  4. The excluded set, each with its owning reason (full argument in the review): docs/historical/ including superseded/ (CP-F-02 — search flattens the authority ranking; superseded numbers must not outrank the registry in a search result); docs/backend/ (self-declared stale layout claims and handover documents); docs/business/ (commercial strategy, not architecture); docs/spikes/ (CP-F-05 — an unrun spike rendered is a result fabricated by presentation, P7); docs/README.md (the contributor hub); docs/Open_Questions.txt (CP-F-04 — it contains the exact threshold-and-gap material §3.1 protects); vtt/Ecosystem_Module_Designs.md, vtt/epic_presentation_smooth_fog.md, studio/Cinematic_Replay_Engine_Proposal.md (design intent and proposals — no owning ADR, must not read as specification); marketplace/CLAUDE_CODE_MVP_PROMPT.md, marketplace/MARKETPLACE_HANDOVER_PROMPT.md (internal working prompts); and the disclosure review itself (its own tier rule applied to itself).

  5. Authorization is file-granular (CP-F-06). No prose-region scrubbing exists and none is claimed by this ADR. A file needing partial withholding is excluded whole until it is restructured, or until a prose-scrubbing decision with its own grammar and fail-closed tests exists. That decision is not pre-authorised here.

  6. Audience condition (CP-F-01). This ADR authorises rendering, not reachability. The built site’s audience today is repository collaborators (a private-repo CI artefact), for whom rendering discloses nothing beyond existing access. Any deployment reachable beyond repository collaborators requires a further ruling that (a) dispositions Quantity_Registry.md’s operational-threshold rows against §3.1’s calibration argument, and (b) completes the content-level disclosure pass the review explicitly did not perform on the Marketplace/Studio whitepapers and the decision records. The Epic C deployment ADR is where both come due; it must cite this clause.

Alternatives Considered and Why Rejected

  • Copy the corpus into apps/descent-docs/src/content/. The W6 drift defect as an architecture: two trees, one authoritative, diverging silently. Already rejected in principle by ADR-112 clause 3; rejected here operationally so nobody re-proposes it as a “simpler first step” — it is simpler precisely until it is wrong.
  • Symlink/junction docs/ into the content directory. Fails on this project’s own primary platform: Windows checkouts default core.symlinks off and junctions don’t travel through git; the build would work on CI and lie on the machine the owner uses.
  • Directory globs over authorised folders. Fail-open: publishes the next file dropped into the folder, and would have published two internal prompt files today (CP-F-03).
  • Render everything, banner the history. A banner survives on the page and dies in the search result; Pagefind surfaces fragments without their banners, which is exactly CP-F-02’s mechanism. Rejected because the mitigation does not reach the exposure.

Consequences (including negative)

  • Every new corpus document requires a manifest row to render. Deliberate friction — it is the reviewed act — but a document can now be written, merged, and invisibly absent from the site. The two-sided route check cannot catch what was never claimed; the release-notes habit has to.
  • Excluded files are invisible on the platform, including the W-files that are the decision records for 22 ADRs. A reader following provenance must leave the site for the repository. Accepted: provenance-followers are collaborators, and the alternative was CP-F-02.
  • The loader and route check are new test surface owned by the platform context.
  • The manifest is a second place that lists corpus files (after docs/README.md’s organisational tables). Accepted with mitigation: the manifest states renderability, the README states authority — different claims, so neither restates the other; the route check keeps the manifest honest against dist/, and nothing needs to keep it synchronised with the README because they answer different questions.

Rights-holders (ADR-079)

None. The decision renders documents this repository already retains; no new class of data about any person is created or retained. (The CI artefact inherits the repository’s existing access control; retention is 7 days.)

Enforcement

  • The manifest/route gate: a check script (sibling of docs-schema-scrubber.mjs, runnable standalone per ADR-112’s exit-cost rule) asserts two-sidedly that the page set under dist/ equals the manifest-derived expectation — an unexpected page fails, and a manifest row yielding no page fails (vacuity guard). Wired into docs-platform-ci.yml after the build step.
  • The read-only witness: a CI step asserts git status --porcelain -- docs/ is empty after the build. Cite-never-copy stops being a reviewed property and becomes a build-failing one (P3).
  • The token belt stays: the existing --check dist scrubber pass now also covers rendered corpus prose.
  • Checks land tests-first (working agreement 6), each case a deliberate mutation that turns the gate red, per the test_bare_number_lint.py precedent.
  • Clause 6 has no CI check and does not claim one (P4, honestly): no workflow exists for a check to bind. Its enforcement point is structural absence — astro.config.mjs carries no site and the workflow no deploy step — plus the named obligation that the Epic C ADR cite clause 6. Adding a deploy step without that citation is the reviewed act this clause exists to make visible.