Writing docs
The docs are built, not merely written. Three kinds of drift fail the build; one kind cannot, and that is the one to watch.
Facts are read, never restated
A second copy of a fact is the copy that goes stale — and it goes stale in the flattering direction, still claiming a feature works months after it stopped.
| Fact | Read from | Written as |
|---|---|---|
| Is this done, partial or planned? | the status table in API.md | <Status of="signal" /> |
| The guard scripts | guards/architecture/data.ts | <Guards /> |
| The pipeline stages | guards/architecture/data.ts | <Pipeline /> |
| Shared-table writers and readers | guards/architecture/data.ts | <TableRoles /> |
Do not hand-write any of those. A <Status of="…" /> naming a row that is not in
API.md is a build error, and that is the feature: you cannot document a surface
the tracking table has never heard of, and renaming a row makes every page pointing at
it fail loudly rather than quietly lose its badge.
What the checks catch
bun run docs-check # examples, API coverage, frontmatter
bun run doc-lint # citations across every Markdown file in the tree
bun run docs:build # all of the above, then the real build
Examples compile. Every ```ts and ```tsx block is extracted and typechecked
under the repo's own tsconfig — same strictness, same jsxImportSource — so an example
is held to exactly what a reader's project would hold it to. Rename an export and the
docs fail in the same run as the code.
A fragment that cannot stand alone (one importing a project's own ./state.ts) gets
```tsx no-check. Those are counted and reported, never silently skipped. If that
count climbs, the examples are drifting toward decoration.
The public surface is covered. src/index.ts defines the public-versus-internal
line, so it defines what the docs owe a reader. The check runs both ways, and the
reverse direction fails hard: a page teaching import { foo } from "dziry" when foo
is not exported is a lie a reader will act on.
Citations resolve. src/runtime/signal.ts:140 is checked at build time by the same
resolver doc-lint uses. Set DZIRY_SOURCE_URL and they become links.
Frontmatter is present. Sidebars are autogenerated, so a page missing
sidebar_position sorts arbitrarily and one missing title gets a name guessed from
its filename.
What no check can catch
Whether the prose is true.
Every check above is mechanical: the citation resolves, the example compiles, the name is exported. None of them read the sentence.
That is not hypothetical. On 2026-07-31 a cited comment in this repo turned out to state
the opposite of what the doc claimed, and an API design was built on it before anyone
opened the file. doc-lint was green throughout, because the line number was fine.
So a clean run means "nothing dangling, nothing that fails to compile". It does not mean the docs are right. When you change behaviour, read the page.
House style
- Document what is done. Check
API.mdfirst. A planned surface gets a badge and, if it needs prose at all, an admonition — not present-tense description of behaviour that does not exist. - Explain the constraint, not just the rule. Nearly every restriction in dziry follows from one thing: anything the runtime reaches by name must have a name at build time. A reader who has that can derive the rest.
- Name the failure. "This compiles cleanly and never updates" is more useful than "this is incorrect", because it says what the reader will actually see.
- Prefer an example that compiles over one that reads well.
- Do not quote measurements. Point at the script. A percentage in prose is a memory of a measurement.
- Pages using components must be
.mdx. Plain.mdis CommonMark and renders<Status/>as literal text.
After a refactor
bun run docs-check— phantom imports and broken examples surface immediately.bun run doc-lint— citations that moved.- Read the pages the change touched. This is the step that finds the real damage, and the one there is no tool for.
Versioning
Set up but unused — there is no released version yet. bun run docs:version 0.1
snapshots the current pages when there is something to freeze. Running it early doubles
every future edit.