write-docs
The TitusKirch docs format — one opinionated, stack-agnostic convention for a project's docs/ tree, the same in every repo (including client projects). This skill owns the convention; the page mechanics live in REFERENCE.md, the skeletons in templates/. Pages are plain Markdown with title + description frontmatter and numeric-prefixed paths — a clean tree any file-based docs generator can render, index, and feed to an LLM.
Portable is the default; a rendered site is an opt-in mode. A repo that publishes its docs/ through a rendering theme sets docs.render in the config, which flips five page rules and nothing else — the render mode. It is declared, never detected, so a repo that has not written the key gets the portable tree above, unchanged.
Opted out? If the repo config sets docs to false, this skill is disabled for the repo — stop immediately (including the proactive trigger) and tell the user docs are turned off in .tituskirch-skills.json. An absent docs block is not disabled — that falls back to defaults/detection. Check .docs == false on the resolved config before any job — and before indexing .docs.*. A missing jq or config exits non-zero too, so a pass is not evidence the config was read.
Jobs — pick by repo state + intent
| State / intent |
Job |
docs/ missing |
scaffold |
docs/ exists + "document feature X" |
route/add |
| "update / align / migrate / reconcile docs" |
reconcile |
Optional verb shortcuts: /write-docs init, /write-docs add <topic>, /write-docs reconcile. Otherwise infer from state and the request. Always: plan → confirm → apply.
Proactive trigger — don't wait to be asked. Once a feature has passed all its reviews and reached final approval (signed off or merged), engage this skill yourself and run the route/add job for that feature. Trigger on final approval, not on implementation finished — code still facing review is too early, and a feature that gets reworked shouldn't be documented twice. The write still follows plan → confirm → apply. An ADR has its own proactive moment, on its own trigger: when a decision earns one.
What belongs in docs at all
Code says what happens. Docs say why. An agent reads the code, so a page that narrates what the code does is a slower, staler copy of something already open in the editor — and if the code needs narrating, the fix is the code, not a page about it.
Before writing anything, ask what the reader could not recover by reading the source. Exactly three answers survive that test:
| Code cannot express |
Because |
Goes to |
| The roads not taken |
What was rejected, and why, leaves no trace in what shipped |
adr, concepts |
| How new work here is done |
The intended approach for the next change is not a property of the current one |
conventions, guides |
| Where to start |
A repo has no first line; which seam to enter by, and how the parts relate, is not in a file |
concepts, getting-started |
Everything else — what a function does, which options exist, what a command prints — either belongs in the code and its own reference output, or is already in a file that owns it. If a planned page fits none of the three rows, that is the answer: don't write it.
Routing matrix — what you changed → page type → section
| You added / changed |
Page type |
Section |
| A user-facing capability's usage |
guide / tutorial |
guides |
| A repeatable task / "add a new X" |
how-to (ends w/checklist) |
guides |
| A subsystem, model, or how-it-works |
concept / architecture |
concepts |
| A lookup value: env var, CLI flag, config, API |
reference entry |
reference |
| Setup / install / first-run change |
(update existing) |
getting-started |
| A run / deploy / maintain procedure |
how-to or concept |
operations |
| A project-specific rule or pattern |
concept |
conventions |
| An architectural decision + its reasoning |
ADR (own schema) |
adr |
A real feature usually spans several types: how-it-works (concepts) + usage (guides) + lookup values (reference). "How it works" means the shape and the reasoning — the seams, the invariants, why it is built this way — never a walk through the implementation, which the code states better and keeps current for free.
The reference row is the one to challenge. Lookup values earn a page only where nothing machine-readable already holds them — an HTTP API with no published schema, env vars with no .env.example. Where a schema, a manifest or --help is the real answer, the reference page is one sentence naming it plus whatever it cannot say (which values are safe to change, which combinations conflict). A transcribed option table is the single most common stale page in any repo. Lead with how-it-works and how-to-use; push every lookup value to reference and link to it. Page type is implied by section + template — it is not a frontmatter field. Catalogue, core sections and presets: REFERENCE.md.
ADRs are the standing exception. They live in docs/99.adr/ (fixed prefix, flat), are named NNNN-title.md — a permanent decision id plus an imperative verb phrase — carry status + date on top of the house frontmatter, run Context / Decision / Consequences (Nygard) with an optional Alternatives considered, and are append-only — a reversed decision writes a new ADR and marks the old one superseded, never edits it; a decision that still stands but whose reasoning has been overtaken grows a dated addendum under its own ## Amendments, the one thing an accepted record may gain in place. A concept page explains how a thing works today; an ADR records why it was chosen, then. Full contract: REFERENCE.md.
When a decision earns an ADR
The matrix above says where a decision goes; this says whether there is one to record. Ask it before routing — the adr row is the only one whose page cannot be pruned later, because the log is append-only.
The threshold. A decision earns an ADR when it constrains work that comes later and its reasoning would otherwise be lost: a choice between real alternatives, a convention every part of the project has to follow, a trade-off that looks like a mistake until the reason is known. Both halves have to hold — a constraint whose reason is obvious from the code needs no record, and a well-argued call that binds nothing later is a comment, not an ADR.
Decisions that typically clear it: architectural shape · integration patterns between parts · technology choices carrying lock-in · boundary and scope decisions · deliberate deviations from the obvious path · constraints invisible in the code · alternatives rejected for non-obvious reasons. A list of the usual suspects, not a checklist — the threshold above is what decides.
Three signs of a certain ADR — hard to reverse, surprising without context, the result of a real trade-off. All three together are sufficient: write the record, no further argument needed. They are never necessary — the cheapest convention in the repo still earns an ADR if it binds later work and its reason would otherwise be lost, and gating on reversal cost alone would exclude exactly the decisions that are easiest to break by accident.
Size is not the gate, and never a reason to skip one. An ADR can be a single paragraph. The value is in recording that a decision was made and why — not in filling out sections. The required H2s stay, and any of them may be one sentence (contract).
What may go in it is the separate question — and the one append-only makes permanent. No sentence in an accepted record can be corrected, so every sentence has to still be true years from now. That rules out the state of the world (currently, is being, a version, a claim about what some tickets hold), anything transcribed out of a file, and any enumeration a later change will extend. Context carries the constraint that forced the decision, not an inventory of the project; a consequence that prescribes how future work is done is a convention and belongs in conventions/, where it can be edited. Freezing a fact deliberately is fine — write it in the past tense. The full test: REFERENCE.md.
Offer an ADR proactively once a decision clearly clears the threshold — same shape as the proactive trigger above: propose the record, plan → confirm → apply, and never write one unasked. Clearly is the bar; a borderline call is the human's to make, so name it and let them answer.
Scaffold — docs/ is missing
- Resolve the preset —
docs.preset config → detect from the repo (bin/CLI → cli, a published manifest — library, module, agent/skill set → package, a UI app → app, a server entrypoint with no frontend build → service, IaC → infra) → ask. The preset is the whole set of sections to scaffold; none is implicit. Several signals hit at once in most repos — a CLI that also publishes a library, a module shipping a demo app — so resolve by how the thing is primarily consumed, not by what the repo contains: invoked → cli, imported into another project → package, run and used → app/service, applied to an environment → infra. Genuinely co-equal → ask.
- Resolve the language, then the locales —
docs.language → root language → existing docs/repo language → en (see REFERENCE.md#config). That is also the tree's default locale, living at the source root with no folder of its own. docs.locales names the additional ones, each a folder inside the source (docs/de-DE/); absent means single-locale, exactly as before (Locales). When set, docs.instructions shapes the generated-docs wording (tone, house conventions) — additive only, never overriding the docs format or guardrails.
- Resolve the render mode —
docs.render, or portable. Read the key; never infer it from a dependency or a config file, and never offer to write it — a repo states its own rendering target, and detection reads the tree that most needs the mode as the one that needs it least. Portable is the answer whenever the key is absent.
- Plan the tree — the preset's sections, plus any its conditional column earns here; show it.
- Drop any section that would only redirect. For each planned section ask: does this repo have a page's worth of material that is not already canonical somewhere else — a README that covers install and first run, a committed schema that is the config reference, per-module docs that ship with their module? If not, the section's
index.md can only say "the real thing is over there", which costs a click and returns nothing. Leave it out, name it in the plan with the reason, and let route/add create it later when it has a page of its own. This applies to every section the preset names — a preset is the starting set, not an obligation.
- On confirm — create numeric-prefixed section dirs, each with an
index.md (frontmatter title + description, plain-text H1), plus a docs/index.md landing page. Generated docs are emoji-free. Skeletons: templates/, with the render mode's flips applied on copy where it is on. Then, for every locale docs.locales lists, write the same tree translated under docs/<locale>/ — same paths, same numeric prefixes, same slugs, with only title, description and body prose in that language.
A scaffold that produces one section is a success, not a failure — the tree's job is to hold what has no other home, and in a repo whose parts already document themselves that can be a single section. Numbering starts at 1. over whatever survives, so nothing looks missing.
Route / add — docs/ exists
- Run the routing matrix; list every page to create or touch.
- New page → next free
N.kebab.md in the section, frontmatter title + description, body from the matching template, then link it from that section's index.md. Under the render mode the same step drops the body H1, writes an absolute doc path, and leaves the section index to its page-cards component rather than gaining a bullet.
- How-tos end with a checklist. Add a
> [!NOTE] Status callout (see REFERENCE.md#status-marker) if the feature isn't shipped yet — the render mode writes that marker as its own callout component instead, because the blockquote form does not render there at all.
- Delta principle — don't restate what something else already documents; link it, and write the project-specific delta plus the glue. Three sources outrank a page here, in descending order of how badly a copy hurts:
- A file in this repo.
package.json's scripts, a workflow, a schema, a lockfile, a config — a doc that lists their contents is wrong the moment the file changes, and nothing will tell you. Name the file and what is surprising about it; never transcribe values, versions or option tables out of it.
- Anything the repo ships. A library, plugin or skill travels without
docs/, so it must carry its own reference — a second copy here is the one guaranteed to drift.
- An authoritative upstream source — framework docs, a dependency's README, a standard.
A strong guideline, not a hard block: cross-cutting glue, and the reason a thing is set the way it is, are exactly what no source file states.
- Updating an existing topic — the one-topic-per-page rule means there's usually exactly one page;
grep docs/ for the term, edit it in place, never open a second page on the same topic, and update affected reference tables + cross-links. Never for an ADR — a changed decision is a new ADR that supersedes the old one, and the ADR section's index.md decision log gets the row in the same change.
- Verify every fact against current code before writing.
- Every listed locale gets the same change, in the same plan. A page created or edited in the default tree is written to
docs/<locale>/ at the identical path for each locale docs.locales names — the section index.md link included. One change, every tree: leaving the translation for later is what puts them out of step, and the locale contract is that they never are. With docs.locales absent this step does not exist.
Reconcile — align existing docs to the convention
Desired-state, idempotent — like a --fix linter for the docs tree.
- Read the whole
docs/ tree fresh (it is live state — never cached).
- Resolve the render mode before diffing anything — it decides which convention the tree is being measured against, and two of the mechanical fixes below are destructive under the wrong one: they would strip the theme's frontmatter fields off the pages that need them and rewrite the mode's absolute doc paths into dangling relative ones.
- Diff against the convention and group the plan:
- Auto-fix (mechanical) — numbering gaps/dupes, missing
index.md, removed/unknown frontmatter keys, N.kebab.md filename normalization, unambiguous broken links.
- Prompt (value-needing) — a missing required field (
title/description): derive a candidate from the H1 / first paragraph and confirm.
- Import (value-needing) — a dedicated ADR directory outside
99.adr/ (docs/adr/ and near variants): decisions recorded in someone else's format, which every other category misses, so they are missing from the decision log while the log still reads as complete. Measure each file against the threshold first — it governs an adopted record exactly as it governs a written one, and this is the last moment it can be asked, because an append-only log cannot be pruned afterwards. What clears it is proposed as one operation — move, rename to NNNN-title.md, the log's row, the missing frontmatter, the body under the required H2s — applied only on confirmation. A record holding a decision and material the expiry test rejects is split in that same proposal — the decision into the log, the rest routed as an ordinary page, a fragment belonging on no page dropped once the human says so — and only where the two come apart with no sentence rewritten. What does not clear the bar at all is routed whole or reported and left alone, in the same plan. Detection rule, the threshold gate, the split's rules, the date derivation and what is report-only instead: REFERENCE.md.
- Report only — a how-to without a checklist, a page that fits no section, suspected duplication of an upstream source or of a file in the repo, any secret detected. Never auto-edited.
- Show the plan + diff; on confirm apply only structure + frontmatter. Never rewrite prose. Only touch files inside
docs/. Two exceptions, both scoped: an imported ADR, whose body is re-homed under the required H2s — a split sends part of it to a page, and a named fragment is dropped on explicit confirmation, so the author's sentences are moved or removed, never rewritten; and a locale tree, which is regenerated rather than reconciled.
- Regenerate every listed locale from the default tree. Where
docs.locales names a locale, the sweep rewrites docs/<locale>/ — structure derived from the source, title, description and body prose translated. This is the one place a reconcile writes prose, and it is scoped to the locale trees: the default-locale tree keeps prose untouched in full. Inside a locale root nothing is categorized and structural drift is never reported — it means the sweep had not run (the rules). With docs.locales absent this step does not exist.
99.adr/ is exempt — links and the decision log only. An ADR id is permanent, so never renumber one, never rename it to the dot-schema, never close a gap in the sequence (REFERENCE.md). A foreign ADR is outside that exemption: it was never accepted in this log, so giving it an id is a first assignment rather than a renumbering, and shaping it on entry is not an edit to an accepted record. A translated ADR is outside it too, in the other direction: the record is the source file, its translation is a rendering of one, so a locale tree's 99.adr/ is regenerated with the rest of that tree — ids, filenames and status copied across untouched.
Presenting the plan
Everything this skill puts in front of a human — plan, preview, candidate list, findings report —
is read once, in a terminal, and answered there. So every section of it renders on arrival,
with no interaction needed to reveal it: prose, lists, tables, fenced code.
Never fold content behind a control. <details>/<summary> is a browser widget, and a
terminal has no way to open it: the summary line prints and everything under it does not. The plan
then arrives as headings with nothing beneath them, and the failure is silent on both sides —
the skill believes it reported, and the reader sees no marker saying anything is missing, so a
human confirms a plan whose contents never reached them. What gets folded is whatever ran long,
which is to say the part the decision actually rested on. The same holds for anything else needing
a click: a tab strip, an accordion, a "show more".
Length is handled by shortening, never by hiding. This is a fixed rule of the skill, not a
per-run judgement, so it holds however long the list runs. Trim to what the decision needs, group
the rest by something the reader already thinks in (ecosystem, kind, verdict) with a count per
group, or split it across sections. What is left out is left out visibly: say how many, why,
and the exact command that shows the rest.
This binds what the skill presents, not what it writes. A <details> block inside a README, an
issue body, a pull request description or a docs page is rendered by a browser and is entirely
legitimate there. The rule is about the message a human reads to decide — never about the content
of a file.
Guardrails (inherited)
- Docs are never the source of truth — the repo is. Code says what happens; a page exists for the why, the rejected alternatives, and the way in — the three things code cannot express. Documentation that mirrors code or a file costs tokens on every read and starts lying at the next commit, so when a page would restate one, link it and write only the delta.
- Plan/preview first; apply only after confirmation. Respect plan-only / dry-run.
- Keep generated content attribution-free — no agent self-naming or
Generated with/🤖 lines.
- No secrets in generated docs — scan, warn, exclude.
- Only the requested action — nothing closed or changed unasked.
- No cache — the
docs/ tree is live state, always read fresh.
- A translated tree is generated, never authored. Listing a locale in
docs.locales binds this skill to maintain and translate it, and a sweep rewrites it from the source — so a correction made inside docs/<locale>/ does not survive. Corrections go to the source page, or to the wording guidance docs.instructions carries. A missing translation is legitimate and never a defect to fix (Locales).
Reference
- Open it where the body's routing matrix stops deciding for you — which sections a preset scaffolds, what frontmatter and status marker a new page carries, which type a slug is allowed to be, whether a subject belongs in a folder or a section, and what a reconcile may change rather than only report: REFERENCE.md.
- Open it before touching a repo that lists
docs.locales — which tree is the default locale, what a locale root may and may not be read as, what a sweep regenerates, and where the prose untouched invariant stops holding: Locales.
- Open it in full before writing or importing an ADR, every time.
99.adr/ is append-only, so the id, the naming, the required sections, what the body may hold and the supersession rule have to be right on entry — there is no later edit that repairs them: REFERENCE.md.
- Copy one per page you are about to create, matched to that page's type:
templates/. They are written for the portable default, so under the render mode apply that mode's flips as you copy — there is no second set of skeletons.
Gap report (mandatory final step)
If you used a section, page type, or preset not in REFERENCE.md, end the turn with a short note (Gap report: section "{x}" — no catalogue entry; added ad hoc.). Only report; don't edit REFERENCE.md yourself — the user folds gaps back in. A new slug is a real gap only when it names a missing type; a subject section (plugins, themes, integrations) is not — route its content into the type sections (nested if it needs grouping) and report that instead. Neither is a named non-section directory such as _partials/ — it is catalogued, so it never triggers a report. A locale root (de-DE/) is not a section at all, so it never belongs in this report either. If everything matched: Gap report: no gaps.
Report the gap, not the rule you had to break. A run whose pages need a form the convention forbids has hit the case the render mode exists for — say so and name docs.render, rather than reporting five separate rules as five separate gaps, or breaking them quietly. Under the mode, following its flips is the convention and is never a gap.
1---2name: write-docs3description: Scaffolds, extends, and reconciles a project's `docs/` tree in the TitusKirch docs format — one stack-agnostic convention shared across all repos, ADRs included (append-only records in `docs/99.adr/`). Routes by state — scaffolds when `docs/` is missing, adds to the right section when it exists, reconciles pages when asked (never rewriting prose). Always previews a plan, writing only after confirmation. Use when the user wants to write, add, scaffold, or update documentation, set up docs/, document a feature, record or supersede an architecture decision, or says things like "write the docs", "add a docs page", "document this", "reconcile the docs", "write an ADR", "record this decision", "Doku schreiben", "docs aktualisieren". Also trigger proactively once a feature has cleared review and final approval — when the work is settled, not when implementation ends — to document the shipped result, and offer an ADR when a decision clearly earns one.4---56# write-docs78The TitusKirch **docs format** — one opinionated, stack-agnostic convention for a project's `docs/` tree, the same in every repo (including client projects). This skill owns the convention; the page mechanics live in [REFERENCE.md](REFERENCE.md), the skeletons in [`templates/`](templates/). Pages are plain Markdown with `title` + `description` frontmatter and numeric-prefixed paths — a clean tree any file-based docs generator can render, index, and feed to an LLM.910**Portable is the default; a rendered site is an opt-in mode.** A repo that publishes its `docs/` through a rendering theme sets `docs.render` in the config, which flips five page rules and nothing else — the [render mode](REFERENCE.md#render-mode). It is **declared, never detected**, so a repo that has not written the key gets the portable tree above, unchanged.1112**Opted out?** If the repo config sets `docs` to `false`, this skill is **disabled** for the repo — **stop immediately** (including the proactive trigger) and tell the user docs are turned off in `.tituskirch-skills.json`. An _absent_ `docs` block is **not** disabled — that falls back to defaults/detection. Check `.docs == false` on the resolved config before any job — and before indexing `.docs.*`. A missing `jq` or config exits non-zero too, so a pass is not evidence the config was read.1314## Jobs — pick by repo state + intent1516| State / intent | Job |17| :------------------------------------------ | :------------ |18| `docs/` missing | **scaffold** |19| `docs/` exists + "document feature X" | **route/add** |20| "update / align / migrate / reconcile docs" | **reconcile** |2122Optional verb shortcuts: `/write-docs init`, `/write-docs add <topic>`, `/write-docs reconcile`. Otherwise infer from state and the request. **Always: plan → confirm → apply.**2324**Proactive trigger** — don't wait to be asked. Once a feature has passed all its reviews and reached final approval (signed off or merged), engage this skill yourself and run the **route/add** job for that feature. Trigger on _final approval_, not on _implementation finished_ — code still facing review is too early, and a feature that gets reworked shouldn't be documented twice. The write still follows plan → confirm → apply. An ADR has its own proactive moment, on its own trigger: [when a decision earns one](#when-a-decision-earns-an-adr).2526## What belongs in docs at all2728**Code says what happens. Docs say why.** An agent reads the code, so a page that narrates what the code does is a slower, staler copy of something already open in the editor — and if the code needs narrating, the fix is the code, not a page about it.2930Before writing anything, ask what the reader could **not** recover by reading the source. Exactly three answers survive that test:3132| Code cannot express | Because | Goes to |33| :---------------------------- | :------------------------------------------------------------------------------------------- | :---------------------------- |34| **The roads not taken** | What was rejected, and why, leaves no trace in what shipped | `adr`, `concepts` |35| **How new work here is done** | The intended approach for the next change is not a property of the current one | `conventions`, `guides` |36| **Where to start** | A repo has no first line; which seam to enter by, and how the parts relate, is not in a file | `concepts`, `getting-started` |3738Everything else — what a function does, which options exist, what a command prints — either belongs in the code and its own reference output, or is [already in a file](#route--add--docs-exists) that owns it. If a planned page fits none of the three rows, that is the answer: don't write it.3940## Routing matrix — what you changed → page type → section4142| You added / changed | Page type | Section |43| :--------------------------------------------- | :------------------------ | :---------------- |44| A user-facing capability's usage | guide / tutorial | `guides` |45| A repeatable task / "add a new X" | how-to (ends w/checklist) | `guides` |46| A subsystem, model, or how-it-works | concept / architecture | `concepts` |47| A lookup value: env var, CLI flag, config, API | reference entry | `reference` |48| Setup / install / first-run change | (update existing) | `getting-started` |49| A run / deploy / maintain procedure | how-to or concept | `operations` |50| A project-specific rule or pattern | concept | `conventions` |51| An architectural decision + its reasoning | ADR (own schema) | `adr` |5253A real feature usually spans several types: how-it-works (`concepts`) + usage (`guides`) + lookup values (`reference`). **"How it works" means the shape and the reasoning** — the seams, the invariants, why it is built this way — never a walk through the implementation, which the code states better and keeps current for free.5455**The `reference` row is the one to challenge.** Lookup values earn a page only where nothing machine-readable already holds them — an HTTP API with no published schema, env vars with no `.env.example`. Where a schema, a manifest or `--help` is the real answer, the reference page is one sentence naming it plus whatever it cannot say (which values are safe to change, which combinations conflict). A transcribed option table is the single most common stale page in any repo. **Lead with how-it-works and how-to-use; push every lookup value to `reference` and link to it.** Page type is implied by section + template — it is **not** a frontmatter field. Catalogue, core sections and presets: [REFERENCE.md](REFERENCE.md).5657**ADRs are the standing exception.** They live in `docs/99.adr/` (fixed prefix, flat), are named `NNNN-title.md` — a permanent decision id plus an imperative verb phrase — carry `status` + `date` on top of the house frontmatter, run `Context` / `Decision` / `Consequences` (Nygard) with an optional `Alternatives considered`, and are **append-only** — a reversed decision writes a new ADR and marks the old one `superseded`, never edits it; a decision that still stands but whose reasoning has been overtaken grows a dated addendum under its own `## Amendments`, the one thing an accepted record may gain in place. A concept page explains how a thing works _today_; an ADR records why it was chosen, _then_. Full contract: [REFERENCE.md](REFERENCE.md#architecture-decision-records).5859## When a decision earns an ADR6061The matrix above says _where_ a decision goes; this says _whether_ there is one to record. Ask it **before** routing — the `adr` row is the only one whose page cannot be pruned later, because the log is append-only.6263**The threshold.** A decision earns an ADR when it **constrains work that comes later** and **its reasoning would otherwise be lost**: a choice between real alternatives, a convention every part of the project has to follow, a trade-off that looks like a mistake until the reason is known. Both halves have to hold — a constraint whose reason is obvious from the code needs no record, and a well-argued call that binds nothing later is a comment, not an ADR.6465Decisions that typically clear it: architectural shape · integration patterns between parts · technology choices carrying lock-in · boundary and scope decisions · deliberate deviations from the obvious path · constraints invisible in the code · alternatives rejected for non-obvious reasons. A list of the usual suspects, not a checklist — the threshold above is what decides.6667**Three signs of a certain ADR** — hard to reverse, surprising without context, the result of a real trade-off. All three together are **sufficient**: write the record, no further argument needed. They are **never necessary** — the cheapest convention in the repo still earns an ADR if it binds later work and its reason would otherwise be lost, and gating on reversal cost alone would exclude exactly the decisions that are easiest to break by accident.6869**Size is not the gate, and never a reason to skip one.** _An ADR can be a single paragraph. The value is in recording that a decision was made and why — not in filling out sections._ The required H2s stay, and any of them may be one sentence ([contract](REFERENCE.md#architecture-decision-records)).7071**What may go _in_ it is the separate question — and the one append-only makes permanent.** No sentence in an accepted record can be corrected, so every sentence has to still be true years from now. That rules out the state of the world (_currently_, _is being_, a version, a claim about what some tickets hold), anything transcribed out of a file, and any enumeration a later change will extend. `Context` carries the constraint that forced the decision, not an inventory of the project; a consequence that prescribes how future work is done is a convention and belongs in `conventions/`, where it can be edited. Freezing a fact deliberately is fine — write it in the past tense. The full test: [REFERENCE.md](REFERENCE.md#what-the-body-may-hold).7273**Offer an ADR proactively** once a decision clearly clears the threshold — same shape as the proactive trigger above: propose the record, plan → confirm → apply, and never write one unasked. _Clearly_ is the bar; a borderline call is the human's to make, so name it and let them answer.7475## Scaffold — `docs/` is missing76771. **Resolve the preset** — `docs.preset` config → detect from the repo (bin/CLI → `cli`, a published manifest — library, module, agent/skill set → `package`, a UI app → `app`, a server entrypoint with no frontend build → `service`, IaC → `infra`) → ask. The preset is the **whole** set of sections to scaffold; none is implicit. **Several signals hit at once in most repos** — a CLI that also publishes a library, a module shipping a demo app — so resolve by **how the thing is primarily consumed**, not by what the repo contains: invoked → `cli`, imported into another project → `package`, run and used → `app`/`service`, applied to an environment → `infra`. Genuinely co-equal → ask.782. **Resolve the language, then the locales** — `docs.language` → root `language` → existing docs/repo language → `en` (see [REFERENCE.md#config](REFERENCE.md#config)). That is also the tree's **default locale**, living at the source root with no folder of its own. `docs.locales` names the **additional** ones, each a folder inside the source (`docs/de-DE/`); absent means single-locale, exactly as before ([Locales](REFERENCE.md#locales)). When set, `docs.instructions` shapes the generated-docs wording (tone, house conventions) — additive only, never overriding the docs format or guardrails.793. **Resolve the [render mode](REFERENCE.md#render-mode)** — `docs.render`, or portable. **Read the key; never infer it** from a dependency or a config file, and never offer to write it — a repo states its own rendering target, and detection reads the tree that most needs the mode as the one that needs it least. Portable is the answer whenever the key is absent.804. **Plan the tree** — the preset's sections, plus any its conditional column earns here; show it.815. **Drop any section that would only redirect.** For each planned section ask: does this repo have a page's worth of material that is **not** already canonical somewhere else — a README that covers install and first run, a committed schema that _is_ the config reference, per-module docs that ship with their module? If not, the section's `index.md` can only say "the real thing is over there", which costs a click and returns nothing. Leave it out, name it in the plan with the reason, and let **route/add** create it later when it has a page of its own. This applies to every section the preset names — a preset is the starting set, not an obligation.826. **On confirm** — create numeric-prefixed section dirs, each with an `index.md` (frontmatter `title` + `description`, plain-text H1), plus a `docs/index.md` landing page. Generated docs are emoji-free. Skeletons: [`templates/`](templates/), with the [render mode](REFERENCE.md#render-mode)'s flips applied on copy where it is on. **Then, for every locale `docs.locales` lists**, write the same tree translated under `docs/<locale>/` — same paths, same numeric prefixes, same slugs, with only `title`, `description` and body prose in that language.8384A scaffold that produces one section is a **success**, not a failure — the tree's job is to hold what has no other home, and in a repo whose parts already document themselves that can be a single section. Numbering starts at `1.` over whatever survives, so nothing looks missing.8586## Route / add — `docs/` exists87881. Run the routing matrix; list **every** page to create or touch.892. New page → next free `N.kebab.md` in the section, frontmatter `title` + `description`, body from the matching template, then link it from that section's `index.md`. **Under the [render mode](REFERENCE.md#render-mode)** the same step drops the body H1, writes an absolute doc path, and leaves the section index to its page-cards component rather than gaining a bullet.903. **How-tos end with a checklist.** Add a `> [!NOTE]` **Status** callout (see [REFERENCE.md#status-marker](REFERENCE.md#status-marker)) if the feature isn't shipped yet — the [render mode](REFERENCE.md#render-mode) writes that marker as its own callout component instead, because the blockquote form does not render there at all.914. **Delta principle** — don't restate what something else already documents; link it, and write the project-specific delta plus the glue. Three sources outrank a page here, in descending order of how badly a copy hurts:92 - **A file in this repo.** `package.json`'s scripts, a workflow, a schema, a lockfile, a config — a doc that lists their contents is wrong the moment the file changes, and nothing will tell you. Name the file and what is surprising about it; never transcribe values, versions or option tables out of it.93 - **Anything the repo ships.** A library, plugin or skill travels without `docs/`, so it must carry its own reference — a second copy here is the one guaranteed to drift.94 - **An authoritative upstream source** — framework docs, a dependency's README, a standard.95 A strong guideline, not a hard block: cross-cutting glue, and the reason a thing is set the way it is, are exactly what no source file states.965. **Updating** an existing topic — the one-topic-per-page rule means there's usually exactly one page; `grep docs/` for the term, edit it **in place**, never open a second page on the same topic, and update affected reference tables + cross-links. **Never for an ADR** — a changed decision is a new ADR that supersedes the old one, and the ADR section's `index.md` decision log gets the row in the same change.976. Verify every fact against current code before writing.987. **Every listed locale gets the same change, in the same plan.** A page created or edited in the default tree is written to `docs/<locale>/` at the identical path for each locale `docs.locales` names — the section `index.md` link included. One change, every tree: leaving the translation for later is what puts them out of step, and the [locale contract](REFERENCE.md#locales) is that they never are. With `docs.locales` absent this step does not exist.99100## Reconcile — align existing docs to the convention101102Desired-state, idempotent — like a `--fix` linter for the docs tree.1031041. Read the **whole** `docs/` tree fresh (it is live state — never cached).1052. **Resolve the [render mode](REFERENCE.md#render-mode) before diffing anything** — it decides which convention the tree is being measured against, and two of the mechanical fixes below are destructive under the wrong one: they would strip the theme's frontmatter fields off the pages that need them and rewrite the mode's absolute doc paths into dangling relative ones.1063. Diff against the convention and group the plan:107 - **Auto-fix (mechanical)** — numbering gaps/dupes, missing `index.md`, removed/unknown frontmatter keys, `N.kebab.md` filename normalization, unambiguous broken links.108 - **Prompt (value-needing)** — a missing required field (`title`/`description`): derive a candidate from the H1 / first paragraph and confirm.109 - **Import (value-needing)** — a **dedicated ADR directory outside `99.adr/`** (`docs/adr/` and near variants): decisions recorded in someone else's format, which every other category misses, so they are missing from the decision log while the log still reads as complete. Measure **each file** against [the threshold](#when-a-decision-earns-an-adr) first — it governs an adopted record exactly as it governs a written one, and this is the last moment it can be asked, because an append-only log cannot be pruned afterwards. What clears it is proposed as **one** operation — move, rename to `NNNN-title.md`, the log's row, the missing frontmatter, the body under the required H2s — applied only on confirmation. A record holding a decision **and** material the expiry test rejects is **split** in that same proposal — the decision into the log, the rest routed as an ordinary page, a fragment belonging on no page dropped once the human says so — and only where the two come apart with no sentence rewritten. What does not clear the bar at all is routed whole or reported and left alone, in the same plan. Detection rule, the threshold gate, the split's rules, the `date` derivation and what is report-only instead: [REFERENCE.md](REFERENCE.md#foreign-adrs--a-decision-log-written-elsewhere).110 - **Report only** — a how-to without a checklist, a page that fits no section, suspected duplication of an upstream source **or of a file in the repo**, any secret detected. Never auto-edited.1114. Show the **plan + diff**; on confirm apply **only structure + frontmatter**. **Never rewrite prose. Only touch files inside `docs/`.** Two exceptions, both scoped: an **imported** ADR, whose body is **re-homed** under the required H2s — a split sends part of it to a page, and a named fragment is dropped on explicit confirmation, so the author's sentences are moved or removed, never rewritten; and a **locale tree**, which is regenerated rather than reconciled.1125. **Regenerate every listed locale from the default tree.** Where `docs.locales` names a locale, the sweep rewrites `docs/<locale>/` — structure derived from the source, `title`, `description` and body prose translated. This is the one place a reconcile **writes prose**, and it is scoped to the locale trees: the default-locale tree keeps _prose untouched_ in full. Inside a locale root nothing is categorized and structural drift is **never** reported — it means the sweep had not run ([the rules](REFERENCE.md#locale-trees-are-regenerated-not-categorized)). With `docs.locales` absent this step does not exist.113114`99.adr/` is **exempt** — links and the decision log only. An ADR id is permanent, so never renumber one, never rename it to the dot-schema, never close a gap in the sequence ([REFERENCE.md](REFERENCE.md#reconcile-rules)). A **foreign** ADR is outside that exemption: it was never accepted _in this log_, so giving it an id is a first assignment rather than a renumbering, and shaping it on entry is not an edit to an accepted record. A **translated** ADR is outside it too, in the other direction: the record is the source file, its translation is a rendering of one, so a locale tree's `99.adr/` is regenerated with the rest of that tree — ids, filenames and `status` copied across untouched.115116<skills-plan>117118## Presenting the plan119120Everything this skill puts in front of a human — plan, preview, candidate list, findings report —121is read **once, in a terminal**, and answered there. So **every section of it renders on arrival**,122with no interaction needed to reveal it: prose, lists, tables, fenced code.123124**Never fold content behind a control.** `<details>`/`<summary>` is a browser widget, and a125terminal has no way to open it: the summary line prints and everything under it does not. The plan126then arrives as headings with nothing beneath them, and the failure is silent on **both** sides —127the skill believes it reported, and the reader sees no marker saying anything is missing, so a128human confirms a plan whose contents never reached them. What gets folded is whatever ran long,129which is to say the part the decision actually rested on. The same holds for anything else needing130a click: a tab strip, an accordion, a "show more".131132**Length is handled by shortening, never by hiding.** This is a fixed rule of the skill, not a133per-run judgement, so it holds however long the list runs. Trim to what the decision needs, group134the rest by something the reader already thinks in (ecosystem, kind, verdict) with a count per135group, or split it across sections. What is left out is left out **visibly**: say how many, why,136and the exact command that shows the rest.137138**This binds what the skill presents, not what it writes.** A `<details>` block inside a README, an139issue body, a pull request description or a docs page is rendered by a browser and is entirely140legitimate there. The rule is about the message a human reads to decide — never about the content141of a file.142143</skills-plan>144145## Guardrails (inherited)146147- **Docs are never the source of truth — the repo is.** Code says what happens; a page exists for the why, the rejected alternatives, and the way in — the three things [code cannot express](#what-belongs-in-docs-at-all). Documentation that mirrors code or a file costs tokens on every read and starts lying at the next commit, so when a page would restate one, link it and write only the delta.148- **Plan/preview first; apply only after confirmation.** Respect plan-only / dry-run.149- **Keep generated content attribution-free** — no agent self-naming or `Generated with`/🤖 lines.150- **No secrets** in generated docs — scan, warn, exclude.151- **Only the requested action** — nothing closed or changed unasked.152- **No cache** — the `docs/` tree is live state, always read fresh.153- **A translated tree is generated, never authored.** Listing a locale in `docs.locales` binds this skill to maintain _and_ translate it, and a sweep rewrites it from the source — so a correction made inside `docs/<locale>/` does not survive. Corrections go to the source page, or to the wording guidance `docs.instructions` carries. A missing translation is legitimate and never a defect to fix ([Locales](REFERENCE.md#locales)).154155## Reference156157- **Open it where the body's routing matrix stops deciding for you** — which sections a preset scaffolds, what frontmatter and status marker a new page carries, which type a slug is allowed to be, whether a subject belongs in a folder or a section, and what a reconcile may change rather than only report: [REFERENCE.md](REFERENCE.md).158- **Open it before touching a repo that lists `docs.locales`** — which tree is the default locale, what a locale root may and may not be read as, what a sweep regenerates, and where the _prose untouched_ invariant stops holding: [Locales](REFERENCE.md#locales).159- **Open it in full before writing or importing an ADR, every time.** `99.adr/` is append-only, so the id, the naming, the required sections, what the body may hold and the supersession rule have to be right on entry — there is no later edit that repairs them: [REFERENCE.md](REFERENCE.md#architecture-decision-records).160- **Copy one per page you are about to create**, matched to that page's type: [`templates/`](templates/). They are written for the portable default, so under the [render mode](REFERENCE.md#render-mode) apply that mode's flips as you copy — there is no second set of skeletons.161162## Gap report (mandatory final step)163164If you used a section, page type, or preset not in [REFERENCE.md](REFERENCE.md), end the turn with a short note (`Gap report: section "{x}" — no catalogue entry; added ad hoc.`). Only report; don't edit REFERENCE.md yourself — the user folds gaps back in. A new _slug_ is a real gap only when it names a missing **type**; a **subject** section (`plugins`, `themes`, `integrations`) is not — route its content into the type sections (nested if it needs grouping) and report that instead. Neither is a [named non-section directory](REFERENCE.md#named-directories-that-are-not-sections) such as `_partials/` — it is catalogued, so it never triggers a report. A **locale root** (`de-DE/`) is not a section at all, so it never belongs in this report either. If everything matched: `Gap report: no gaps.`165166**Report the gap, not the rule you had to break.** A run whose pages need a form the convention forbids has hit the case the [render mode](REFERENCE.md#render-mode) exists for — say so and name `docs.render`, rather than reporting five separate rules as five separate gaps, or breaking them quietly. Under the mode, following its flips is the convention and is never a gap.