---
name: documentation
description: "Use when creating or substantively editing ANY markdown doc — invoke BEFORE writing. Templates + style authority for technical docs: LLDs, ADs, plans, changelogs, READMEs, runbooks. Owns the tense rule: truth docs state the present; history only in dated records. Baselining is the maintenance agent."
---

# Technical Documentation Skill

**This skill is a template & generator library, not an orchestrator.** It supplies the templates, the frontmatter-derivation generator, and the co-authoring workflow that _agents_ call to produce individual docs. It does **not** baseline, audit, or reconcile a repo's doc tree — that is the **`maintenance` agent (baseline mode)**, which calls this skill for each template it scaffolds. Reach here to generate _one_ doc from a template; reach for the `maintenance` agent (baseline mode) to bring a whole repo into shape.

Document contracts and architecture, not implementation details. Show function signatures and API shapes — never paste full implementations (they rot fast). Use ASCII diagrams over prose for flows. Only document state at persistence boundaries (Redux persistence, DB schemas, API shapes), not component-level or transient state. Mark deprecated fields with `// UNUSED:` comments stating why the field is dead now — never where it went or when it dies. Breaking changes belong in changelogs only, not module docs.

---

## Where a file's documentation lives

**In `docs/`, never in the file's comments** — and this skill is the half of that rule that makes it safe. A source file carries a repo-relative path header of two or three present-tense sentences and one third-person sentence above each non-obvious function; the shape is the harness's `CLAUDE.md` § File Structure, which is canonical and is not restated here. What lands **here** is everything that shape displaces: design rationale, the alternatives weighed, the measurement behind a constant, the reason an obvious-looking change is wrong.

**Cutting a comment is a move, not a deletion.** A claim taken out of a source file is owed one of two things before the cut is done, and the choice is per claim, not per file:

| The claim is | What it earns |
| --- | --- |
| Worth keeping, and its doc does not state it | A section in the **owning feature doc** (the ownership index resolves which), carrying the claim and the argument for why the codebase is worse without it — never the comment's wording, which was written for a reader holding the code |
| Already stated in its doc | Nothing — but you **open that doc and find the sentence** before saying so. An assumed duplicate is how a claim is lost |
| History, a ticket or decision-record reference, a dated note, a divider, or narration of code that no longer exists | Nothing, and no record. A record lives in `docs/history/`; these are not claims about the present |

**Count what you cut, and prove the cut was only comments.** A comment sweep states comment lines before and after per file, so a drop nobody accounted for is visible as a number rather than as a feeling. [`scripts/comment-strip.mjs`](scripts/comment-strip.mjs) turns that from a claim into a check: it strips comments from both revisions — honouring strings, template and character literals, regex literals, raw strings and JSX comment braces — and diffs what is left, so a sweep that quietly changed executable code cannot pass as a cleanup. `comment-strip.mjs --rev <before> --to <after> <paths…>` exits 0 for "no executable code changed" and 1 naming the file and the first diverging line. Run it over any pass that touches more than a handful of files; a sweep without it is an assertion.

**The mechanical half of the shape is checked, not remembered.** [`scripts/source-lint.mjs`](scripts/source-lint.mjs) `--changed` reports a header that names another path or none, a change whose added lines are over a quarter comments, and the four shapes the convention deletes outright — narration, ALL-CAPS emphasis, an over-long header, a comment block that has become a document. Block-severity findings must be fixed; the escape hatch is a `source-lint-disable: <id>` comment with the reason in a second comment beside it, and the only finding that legitimately takes one is a booby trap. The harness detector raises a bookkeeping row for the same condition, so an unfixed breach outlives the session that wrote it.

**Never convert a booby trap into a one-liner.** A comment whose subject is a danger on an edge that looks safe — a pinned dependency's limitation, a measurement that justifies a narrow allowlist, a two-clock interaction — is load-bearing where it sits, because the reader who needs it is mid-edit and will not go looking. Compress it if it rambles; do not relocate it. The tell is that the comment names a version, a measurement, or a condition under which it expires.

---

## Present tense, always

**A document states what is — never how it got there, and never how far along it is.** Write as if the current design had always been the plan. This is the most-violated rule in the skill, so it sits at the top; it applies to every document written or edited through this skill, whatever the template.

| ❌ Strip this | ✅ Write this |
| --- | --- |
| "Build Sequence phases 0–4 are complete." | Nothing — progress lives in the plan/backlog record |
| "There is no standalone `skills/components/` — treat any reference to it as stale." | Nothing — fix the references; state only the present design |
| "Folded in from the `NuStack-X` repo (2026-06-12)." | "Vendor-connector catalogue; one subpath per vendor." |
| "was X, now Y", "the old worker", "Phase 2 softened this", "v1 → v2" | The current fact, with no predecessor named |

The journey — dates, migrations, progress, supersessions — lives only in records (`docs/history/`); when reality changes, a truth document simply becomes correct. The full strip-list and the record-side rules: [`references/style-guide.md`](references/style-guide.md) § The Two Natures — Tense & History.

**A date in a truth document is a defect, and the list of exceptions is closed.** Not a softened preference: a truth document carries no dated line at all, because a date answers *when did this happen*, which is a record's question, and a reader who needs the answer is reading the wrong file. The four exceptions, and there are no others: the frontmatter's tooling keys (`last-reviewed:`, `last-updated:`, `rev:` — written by tooling, never by you), an Architecture Decision's **Decision Changelog** tail, `docs/structure.md`'s **Health Log** tail, and a dated path inside a link or code span, which is a *citation* of a record rather than a claim about the present.

**Cite a record by what it is, never by when it was.** `the dev reset runbook` links to the file; "the 2026-09-13 reset" states a date the reader must now keep in their head and that goes stale as soon as a second reset happens. The link carries the date in its path, where it belongs, and the prose stays true forever.

**A verification stamp is a record, not a status line.** "Driven 2026-09-13 · PASS" in a behavioural checklist is the clearest instance of this defect: it reads as documentation and is actually a dated event. The section states what must be true and which suite test guards it; *that* somebody drove it on a Tuesday belongs to the changelog entry of the unit that drove it, and staleness is then visible from something that re-checks itself — a pin that runs in CI — rather than from a date nobody re-stamped. A section with no automated guard says so in its trace, which is a stronger signal than an old date, because it cannot silently become wrong.

The mechanical subset of these rules is checked by [`scripts/doc-lint.mjs`](scripts/doc-lint.mjs) — run it over changed docs before finishing. Block-severity findings must be fixed; `<!-- doc-lint-disable: <id> -->` with a reason beside it is the escape hatch. Two invocation traps: the disable comment takes **bare rule ids only** — prose inside the comment silently breaks the parse, so put the reason in a second comment beside it — and the script grades truth-vs-record scope by matching `docs/(history|research|memory)/` against **the path string it is given**, so always hand it a repo-root-relative path; linting a record from inside its own folder grades it as a truth document.

---

## The Two Natures

Every document is exactly one of two things. Decide which before writing a word — every rule in this skill derives from the split.

| | **Truth document** | **Record** |
| --- | --- | --- |
| States | Absolutes — how the system is, **now** | What happened — a dated account of activity |
| Named by | Topic: `auth.md`, `AD-003-persistence.md` | Date: `YYYY-MM-DD-slug.md` (or append position) |
| On change | Body rewritten in place the moment reality changes | Never edited — frozen at completion or append-only |
| Read to | Act correctly today | Trace provenance, diagnose, audit |
| Lives in | `docs/features/`, `docs/architecture/`, `docs/operations/`, `docs/tests/`, READMEs | `docs/history/` (plans, changelog, brainstorms, research, migrations), `docs/research/`, `docs/memory/` |

The two guarantees are complementary and both break on mixing: a reader holding a **truth document** must never need history to interpret it; a reader holding a **record** must be able to trust it was never rewritten. So the natures never share a document body.

- **Truth documents never narrate history** — the § Present tense, always rule above, applied to this nature. When the system changes, the change-story goes to `docs/history/` (a changelog entry) and the truth document simply becomes correct in present tense. When you find historical narration in a truth document, rewrite it to the present-tense fact and let the record carry the story.
- **Records are never edited to match the present.** A completed plan stays wrong-in-hindsight; a changelog entry describes close-time state; a scope change gets a *new* dated record that links back, never an edit to a frozen one.
- **Two sanctioned bridges — head is truth, tail is provenance.** An **Architecture Decision (AD)** ends with a **Decision Changelog** (for a decision, the journey — what each revision reversed and what triggered it — is part of the reasoning), and `docs/structure.md` ends with the maintenance agent's **Health Log** (tooling-stamped audit rows, the structural cousin of `last-reviewed:`). In both, the body stays current-truth-only; the tail carries the dates. No other truth document carries dated history.
- **Truth documents cite records for the _why_, never the _when_.** `Provenance: AD-002` is good; "decided in AD-002 and shipped 2026-06-13" is not. Routing/index docs point only to truth documents as a topic's authority — never a plan or changelog entry as the "read first" source. (An index _of the records themselves_ — a History section listing `plans/`, `changelog/` — is sanctioned.)

**No empty stubs.** Never leave a template section as an empty placeholder — omit any section with nothing real to say (each template declares its required core; everything else is optional). Write an explicit "None" only where the absence itself is information (e.g., a plan's Cross-Module Contracts Affected, an LLD's Ownership Conflicts). An empty table stub reads as "not yet researched"; an omitted section reads as "doesn't apply" — keep those meanings distinct.

**No redirect docs.** When moving a doc to a new location, delete the old file entirely. Update all references (cross-doc links, memory files) to point to the new path. Never leave a stub file that says "Moved to X" — redirect docs rot, clutter the repo, and create false search hits.

---

## Routing

| Request | Route |
| --- | --- |
| Baseline / reconcile docs | `maintenance` agent (baseline mode) — it calls this skill per template |
| Document feature/module (LLD) | [`templates/feature.md`](templates/feature.md) |
| Document data flow / state / Redux | [`templates/feature.md`](templates/feature.md) — they are sections of the feature doc |
| Document a workspace package (`docs/architecture/packages/{package}.md`) | [`templates/package.md`](templates/package.md) — the **residual** owner of `packages/{name}/**`: its manifest, build, entry, layout and every behavior no system or feature doc claims at higher specificity |
| Document one system spanning packages (`docs/architecture/systems/{system}.md`) | [`templates/system.md`](templates/system.md) — one machine end to end; its `owns:` names the machine's subtrees, narrower than the package glob, so it wins by specificity. Not a screen — a system doc written as a giant feature doc is the mistake it prevents |
| Create/update changelog entry | [`templates/changelog-entry.md`](templates/changelog-entry.md) (record — one per unit of work; carries the refinement fields, frozen inside its commit, distilled by the `retro` agent) |
| Create/update plan | [`templates/plan.md`](templates/plan.md) (record) |
| Record/revise architecture decision | [`templates/architecture-decision.md`](templates/architecture-decision.md) — one AD per subject; revisions update it in place |
| System at a glance | [`templates/system-overview.md`](templates/system-overview.md) |
| Domain model | [`templates/domain-model.md`](templates/domain-model.md) |
| Glossary | [`templates/glossary.md`](templates/glossary.md) |
| Non-goals | [`templates/non-goals.md`](templates/non-goals.md) |
| Catalog the API surface | [`templates/api-reference.md`](templates/api-reference.md) |
| Document env vars / config | [`templates/environment-registry.md`](templates/environment-registry.md) |
| Security / threat model | [`templates/security-overview.md`](templates/security-overview.md) |
| Testing strategy | [`templates/testing-strategy.md`](templates/testing-strategy.md) |
| What a person sees and can do on a surface (UX behavior) | [`templates/ux-behavior.md`](templates/ux-behavior.md) — decides observable behavior BEFORE the code; per-surface in `docs/features/{surface}-ux.md` owning the component globs, cross-cutting in `docs/architecture/`. Not the checklist, which only verifies it |
| Behavioral checklist for one feature (`docs/tests/{feature}.md`) | [`templates/behavioral-checklist.md`](templates/behavioral-checklist.md) — the verification gate's driving script, and the source its suite tests are drawn from |
| Acceptance criteria for one capability (`docs/acceptance/{capability}.md`) | [`templates/acceptance-criteria.md`](templates/acceptance-criteria.md) — the promises the built product must keep, as stable `AC-n` keyed assert lines verification surfaces parse as claims |
| Recurring procedure/setup | [`templates/operations-procedure.md`](templates/operations-procedure.md) |
| Cold-start build order (implementation map) | [`templates/implementation-map.md`](templates/implementation-map.md) — authored by the executing session at build start, updated in place as waves complete |
| Capture mid-execution knowledge (`docs/history/journal.md`) | [`templates/journal.md`](templates/journal.md) (record — append-only; a row resolves by landing in its owning doc) |
| Bookkeeping work queue (`docs/operations/bookkeeping.md`) | [`templates/bookkeeping.md`](templates/bookkeeping.md) — machine-read row grammar; appended by hooks and sessions, drained by the `bookkeeper` agent, walled at Stop and commit |
| One-shot migration/upgrade | [`templates/migration-runbook.md`](templates/migration-runbook.md) (record) |
| Add to protected files | [`templates/protected-files.md`](templates/protected-files.md) |
| Create/update root README | [`templates/root-readme.md`](templates/root-readme.md) |
| Scaffold a standard doc (docs index, `structure.md`, CHANGELOG, backlog, memory) | [`templates/Readme.md`](templates/Readme.md) — the index names each scaffold file |
| Derive/update an owning doc's `owns:` (feature, package, system) | [`references/frontmatter.md`](references/frontmatter.md) |
| Update existing doc | § Editing an existing doc (below) |
| Instruction file (`CLAUDE.md`, a rule, an agent, skill prose) | [`references/instruction-style.md`](references/instruction-style.md) — not these templates; every-load prose stays lean, and the present-tense rule still applies |
| Author or restructure a **skill** (fit, triggers, shape, anatomy, shipping) | The skill-authoring field guide — the `etna` repo (`nurixlabs/etna`), folder `docs/playbooks/skills/`, chapters `00`–`10`. **No etna checkout available ⇒ ask the user for access to the etna repo, or proceed from first principles** with [`references/instruction-style.md`](references/instruction-style.md) governing the prose. This row is the only place the playbook is referenced |
| **No template matches** | [`references/untemplated.md`](references/untemplated.md) — shape decisions + the co-authoring workflow |

---

## Editing an existing doc

"Substantive" = anything beyond typo, link, or formatting fixes. On a substantive edit:

1. **Determine the nature first.** A frozen record (a `Status: Complete`/`Abandoned` plan, a changelog entry, an executed runbook) is never edited — write a new dated record that links back.
2. **Preserve structure, not violations.** Keep the doc's existing section shape, but every section touched sheds history narration, progress markers, and stale tripwires on the way through (§ Present tense, always).
3. **Stamp and re-derive.** A substantive feature-doc update bumps `last-reviewed:`; if the documented scope moved, update `owns:` ([`references/frontmatter.md`](references/frontmatter.md)).

---

## Naming & Lifecycle

| Document | Nature | Name | Lifecycle |
| --- | --- | --- | --- |
| Feature (LLD) | Truth | `kebab-case.md` | Living; `status:` `draft` → `active` → `deprecated` |
| Package | Truth | `{package-folder}.md` | Living; `status:` as the feature doc |
| System | Truth | `{machine}.md` | Living; `status:` as the feature doc |
| Architecture Decision | Truth | `AD-NNN-kebab-case.md` | Living; `Proposed` → `Accepted` → `Deprecated`; revisions rewrite the body + append to its Decision Changelog |
| Steering docs (overview, domain model, glossary, non-goals, security, testing strategy) | Truth | fixed names | Living |
| API reference / environment registry | Truth | fixed names | Living |
| Operations procedure | Truth | `kebab-case.md` | Living |
| Protected files | Truth | `protected-files.md` | Living |
| Plan | Record | `YYYY-MM-DD-kebab-case.md` | Freezes at `Complete` / `Abandoned` |
| Changelog entry | Record | `YYYY-MM-DD-kebab-case.md` | Frozen; master index append-only |
| Migration runbook | Record | `YYYY-MM-DD-kebab-case.md` | Freezes once executed, outcome stamped |
| Research | Record | in progress: `docs/research/{topic}/` · harvested: `docs/history/brainstorms/{topic}/` | Frozen at harvest |
| Memory | Record | `{agent-name}.md` | Append-only dated ledger |

---

## Frontmatter Derivation

**Frontmatter is compulsory on every document this skill authors** — `type:` (the genre) on every
doc, plus the genre's own keys beside it; a document without its frontmatter fails the skill's
contract and is not done. **There is no frontmatter-less genre.** A feature or UX doc carries the
full set — `owns:` (required), `module:` (optional), `keywords:` (required), `status:` (required),
`applies:` (the architecture docs it obeys; omitted when none), optional `last-reviewed:`. A
**package** doc (`docs/architecture/packages/`) and a **system** doc (`docs/architecture/systems/`)
carry the same set minus `module:` — a package is already a bounded unit and a machine spanning
packages belongs to no single context. A behavioral checklist (`docs/tests/`) carries
`type: checklist` and `verifies:` — the doc whose promises it proves. An acceptance-criteria doc
(`docs/acceptance/`) carries `type:` and `status:`.

**The frontmatter is where the docs tree's edges live**, and a doc that declares none is a node
nothing can reach: no change implicates it, so it rots without the rot being detectable. Deriving
every edge — `owns:` from code, `verifies:` and `applies:` from the docs they answer to — is
[`references/frontmatter.md`](references/frontmatter.md), and it carries the rule the whole graph
rests on: **declare once at the consuming end, generate every inverse.** Every doc create or
update, and the `maintenance` agent's baseline pass, route there.

---

## Resources

| File | Use For |
| --- | --- |
| `templates/` | One file per template, indexed by [`Readme.md`](templates/Readme.md) — feature (LLD), changelog, plan, AD, steering docs, API reference, environment registry, operations procedure, migration runbook, protected files, READMEs, and the baseline scaffolds |
| `references/style-guide.md` | Writing rules, formatting conventions, the two-natures tense rules |
| `references/instruction-style.md` | The style for instruction files (`CLAUDE.md`, rules, agents, skill prose) — the every-line test, armor vs. narration, voice, negative space |
| `references/frontmatter.md` | Deriving a feature doc's `owns:` / `module:` / `keywords:` / `status:` from code |
| `references/untemplated.md` | How to write a document no template covers (nature, placement, shape) + the co-authoring workflow |
| `references/examples.md` | Good vs bad documentation examples |
| `scripts/doc-lint.mjs` | Mechanical checks over written docs (tense narration, dates, stubs, redirects) — run over changed docs before finishing |
| `scripts/source-lint.mjs` | Mechanical checks over source **comments** (header path identity, the added-line comment ratio, narration, shouting) — run `--changed` before finishing any change that writes source |
| `scripts/comment-strip.mjs` | The proof a comment sweep changed no executable code: strips comments from two revisions and diffs what is left. Exit 0 is the claim; exit 1 names the file and the first diverging line |
