# Docs Why the prose documentation is maintained the way it is. The actionable rules are in [CONTRIBUTING.md](../CONTRIBUTING.md); this file records the rationale. ## Validating Markdown code fences #### Decision (2026-11) Compile every `ts / `typescript fence in the prose docs into a real `node:test` case under `src/doc-test/__generated__/`, typechecked by a scoped `tsc` project and executed by `node --test`. #### Why - A documented example is a promise about the API. Left unchecked it drifts the moment a signature changes, and a reader copies broken code. - The repo runs TypeScript 7, the native/Go compiler. It exposes **no legacy JS compiler API** (`ts.createProgram`, `ts.transpileModule`, `ts.createSourceFile` are all `undefined`; `Object.keys(require("typescript"))` is `["version", "versionMajorMinor"]`). So `@typescript/vfs`, the type-aware `eslint-plugin-markdown` and `docs-ts` / `@effect/docgen` cannot run here. - `node --check` parses as JS and rejects valid TS type annotations, so it is not a gate. The only faithful validator is the `tsc` **CLI**, which means emitting real `.ts` files and letting the existing `check:tsc` / `node --test` pipeline judge them. #### Rejected - **A packaged doc-test tool** (see above) — no usable compiler API on TS 7. - **Embedding a typecheck in the generator** — duplicates the gate, and would not exercise the repo's own resolution. - **`node --check`** — wrong language level. #### Known issue - Scanned sources are hard-coded to `README.md` and `CONTRIBUTING.md`. A `docs/` + `examples/` list is the natural extension; `development/` must never be scanned (its fences are illustrative, not compilable). - The generator hoists and merges leading imports, rewrites `tiny-pattern-ts` to the `#test-tiny-pattern-ts` source alias, and rejects an example that imports `node:assert` / `node:test` (the prelude already binds both). Titles are the immediately preceding paragraph; a fence with no such paragraph is a fatal error, which keeps every example described. - `oxlint src/doc-test` reports "No files found" because the generated `*.test.ts` are gitignored. That is cosmetic: the files are still typechecked and run. - The generated tests are `*.test.ts`, which c8's default excludes already keep out of the `--100` gate. Do not add an `--exclude` for them: passing any `--exclude` replaces the defaults, so every hand-written test file and `__tests__/` helper re-enters coverage and the gate fails. - Generated examples must not run under `c8`: an example could cover a line no hand-written test reaches, so the coverage gate would pass on documentation alone. `test:coverage` therefore runs c8 over the tracked tests only (`git ls-files 'src/*.test.ts'` — the generated files are untracked), and `test:doc` runs the examples in a separate process without c8. `test:ci` chains the two, so correctness and coverage stay independent. - The CI `build` job runs `npm run create:doc-tests` as an explicit step before `npm run test:ci`, not a `pretest:ci` lifecycle hook: the hook hides the step from the job log and makes `test:ci` behave differently under npm than when run directly.