evals/evals.json
{
"skill_name": "docs-writing",
"evals": [
{
"id": 1,
"prompt": "Audit this explanation page: it explains why transactions provide atomicity, has no quickstart, and links to /guides/transactions for usage.",
"expected_output": "Classify as explanation and avoid tutorial-only findings.",
"files": [],
"assertions": [
"Does not demand a quickstart",
"Does not require a next-steps procedure",
"Applies only relevant documentation rules"
]
},
{
"id": 2,
"prompt": "Improve API reference prose: timeout defaults to 5 seconds in the code, but the page says 30 seconds. Keep the current reference structure.",
"expected_output": "Correct the documented default using implementation evidence.",
"files": [],
"assertions": [
"Uses the code default of 5 seconds",
"Preserves reference rather than converting to tutorial",
"Reports the source of the correction"
]
}
],
"routing": {
"should_trigger": [
"Audit this explanation page: it explains why transactions provide atomicity, has no quickstart, and links to /guides/transactions for usage.",
"Improve API reference prose: timeout defaults to 5 seconds in the code, but the page says 30 seconds. Keep the current reference structure."
],
"near_miss": [
{
"prompt": "Write a README from scratch for this CLI.",
"expected": "readme-creator"
}
]
}
}
rules/_sections.md
# Sections
All 9 categories with ordering, impact, and rules. The ID in parentheses is the filename prefix. 51 rules total. To add a rule, copy `_template.md` and update the three counts it lists.
---
## 1. Voice & Tone (voice)
**Impact:** CRITICAL
**4 rules:** voice defaults (active voice, contractions, second person with the tutorial "we" allowance, professional tone, reader-centric framing), no jargon, no anthropomorphism, requirements language (must for requirements, should only for recommendations, no please).
## 2. Structure & Organization (structure)
**Impact:** CRITICAL
**9 rules:** Diataxis one-type-per-file (with the compass), bottom line up front, conditions before instructions, heading followed by an orienting sentence, next steps, one idea per section (and per page), every section earns its place, clear procedures, quick start for getting-started docs.
## 3. Clarity & Language (clarity)
**Impact:** HIGH
**5 rules:** clarity defaults (plain language, cut filler, be specific, global audience, short paragraphs; the source of truth for paragraph length), serial comma, no Latin abbreviations, no nominalizations, one idea per sentence.
## 4. Code Examples (code)
**Impact:** HIGH
**8 rules:** runnable examples (with why-not-what comments and a language tag on every fence), code-to-context ratio, isolated-to-full layering, multiple languages, error descriptions, named functions, realistic example names, placeholders and test credentials.
## 5. Formatting & Syntax (format)
**Impact:** MEDIUM-HIGH
**7 rules:** sentence case headings, bold UI elements and code font for commands, descriptive link text, image alt text, lowercase filenames, periods inside quotes, semantic HTML.
## 6. Navigation & Linking (nav)
**Impact:** MEDIUM-HIGH
**7 rules:** every doc linked from at least one other doc, opening context where the site renders no breadcrumb, don't repeat content covered elsewhere, layered content depth, relative paths, searchable headings, agent-readable docs (llms.txt and Markdown page variants).
## 7. Scanability & Readability (scan)
**Impact:** MEDIUM
**2 rules:** scan defaults (white space between logical groups, diagrams and tables over prose), request and response examples beside each API reference entry.
## 8. Content Hygiene (hygiene)
**Impact:** MEDIUM
**6 rules:** delete outdated content, dedicated docs directory, experimental-feature labels, no temporal content (status reports, dated plans), planned-feature labels for docs written ahead of code, freshness metadata sourced from the build.
## 9. Review & Testing (review)
**Impact:** LOW-MEDIUM
**3 rules:** review defaults (fresh-reader test, read aloud and cut, verify against implementation), verify links, docs change with the code (same PR, prose lint in CI).
rules/_template.md
---
title: Rule Title Here
impact: MEDIUM
tags: tag1, tag2
---
## Rule Title Here
Brief explanation of the rule and why it matters (quality, readability, discoverability).
**Incorrect (description of what's wrong):**
```markdown
Bad documentation example showing the problem.
```
**Correct (description of what's right):**
```markdown
Good documentation example showing the solution.
```
Reference: [Link to documentation or resource](https://example.com)
<!--
Conventions:
- Filename: <prefix>-<slug>.md where <prefix> matches a section in _sections.md
- impact: must match the section's impact level (CRITICAL, HIGH, MEDIUM-HIGH, MEDIUM, LOW-MEDIUM).
Severity is a property of the category, not the rule, so this field is deliberately
redundant with the filename prefix: it lets a rule file read standalone. A rule that
wants a different severity belongs in a different section.
- H2 title matches the frontmatter title
- The Incorrect/Correct parentheticals name the specific failure/fix, not just "bad"/"good"
- Use ````markdown fences (four backticks) when the example itself contains code fences
- Adding or removing a rule: update the count in _sections.md, the SKILL.md priority table, and the SKILL.md description
-->
rules/clarity-defaults.md
---
title: Clarity defaults checklist
impact: HIGH
tags: clarity, defaults, plain-language
---
## Clarity defaults checklist
Default behaviors, codified as the project standard:
- [ ] **Plain language**: "use" not "utilize," "start" not "initiate," "help" not "facilitate."
- [ ] **Cut filler**: remove "very," "really," "just," "basically," "actually," "simply," "in order to."
- [ ] **Be specific**: quantify claims; replace "fast," "easy," "powerful" with numbers or concrete examples.
- [ ] **Global audience**: standard US English, no idioms, sports metaphors, or cultural references.
- [ ] **Short paragraphs**: max 4 sentences for web content. One-sentence paragraphs are fine for emphasis.
**Incorrect (vague, wordy, uses idioms and filler):**
```markdown
It is important to note that you should basically just utilize the
dashboard to facilitate deployment. The API is really fast and very
powerful. Once you get the hang of it, you'll hit the ground running.
Tokens expire after 24 hours. You can configure expiry. When a token
expires, the client must request a new one. The refresh endpoint
handles this automatically.
```
**Correct (plain, specific, literal, scannable):**
```markdown
Use the dashboard to start your deployment. The API responds in under
50ms and handles 10,000 concurrent requests.
Tokens expire after 24 hours by default. You can configure the expiry
time in the dashboard.
When a token expires, the client requests a new one. The refresh token
endpoint handles this automatically.
```
Reference: [Digital.gov: Plain language guide](https://digital.gov/guides/plain-language/)
rules/clarity-no-latin.md
---
title: Write out Latin abbreviations
impact: HIGH
tags: abbreviations, latin, accessibility
---
## Write out Latin abbreviations
Use "for example" not "e.g.," "that is" not "i.e.," "and so on" not "etc." Latin abbreviations trip up screen readers and non-native English speakers.
**Incorrect (Latin abbreviations assume familiarity):**
```markdown
Supports multiple formats, e.g., JSON, XML, etc. The config
file (i.e., the main settings file) must be valid YAML.
```
**Correct (written-out forms are universally clear):**
```markdown
Supports multiple formats, for example, JSON and XML. The config
file (that is, the main settings file) must be valid YAML.
```
Reference: [Google developer documentation style guide: Word list (e.g., i.e.)](https://developers.google.com/style/word-list), [GOV.UK style guide: eg, etc and ie](https://guidance.publishing.service.gov.uk/writing-to-gov-uk-standards/style-guides/a-to-z-style-guide/)
rules/clarity-no-nominalizations.md
---
title: Use verbs, not noun forms of verbs
impact: HIGH
tags: nominalizations, verbs, conciseness
---
## Use verbs, not noun forms of verbs
Nominalizations bury the action in a noun. "Make a decision" becomes "decide." "Perform an analysis" becomes "analyze." "Give consideration to" becomes "consider."
**Incorrect (nominalized verbs hide the action):**
```markdown
The system performs validation of the input and makes a
determination of the output format. After completion of the
installation, perform a verification of the configuration.
```
**Correct (strong verbs communicate directly):**
```markdown
The system validates the input and determines the output format.
After installing, verify the configuration.
```
Reference: [Microsoft Writing Style Guide: Verbs](https://learn.microsoft.com/en-us/style-guide/grammar/verbs)
rules/clarity-one-idea-per-sentence.md
---
title: One idea per sentence, max 25 words
impact: HIGH
tags: sentence-length, readability, conciseness
---
## One idea per sentence, max 25 words
Long compound sentences lose readers. Split at conjunctions; more than one "and" or "but" signals a split. GOV.UK sets the same threshold: check any sentence over 25 words to see if it splits.
**Incorrect (compound sentence buries multiple ideas):**
```markdown
The system processes the request and validates the input and then
generates a response, which is sent back to the client and logged
for debugging purposes.
```
**Correct (each sentence carries one idea):**
```markdown
The system processes the request and validates the input. It
generates a response, sends it to the client, and logs it for
debugging.
```
Reference: [Microsoft Writing Style Guide: Use simple words, concise sentences](https://learn.microsoft.com/en-us/style-guide/word-choice/use-simple-words-concise-sentences), [GOV.UK style guide (25-word check)](https://guidance.publishing.service.gov.uk/writing-to-gov-uk-standards/style-guides/a-to-z-style-guide/)
rules/clarity-serial-comma.md
---
title: Always use the serial comma
impact: HIGH
tags: comma, punctuation, oxford-comma
---
## Always use the serial comma
Place a comma before the final "and" or "or" in a list of three or more items to prevent ambiguity.
**Incorrect (missing serial comma creates ambiguity):**
```markdown
The CLI supports create, update and delete commands. You can
export to JSON, XML and CSV.
```
**Correct (serial comma removes ambiguity):**
```markdown
The CLI supports create, update, and delete commands. You can
export to JSON, XML, and CSV.
```
Reference: [Microsoft Writing Style Guide: Commas](https://learn.microsoft.com/en-us/style-guide/punctuation/commas)
rules/code-context-ratio.md
---
title: Match code-to-text ratio to document type
impact: HIGH
tags: ratio, tutorials, reference
---
## Match code-to-text ratio to document type
A tutorial explains before each block and names what the learner should notice after it, so it reads as mostly prose with short code steps. A reference page is the opposite: a signature, a parameter table, a request and a response, with prose only where a value needs qualifying. A tutorial that is one long code dump teaches nothing; a reference page wrapped in paragraphs hides the fact the reader came for.
**Incorrect (tutorial is one code block with no explanation):**
````markdown
```javascript
const app = express();
app.use(express.json());
app.post("/webhook", (req, res) => {
if (req.body.type === "payment.completed") handlePayment(req.body);
res.sendStatus(200);
});
```
````
**Correct (tutorial explains each step before showing code):**
````markdown
First, set up an Express server to receive POST requests:
```javascript
const app = express();
app.use(express.json());
```
Next, create a route that handles incoming events by type. Notice
that the handler returns 200 before doing any work, so the sender
never retries a slow handler:
```javascript
app.post("/webhook", (req, res) => {
if (req.body.type === "payment.completed") handlePayment(req.body);
res.sendStatus(200);
});
```
````
Reference: [Diataxis: Tutorials](https://diataxis.fr/tutorials/), [Diataxis: Reference](https://diataxis.fr/reference/)
rules/code-error-descriptions.md
---
title: Document errors with codes, meanings, and fixes
impact: HIGH
tags: errors, troubleshooting, status-codes
---
## Document errors with codes, meanings, and fixes
Readers reach for error docs when stuck, so a bare code-to-label table sends them away with nothing to do. For each error, show the message as the reader sees it, state the cause, and give the fix, with a link to where the fix happens. Google's error-message guidance names the two questions to answer: what went wrong, and how does the reader fix it.
**Incorrect (error code with no actionable guidance):**
```markdown
| Code | Description |
|------|-------------|
| 403 | Forbidden |
| 429 | Rate limited |
```
**Correct (each error includes cause and fix):**
```markdown
### 403 Forbidden
Your API key doesn't have permission for this endpoint. Check
that your key has the `billing:read` scope in the
[API dashboard](https://dashboard.acme.com/keys).
### 429 Too Many Requests
You exceeded 100 requests per minute. Add exponential backoff
to your retry logic or request a rate limit increase in the
[API dashboard](https://dashboard.acme.com/limits).
```
Reference: [Google Technical Writing: Error messages](https://developers.google.com/tech-writing/error-messages), [Stripe: Error codes](https://docs.stripe.com/error-codes)
rules/code-isolated-to-full.md
---
title: Layer examples from focused snippet to full context
impact: HIGH
tags: layering, progressive, context
---
## Layer examples from focused snippet to full context
Show the key function first, then where it fits. Let readers build understanding incrementally instead of dumping the whole file upfront.
**Incorrect (full file where the relevant part is 3 lines):**
````markdown
```javascript
import express from "express";
import { db } from "./db.js";
import { validateAuth } from "./auth.js";
const app = express();
app.use(express.json());
app.post("/webhooks", validateAuth, async (req, res) => {
await db.events.insert(req.body);
res.sendStatus(200);
});
app.listen(3000);
```
````
**Correct (focused handler first, link to full context):**
````markdown
The webhook handler validates auth, stores the event, and returns 200:
```javascript
app.post("/webhooks", validateAuth, async (req, res) => {
await db.events.insert(req.body);
res.sendStatus(200);
});
```
See the [full server setup](examples/server.js) for middleware
and startup configuration.
````
Reference: [Divio Documentation System: Tutorials](https://docs.divio.com/documentation-system/tutorials/)
rules/code-multiple-languages.md
---
title: Provide examples in multiple languages when applicable
impact: HIGH
tags: languages, polyglot, sdk
---
## Provide examples in multiple languages when applicable
If your API ships SDKs in several languages, show the same operation in the 2-3 most used, plus `curl` as the language-neutral baseline. Use tabbed code blocks or clearly labeled sections. A Node developer handed only Python has to translate before they can try anything, and translation is where the copy-paste errors come from.
**Incorrect (only one language for a multi-language SDK):**
````markdown
```python
import acme
client = acme.Client(api_key="sk_test_YOUR_TEST_KEY")
user = client.users.create(name="Ada Lovelace")
```
````
**Correct (same operation in each supported stack):**
````markdown
```python
import acme
client = acme.Client(api_key="sk_test_YOUR_TEST_KEY")
user = client.users.create(name="Ada Lovelace")
```
```javascript
import Acme from "acme";
const client = new Acme({ apiKey: "sk_test_YOUR_TEST_KEY" });
const user = await client.users.create({ name: "Ada Lovelace" });
```
```bash
curl -X POST https://api.acme.com/users \
-H "Authorization: Bearer sk_test_YOUR_TEST_KEY" \
-d '{"name": "Ada Lovelace"}'
```
````
Reference: [Stripe API reference](https://docs.stripe.com/api)
rules/code-named-functions.md
---
title: Break complex operations into named functions
impact: HIGH
tags: functions, readability, decomposition
---
## Break complex operations into named functions
Readers should skim function names and grasp the flow. Extract named functions instead of inline logic so top-level code reads like pseudocode.
**Incorrect (nested inline logic hides the flow):**
```javascript
app.post("/orders", async (req, res) => {
if (!req.body.items || req.body.items.length === 0) {
return res.status(400).json({ error: "No items" });
}
const total = req.body.items.reduce((sum, i) => sum + i.price, 0);
const charge = await stripe.charges.create({ amount: total });
await sendEmail(req.body.email, { orderId: charge.id });
res.json({ orderId: charge.id });
});
```
**Correct (named functions make the flow scannable):**
```javascript
app.post("/orders", async (req, res) => {
validateOrderItems(req.body.items);
const total = calculateOrderTotal(req.body.items);
const charge = await processPayment(total);
await sendConfirmation(req.body.email, charge.id);
res.json({ orderId: charge.id });
});
```
Reference: [Google developer documentation style guide: Code samples](https://developers.google.com/style/code-samples)
rules/code-placeholders.md
---
title: Use explained placeholders and test-mode credentials
impact: HIGH
tags: placeholders, credentials, secrets, copy-paste
---
## Use explained placeholders and test-mode credentials
Readers copy examples verbatim. A value the reader must supply is a placeholder in `UPPER_SNAKE_CASE`, explained once after the block under "Replace the following:". A credential that looks real, especially one with a live prefix such as `sk_live_`, gets pasted into production code and tripped by secret scanners in CI. Sample credentials use the provider's documented test prefix (`sk_test_...`) or a placeholder, never a live-looking string.
**Incorrect (live-looking key, unexplained placeholders):**
````markdown
```bash
curl https://api.acme.com/v1/charges \
-u sk_live_REDACTED_FULL_LENGTH_LIVE_KEY: \
-d amount=2000 -d customer=cus_123 -d source=xyz
```
````
**Correct (test key, named placeholders, explained in order):**
````markdown
```bash
curl https://api.acme.com/v1/charges \
-u sk_test_YOUR_TEST_KEY: \
-d amount=2000 \
-d customer=CUSTOMER_ID \
-d source=PAYMENT_SOURCE_ID
```
Replace the following:
- `CUSTOMER_ID`: the ID of the customer to charge, for example `cus_NffrFeUfNV2Hib`
- `PAYMENT_SOURCE_ID`: the ID of a saved card or bank account on that customer
````
A full-length live-looking sample is not just a reader hazard: GitHub push protection blocks the commit that carries it, including the commit that adds this rule. Write the prefix plus a named placeholder, never a plausible key body.
Reference: [Google developer documentation style guide: Placeholders](https://developers.google.com/style/placeholders), [Stripe: API keys](https://docs.stripe.com/keys)
rules/code-realistic-example-names.md
---
title: Name example values after the product's domain, never foo/bar/x/data
impact: HIGH
tags: examples, naming, placeholders
---
## Name example values after the product's domain, never foo/bar/x/data
An example is reference material, not a syntax demo: readers copy it and adapt it. Placeholder names (`foo`, `bar`, `x`, `data`, `temp`, `result`) force a mental substitution before that adaptation can start, and they hide which argument is an ID, an object, or a config path. Use names from the product's own domain: `subscriptionId`, `paymentIntent`, `orderTotal`, `configPath`.
**Incorrect (placeholders hide what each value is):**
```javascript
const x = await get(foo);
const result = transform(x, bar);
console.log(result.status);
```
**Correct (domain names make the example self-documenting):**
```javascript
const invoice = await getInvoice(invoiceId);
const receipt = formatReceipt(invoice, displayOptions);
console.log(receipt.status);
```
Reference: [Google developer documentation style guide: Code samples](https://developers.google.com/style/code-samples)
rules/code-runnable-examples.md
---
title: Every concept needs a copy-paste-ready example
impact: HIGH
tags: examples, runnable, copy-paste
---
## Every concept needs a copy-paste-ready example
Prose that describes a function's behavior makes the reader write the first call themselves, and that first call is where they get it wrong. Every concept, function, or endpoint gets a complete example: imports included, expected output in a comment, and a language tag on the fence so the site highlights it and agents can tell shell from JSON. Comments explain why (a constraint, a non-obvious choice), not what the code already shows.
**Incorrect (describes behavior without showing it):**
```markdown
The `createUser` function accepts a name and email, validates
the input, and returns the new user object.
```
**Correct (complete example readers can copy and run):**
````markdown
```javascript
import { createUser } from "@acme/sdk";
const user = await createUser({
name: "Ada Lovelace",
email: "ada@example.com",
});
// => { id: "usr_abc123", name: "Ada Lovelace", email: "ada@example.com" }
```
````
Reference: [Google developer documentation style guide: Code samples](https://developers.google.com/style/code-samples)
rules/format-bold-ui-code-font.md
---
title: Bold for UI elements, code font for technical terms
impact: MEDIUM-HIGH
tags: formatting, ui-elements, code-font
---
## Bold for UI elements, code font for technical terms
Use **bold** for UI elements the reader interacts with (buttons, menu items, page names, tabs). Use `code font` for filenames, commands, parameters, functions, and API elements. Mixing them makes docs harder to scan.
**Incorrect (no visual distinction between UI and code):**
```markdown
Click the Submit button and check the config.yaml file. Go to the
Settings page and update the timeout parameter.
```
**Correct (bold for UI, code font for technical terms):**
```markdown
Click **Submit** and check the `config.yaml` file. Go to the
**Settings** page and update the `timeout` parameter.
```
Reference: [Microsoft Style Guide: Formatting text in instructions](https://learn.microsoft.com/en-us/style-guide/procedures-instructions/formatting-text-in-instructions)
rules/format-descriptive-links.md
---
title: Use descriptive link text, never "click here"
impact: MEDIUM-HIGH
tags: links, anchor-text, accessibility
---
## Use descriptive link text, never "click here"
Link text must describe the destination and make sense out of context. Screen readers list links by text alone, so "click here" and "learn more" are meaningless in a list of 20 links.
**Incorrect (vague link text):**
```markdown
To configure authentication, [click here](auth.md).
For more information, [see this page](rate-limits.md).
```
**Correct (descriptive link text):**
```markdown
See [Configure authentication](auth.md) for setup steps.
Review the [rate limit thresholds](rate-limits.md) before going live.
```
Reference: [W3C: Link text and purpose](https://www.w3.org/WAI/WCAG21/Understanding/link-purpose-in-context.html)
rules/format-image-alt-text.md
---
title: Write descriptive alt text for all images
impact: MEDIUM-HIGH
tags: images, alt-text, accessibility
---
## Write descriptive alt text for all images
Every image needs alt text conveying the information it provides. Describe what the image shows, not what it is. Empty or generic alt text like "screenshot" leaves screen reader users with no context.
**Incorrect (missing or generic alt text):**
```markdown


```
**Correct (alt text describes the content):**
```markdown


```
Reference: [W3C: Alt text decision tree](https://www.w3.org/WAI/tutorials/images/decision-tree/)
rules/format-lowercase-filenames.md
---
title: Use lowercase hyphenated filenames for all assets
impact: MEDIUM-HIGH
tags: filenames, naming, consistency
---
## Use lowercase hyphenated filenames for all assets
Lowercase hyphenated filenames avoid case-sensitivity bugs across operating systems. macOS and Windows are case-insensitive by default; Linux is case-sensitive, so mixed conventions cause broken links that only surface in deployment.
**Incorrect (mixed case, underscores, or spaces):**
```markdown
Getting_Started_Guide.md
SetupInstructions.md
API Reference.md
```
**Correct (lowercase with hyphens):**
```markdown
getting-started.md
setup-instructions.md
api-reference.md
```
Reference: [Google developer documentation style guide: Filenames](https://developers.google.com/style/filenames)
rules/format-periods-inside-quotes.md
---
title: Periods and commas go inside quotation marks
impact: MEDIUM-HIGH
tags: punctuation, quotation-marks, us-english
---
## Periods and commas go inside quotation marks
US English: place periods and commas inside closing quotation marks, colons and semicolons outside. For code strings where punctuation accuracy matters, use `code font` instead of quotation marks to avoid ambiguity.
**Incorrect (period outside closing quote):**
```markdown
The status shows "active". Set the mode to "production",
then restart the service.
```
**Correct (period and comma inside closing quote):**
```markdown
The status shows "active." Set the mode to "production,"
then restart the service.
```
Reference: [Microsoft Style Guide: Quotation marks](https://learn.microsoft.com/en-us/style-guide/punctuation/quotation-marks)
rules/format-semantic-html.md
---
title: Use semantic HTML for content structure
impact: MEDIUM-HIGH
tags: html, semantics, accessibility
---
## Use semantic HTML for content structure
Use the correct element for each content type: headings for sections, lists for groups, tables for tabular data, `<code>` for inline code. In Markdown and MDX, that means the Markdown syntax, not raw HTML: `<b>` and `<br>` carry no structure for screen readers, and a hand-rolled `<ul>` or `<h2>` skips the docs site's own renderer, so it gets no heading anchor, no table-of-contents entry, and no copy button.
**Incorrect (raw HTML in an MDX file where Markdown carries the semantics):**
```markdown
<b>Prerequisites</b><br>
<ul>
<li>Node.js 18+</li>
<li>PostgreSQL 15+</li>
</ul>
```
**Correct (Markdown syntax; reach for HTML only where Markdown has no equivalent):**
```markdown
## Prerequisites
- Node.js 18+
- PostgreSQL 15+
```
Reference: [MDN: Semantics](https://developer.mozilla.org/en-US/docs/Glossary/Semantics)
rules/format-sentence-case.md
---
title: Use sentence case for headings and titles
impact: MEDIUM-HIGH
tags: headings, capitalization, case
---
## Use sentence case for headings and titles
Capitalize only the first word and proper nouns in headings. Sentence case reads easier and avoids title-case ambiguity about which words to capitalize.
**Incorrect (title case capitalizes every major word):**
```markdown
## Configure Your Application Settings
## Getting Started With the API Gateway
```
**Correct (sentence case capitalizes only first word and proper nouns):**
```markdown
## Configure your application settings
## Getting started with the API Gateway
```
Reference: [Google developer documentation style guide: Capitalization](https://developers.google.com/style/capitalization)
rules/hygiene-delete-outdated.md
---
title: Delete outdated docs, don't leave them to rot
impact: MEDIUM
tags: maintenance, outdated, deletion
---
## Delete outdated docs, don't leave them to rot
Outdated docs are worse than none: they mislead with authority. Delete docs for removed features; update docs when behavior changes. Where a migration needs the old behavior for context, put it in a collapsed `<details>` block (Markdown has no equivalent, so raw HTML is right here) rather than leaving stale prose inline.
**Incorrect (doc for a removed feature still in navigation):**
```markdown
## XML export
Use the `/export/xml` endpoint to generate an XML report.
<!-- This endpoint was removed in v3.0 -->
```
**Correct (doc deleted, migration note added where needed):**
```markdown
<!-- xml-export.md deleted -->
<!-- All links updated to point to the JSON export doc -->
## JSON export
Use the `/export/json` endpoint. XML export was removed in v3.0.
For migration details, see [v3.0 changelog](changelog.md#v30).
```
Reference: [Google developer documentation style guide: Timeless documentation](https://developers.google.com/style/timeless-documentation)
rules/hygiene-docs-directory.md
---
title: All documentation lives in the docs/ directory
impact: MEDIUM
tags: location, organization, directory
---
## All documentation lives in the docs/ directory
A reader who can't find a page assumes it doesn't exist and asks, or guesses. Keep docs in `docs/` (or the project's established equivalent), with subdirectories by Diataxis type so the folder tells a reader which kind of page they are opening. Subdirectory READMEs are the exception: they describe that specific directory. Docs living next to the code they describe is also what lets a docs change ride in the same PR (`review-docs-with-code`).
**Incorrect (docs scattered across the repo):**
```markdown
wiki/setup.md
notes/architecture.md
guides/deployment.md
src/utils/HOWTO.md
```
**Correct (one directory, subdirectories by type):**
```markdown
docs/tutorials/getting-started.md
docs/how-to/deploy-to-production.md
docs/reference/api.md
docs/explanation/architecture.md
README.md
src/utils/README.md
```
Reference: [Write the Docs: Docs as code](https://www.writethedocs.org/guide/docs-as-code/)
rules/hygiene-experimental-label.md
---
title: Mark experimental features with a visible note
impact: MEDIUM
tags: experimental, preview, warning
---
## Mark experimental features with a visible note
If a feature is experimental or in preview, add a callout right after the intro paragraph. Readers must know before investing time on an unstable API.
**Incorrect (experimental feature documented without any warning):**
```markdown
## Batch processing endpoint
Send up to 1000 items in a single request using the
`/api/batch` endpoint.
```
**Correct (clear experimental callout before details):**
```markdown
## Batch processing endpoint
Send up to 1000 items in a single request using the
`/api/batch` endpoint.
> **Note:** This feature is in preview and may change without
> notice. Do not use in production.
```
Reference: [Google developer documentation: Deprecation and experimental notices](https://developers.google.com/style/notices)
rules/hygiene-no-temporal.md
---
title: No status reports, meeting notes, or dated plans in docs
impact: MEDIUM
tags: temporal, status, evergreen
---
## No status reports, meeting notes, or dated plans in docs
Documentation must be evergreen. Status reports, test results, meeting notes, and dated plans belong in issues, PRs, or project tools, not in docs readers expect to be current.
**Incorrect (temporal content mixed into docs):**
```markdown
## Migration status
As of Q3 2025, the team is migrating to the new API. The
remaining endpoints will be ported by end of sprint 14.
```
**Correct (evergreen content with links to tracking):**
```markdown
## Migrate to v2
The v2 API replaces the v1 API. For migration steps, see
[Migrate to v2](migrate-v2.md). Track migration progress in
[issue #482](https://github.com/example/repo/issues/482).
```
Reference: [Google developer documentation: Timeless documentation](https://developers.google.com/style/timeless-documentation)
rules/hygiene-planned-label.md
---
title: Label docs written ahead of the code as planned
impact: MEDIUM
tags: planned, document-driven, unshipped
---
## Label docs written ahead of the code as planned
Document-driven development writes the page before the feature exists. Published as-is, it reads as current behavior: a reader calls the endpoint, gets a 404, and files a bug against docs that were never wrong, only early. Mark unshipped content `[PLANNED]` in the heading, write it in the future tense, link the tracking issue, and remove the marker in the PR that ships the feature. This differs from `hygiene-experimental-label`, which covers features that exist but may change.
**Incorrect (unshipped endpoint documented as current behavior):**
```markdown
## Batch processing endpoint
Send up to 1000 items in a single request using the
`/api/batch` endpoint. Batches are processed in the order received.
```
**Correct (marked, future tense, tracked):**
```markdown
## [PLANNED] Batch processing endpoint
This endpoint will accept up to 1000 items per request.
Implementation is tracked in
[#1234](https://github.com/example/repo/issues/1234).
```
Reference: [Google developer documentation style guide: Timeless documentation](https://developers.google.com/style/timeless-documentation)
rules/hygiene-update-metadata.md
---
title: Show freshness from the build, not a hand-typed date
impact: MEDIUM
tags: metadata, dates, freshness, versions
---
## Show freshness from the build, not a hand-typed date
Readers judge whether to trust a page by how current it looks. The signal that stays true is one the build produces: a last-modified date from version control, or the product version the docs are generated for. A hand-typed `Last updated: 2024-03-01` that nobody maintains says "abandoned" about a page that changed last week, which is worse than no date. Where the site cannot derive the date, prefer a version marker (`applies_to: v3.2+`) that only changes when the behavior does.
**Incorrect (manual date already stale, nothing about version):**
```markdown
*Last updated: March 2024*
## Configure authentication
Set the `AUTH_PROVIDER` environment variable to your identity
provider's URL.
```
**Correct (version pinned by the author, date owned by the build):**
```markdown
---
applies_to: v3.2+
---
<!-- Site renders "Last modified" from the file's git commit date -->
## Configure authentication
Set the `AUTH_PROVIDER` environment variable to your identity
provider's URL.
```
Reference: [Google developer documentation style guide: Timeless documentation](https://developers.google.com/style/timeless-documentation)
rules/nav-agent-readable.md
---
title: Publish llms.txt and a Markdown variant of every page
impact: MEDIUM-HIGH
tags: llms-txt, agents, markdown, machine-readable
---
## Publish llms.txt and a Markdown variant of every page
A growing share of docs-site traffic is coding agents fetching pages on a developer's behalf, and an agent reading rendered HTML pays for navigation, scripts, and tab widgets before it reaches the content. Two conventions fix that: an `/llms.txt` index at the site root (an H1, a one-paragraph blockquote summary, then H2 sections of `[title](url): one-line description` links, with an `## Optional` section for what an agent can skip), and a clean Markdown variant of each page at the same URL with `.md` appended. Stripe, Vale, and Supabase all serve both. Content rules follow from the same reader: every code fence carries a language tag, and no fact lives only in an image or a collapsed tab.
The docs author decides what the index lists and writes the one-line descriptions; the site build generates the file. In a Next.js App Router site, `optimise-seo` implements the routes.
**Incorrect (no machine-readable index, content trapped in a screenshot):**
```markdown
<!-- Site has no /llms.txt; each page is HTML only -->
## Rate limits

```
**Correct (index with described links, content as Markdown):**
```markdown
<!-- /llms.txt -->
# Acme API
> Acme processes payments and subscriptions through a REST API with
> SDKs for Node, Python, and Go. Sandbox keys start with `sk_test_`.
## Reference
- [Authentication](https://docs.acme.com/reference/auth.md): key types, headers, sandbox vs live
- [Rate limits](https://docs.acme.com/reference/rate-limits.md): per-plan limits and the 429 retry contract
## Optional
- [Changelog](https://docs.acme.com/changelog.md): dated release notes
```
Reference: [llms.txt specification](https://llmstxt.org/), [Stripe docs: read this page in your terminal](https://docs.stripe.com/docs)
rules/nav-breadcrumb-context.md
---
title: Open with context where the site renders no breadcrumb
impact: MEDIUM-HIGH
tags: navigation, breadcrumbs, wayfinding, prerequisites
---
## Open with context where the site renders no breadcrumb
Readers arrive from search and deep links, not from the top. A docs site shows them where they are through its sidebar and breadcrumb, so repeating "this guide is part of the X series" on every page is noise. Plain Markdown in a repository has no such chrome, and any page that depends on an earlier one has a prerequisite the reader may not have met. In those two cases the opening lines name the parent or the prerequisite and link to it.
**Incorrect (repo Markdown page with no indication of what came before):**
```markdown
# Token rotation
Rotate tokens every 90 days to reduce the impact of leaked
credentials...
```
**Correct (opening sentence names the prerequisite):**
```markdown
# Token rotation
This guide assumes you have created API tokens; see
[Create API tokens](create-tokens.md) if not.
Rotate tokens every 90 days to reduce the impact of leaked
credentials...
```
Reference: [Nielsen Norman Group: Breadcrumbs](https://www.nngroup.com/articles/breadcrumbs/)
rules/nav-dont-repeat.md
---
title: Link to existing docs instead of re-explaining
impact: MEDIUM-HIGH
tags: dry, linking, maintenance
---
## Link to existing docs instead of re-explaining
If a concept is explained in another document, link to it; don't re-explain. Duplicate explanations drift apart: one gets updated while the other goes stale, and readers can't tell which is authoritative.
**Incorrect (re-explains webhook setup in every guide):**
```markdown
## Send notifications
To configure webhooks, create a JSON payload with the event type,
target URL, and authentication header. Set the retry policy to
exponential backoff with a maximum of 5 attempts...
```
**Correct (links to the canonical explanation):**
```markdown
## Send notifications
Configure a webhook to receive event callbacks. For setup
details, see [Configure webhooks](../reference/webhooks.md).
```
Reference: [Diataxis: Reference documentation](https://diataxis.fr/reference/)
rules/nav-every-doc-linked.md
---
title: Every doc must be linked from at least one other doc
impact: MEDIUM-HIGH
tags: linking, discoverability, orphans
---
## Every doc must be linked from at least one other doc
An unlinked document is undiscoverable. Every file in the docs directory must be reachable from at least one other (index, sidebar, or parent page). Orphaned pages accumulate and rot.
**Incorrect (new page with no inbound links):**
```markdown
<!-- advanced-config.md exists but no other page links to it -->
# Advanced configuration
These settings control cluster behavior...
```
**Correct (parent page links to the new page):**
```markdown
<!-- In getting-started.md -->
For cluster tuning options, see [Advanced configuration](advanced-config.md).
```
Reference: [Google developer documentation style guide: Cross-references](https://developers.google.com/style/cross-references)
rules/nav-layer-depth.md
---
title: Layer content depth with links for different skill levels
impact: MEDIUM-HIGH
tags: audience, progressive-disclosure, depth
---
## Layer content depth with links for different skill levels
Give a summary for quick readers and link to deeper explanations. Experts skip the links; beginners click through. Pages stay focused without leaving anyone behind.
**Incorrect (either too shallow or too deep):**
```markdown
## Authentication
Use API keys.
```
```markdown
## Authentication
API keys are cryptographic strings that identify the calling
application. They use HMAC-SHA256 to sign requests. The key
derivation function applies PBKDF2 with 100,000 iterations...
```
**Correct (summary with link to deeper content):**
```markdown
## Authentication
Requests are authenticated with API keys passed in the
`Authorization` header. For details on key rotation and
scoping, see [API key management](api-keys.md).
```
Reference: [Nielsen Norman Group: Progressive disclosure](https://www.nngroup.com/articles/progressive-disclosure/)
rules/nav-relative-paths.md
---
title: Use relative paths for internal doc links
impact: MEDIUM-HIGH
tags: links, paths, relative
---
## Use relative paths for internal doc links
Link between docs with relative paths. Absolute paths break when docs are hosted at different base URLs or built into subfolders.
**Incorrect (absolute path, vague link text):**
```markdown
See [here](/docs/reference/api.md) for more info.
```
**Correct (relative path, descriptive context):**
```markdown
See [API authentication methods](../reference/api.md#authentication) for token setup.
```
Reference: [MDN: Absolute vs. relative URLs](https://developer.mozilla.org/en-US/docs/Learn_web_development/Howto/Web_mechanics/What_is_a_URL#absolute_urls_vs._relative_urls)
rules/nav-searchable-headings.md
---
title: Write headings that match what readers search for
impact: MEDIUM-HIGH
tags: headings, search, seo
---
## Write headings that match what readers search for
Use the words readers type when searching. Include the feature name or action so the heading describes the section. Generic headings like "Common issues" or "More info" match no query and force readers to scan the body for relevance.
**Incorrect (generic headings):**
```markdown
## Common issues
## More information
## Overview
```
**Correct (specific, searchable headings):**
```markdown
## Troubleshoot authentication errors
## Configure rate limits
## Deploy to production
```
Reference: [Google developer documentation style guide: Headings](https://developers.google.com/style/headings)
rules/review-defaults.md
---
title: Review defaults
impact: LOW-MEDIUM
tags: review, defaults, quality
---
## Review defaults
Apply before publishing:
- **Test with a fresh reader**: have someone unfamiliar with the feature follow the doc from scratch; note where they get stuck
- **Read aloud, cut what makes you stumble**: remove 20% of the words each editing pass
- **Verify against the current implementation**: run every code example, check parameter names, confirm default values against the actual software
**Incorrect (unreviewed first draft with stale content):**
```markdown
In order to be able to configure the application, you will
need to first make sure that you have created a configuration
file. Run the CLI with the `--verbose` flag to enable logging.
```
**Correct (edited, verified, and reader-tested):**
```markdown
Create a configuration file in the project root.
Run the CLI with the `--debug` flag to enable logging.
```
Reference: [Write the Docs: Documentation review guide](https://www.writethedocs.org/guide/docs-as-code/)
rules/review-docs-with-code.md
---
title: Change docs in the same PR as the code, with prose lint in CI
impact: LOW-MEDIUM
tags: docs-as-code, ci, vale, markdownlint, review
---
## Change docs in the same PR as the code, with prose lint in CI
Docs that ship in a separate PR ship late or never, and the reviewer who knows the behavior changed has already moved on. Docs-as-code fixes both: the docs live in the repository (`hygiene-docs-directory`), the change to them rides in the PR that changes the behavior, and CI lints them like code. Vale with the Google or Microsoft style package catches the voice and clarity categories mechanically; markdownlint catches heading and list structure; a link checker (`review-verify-links`) catches renames. Findings the tools already report are not findings for a human reviewer to repeat.
**Incorrect (behavior change merged, docs "to follow"):**
```markdown
<!-- PR #482: rename --verbose to --debug -->
<!-- Files changed: src/cli.ts -->
<!-- Description: "Will update docs in a follow-up" -->
```
**Correct (docs in the diff, linted on the PR):**
```markdown
<!-- PR #482: rename --verbose to --debug -->
<!-- Files changed: src/cli.ts, docs/reference/cli.md -->
<!-- .vale.ini -->
StylesPath = .vale/styles
MinAlertLevel = suggestion
Packages = Google
[*.md]
BasedOnStyles = Vale, Google
<!-- .github/workflows/docs.yml runs vale, markdownlint, and lychee on docs/**/*.md -->
```
Reference: [Write the Docs: Docs as code](https://www.writethedocs.org/guide/docs-as-code/), [Vale](https://docs.vale.sh/)
rules/review-verify-links.md
---
title: Verify all links point to valid destinations
impact: LOW-MEDIUM
tags: links, broken-links, maintenance
---
## Verify all links point to valid destinations
After any doc change, verify all outbound links still resolve, both internal doc links and external URLs. Automate the check in CI when possible.
**Incorrect (renamed doc without updating inbound links):**
```markdown
For setup instructions, see [Getting started](setup.md).
<!-- setup.md was renamed to getting-started.md -->
```
**Correct (links verified after every rename or restructure):**
```markdown
For setup instructions, see
[Getting started](getting-started.md).
<!-- CI runs markdown-link-check on every PR -->
```
Reference: [markdown-link-check: Link validation tool](https://github.com/tcort/markdown-link-check)
rules/scan-defaults.md
---
title: Scanability defaults
impact: MEDIUM
tags: scanability, defaults, readability
---
## Scanability defaults
Apply to every page:
- **White space between logical groups**: a blank line between conceptual groups; `clarity-defaults` owns paragraph length
- **Diagrams and tables over prose**: diagrams for flows, tables for comparisons
**Incorrect (a comparison buried in prose, one undifferentiated block):**
```markdown
Option A is fast but expensive, while Option B costs less and
performs adequately, whereas Option C is slow yet cheap and
reliable. Most deployments use Option B.
```
**Correct (recommendation set off on its own, table for the comparison):**
```markdown
Most deployments use Option B.
| Option | Speed | Cost | Reliability |
|--------|-------|------|-------------|
| A | Fast | High | Medium |
| B | Medium| Low | High |
| C | Slow | Low | High |
```
Reference: [Nielsen Norman Group: How Users Read on the Web](https://www.nngroup.com/articles/how-users-read-on-the-web/)
rules/scan-three-column-api.md
---
title: Keep request and response examples beside each API reference entry
impact: MEDIUM
tags: api-docs, layout, reference, examples
---
## Keep request and response examples beside each API reference entry
Stripe's API reference puts parameters in one column and a request plus its response in the other, so the reader never scrolls between "what does this take" and "what does the call look like." Docs frameworks (Mintlify, Fern, Redocly, Docusaurus OpenAPI) render that two-pane layout from the source; what the author controls is that every endpoint entry carries its own request example and response example, in that order, immediately after its parameters. A reference page that describes ten endpoints and then dumps all the examples at the bottom has the same content and none of the usefulness.
**Incorrect (examples gathered at the end, away from the parameters they show):**
```markdown
## Create a user
Creates a user. Parameters: `name` (required), `email` (required).
## Retrieve a user
Retrieves a user by ID.
## Examples
curl -X POST https://api.acme.com/users -d '{"name": "Ada"}'
curl https://api.acme.com/users/usr_abc123
```
**Correct (each entry: parameters, then request, then response):**
````markdown
## Create a user
`POST /v1/users`
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `name` | string | yes | Full name shown in the dashboard |
| `email` | string | yes | Must be unique per account |
Request:
```bash
curl -X POST https://api.acme.com/v1/users \
-u sk_test_YOUR_TEST_KEY: \
-d name="Ada Lovelace" -d email="ada@example.com"
```
Response:
```json
{ "id": "usr_abc123", "name": "Ada Lovelace", "email": "ada@example.com" }
```
````
Reference: [Stripe API reference](https://docs.stripe.com/api), [Diataxis: Reference](https://diataxis.fr/reference/)
rules/structure-bluf.md
---
title: Start with the bottom line
impact: CRITICAL
tags: structure, bluf, introduction, purpose
---
## Start with the bottom line
Open with the bottom line up front: state what the doc covers, what the reader can do after reading, and the problem plus the outcome. Push backstory and caveats to the end of the page, or into a separate explanation doc.
**Incorrect (buries the purpose under background):**
```markdown
# Deploying to production
In the early days of our platform, deployments were done manually
using SSH. Over time, we developed an internal tool that automated
parts of the process. In version 2.3, we introduced the deploy
pipeline, which builds on lessons learned from these earlier
approaches. This guide covers the deploy pipeline.
```
**Correct (leads with the purpose and outcome):**
```markdown
# Deploying to production
This guide shows you how to deploy your application to production
in under 5 minutes. You'll configure the build pipeline, set
environment variables, and trigger your first deploy.
Before you begin, make sure you have the CLI installed and access
to the production project.
```
Reference: [Write the Docs: Writing style](https://www.writethedocs.org/guide/writing/style-guides/)
rules/structure-conditions-first.md
---
title: Put conditions before instructions
impact: CRITICAL
tags: structure, conditions, order, procedures
---
## Put conditions before instructions
State where to be, what to have, or what to check before the action. Instruction-first, condition-second makes readers act in the wrong context and undo it. Applies to prerequisites, UI navigation, and conditional branches.
**Incorrect (instruction before condition):**
```markdown
Click **Save** to apply your changes on the Settings page.
Run the migration script if you're upgrading from version 2.x.
Enter your API key in the **Credentials** field, which you'll find
under **Project Settings > Integrations**.
```
**Correct (condition before instruction):**
```markdown
On the **Settings** page, click **Save** to apply your changes.
If you're upgrading from version 2.x, run the migration script.
Go to **Project Settings > Integrations**. In the **Credentials**
field, enter your API key.
```
Tip: find "if," "when," "on," "in" buried at a sentence's end and move them to the front.
Reference: [Microsoft Writing Style Guide: Procedures and instructions](https://learn.microsoft.com/en-us/style-guide/procedures-instructions/)
rules/structure-diataxis.md
---
title: One doc type per file
impact: CRITICAL
tags: structure, diataxis, doc-types, compass
---
## One doc type per file
Per Diataxis, each document is exactly one type: tutorial, how-to guide, reference, or explanation. Each serves a different need and reading mode, so mixing them fails every reader at once. Classify with the compass: does the content serve action (doing) or cognition (understanding), and is the reader acquiring a skill or applying one?
| | Acquisition | Application |
|---|---|---|
| **Action** | Tutorial: a lesson, "we" voice, reliable result, no options | How-to: a task for a competent reader, no teaching |
| **Cognition** | Explanation: "About X", context and alternatives | Reference: neutral description mirroring the product's structure |
When one page answers differently for different sections, split it into separate files and link between them. Name each by its type: a how-to says exactly what it shows ("Rotate API tokens"), an explanation reads with an implicit "About" ("How authentication works").
**Incorrect (tutorial, reference, and explanation in one file):**
```markdown
# Authentication
## Getting started with auth
Follow these steps to add login to your app...
## API reference
### POST /auth/token
Parameters:
- grant_type (required): The OAuth grant type...
## How authentication works
The system uses a three-legged OAuth flow where...
```
**Correct (separate files, each one type, cross-linked):**
```markdown
<!-- tutorials/add-login.md -->
# Add login to your app
In this tutorial, we add authentication to the sample app...
See [How authentication works](../explanation/authentication.md) for the why.
<!-- reference/auth-api.md -->
# Auth API
### POST /auth/token
Parameters:
- grant_type (required): The OAuth grant type...
<!-- explanation/authentication.md -->
# How authentication works
The system uses a three-legged OAuth flow where...
```
Reference: [Diataxis framework](https://diataxis.fr/), [Diataxis compass](https://diataxis.fr/compass/)
rules/structure-heading-overview.md
---
title: Follow every heading with an orienting sentence
impact: CRITICAL
tags: structure, headings, introductions
---
## Follow every heading with an orienting sentence
A heading followed directly by a subheading, list, or code block leaves the reader to infer what the section is for and why it matters. One sentence orients them: what this covers and when they need it. Google's guide puts it as "don't use empty headings."
The allowance is reference pages: an entry heading (`### POST /auth/token`, `### --timeout`) followed by its signature or parameter table is the standard pattern Diataxis asks reference to adopt, and a sentence there would be padding. Everywhere else, write the sentence.
**Incorrect (heading jumps straight to a list):**
```markdown
## Configuration
- `DB_HOST`: The database hostname
- `DB_PORT`: The database port
- `DB_NAME`: The database name
```
**Correct (heading followed by an intro sentence):**
```markdown
## Configuration
Configure the database connection by setting these environment
variables in your `.env` file.
- `DB_HOST`: The database hostname (default: `localhost`)
- `DB_PORT`: The database port (default: `5432`)
- `DB_NAME`: The database name
```
This applies to a heading followed by a subheading too: add a sentence between them.
Reference: [Google developer documentation style guide: Headings](https://developers.google.com/style/headings), [Diataxis: Reference](https://diataxis.fr/reference/)
rules/structure-next-steps.md
---
title: End with next steps
impact: CRITICAL
tags: structure, navigation, next-steps, links
---
## End with next steps
End tutorials and how-to guides with a "Next steps" section linking 2-4 related guides or logical next actions. Describe what each link helps the reader do, not just where it goes.
**Incorrect (ends abruptly after the last step):**
```markdown
## Step 3: Verify the deployment
Run the health check endpoint to confirm the service is running:
curl https://api.example.com/health
You should see `{"status": "ok"}` in the response.
```
**Correct (ends with actionable next steps):**
```markdown
## Step 3: Verify the deployment
Run the health check endpoint to confirm the service is running:
curl https://api.example.com/health
You should see `{"status": "ok"}` in the response.
## Next steps
- [Configure a custom domain](custom-domains.md) to serve your
API from your own URL.
- [Set up monitoring](monitoring.md) to get alerts when the
health check fails.
- [Enable auto-scaling](auto-scaling.md) to handle traffic spikes.
```
Reference: [Diataxis: How-to guides](https://diataxis.fr/how-to-guides/)
rules/structure-no-filler-sections.md
---
title: Every section earns its place
impact: CRITICAL
tags: structure, length, redundancy, padding
---
## Every section earns its place
A section that restates another section, or that exists because the format seemed to expect it, costs the reader a scan and returns nothing. Cut it rather than filling it. The usual offenders are a summary that repeats the introduction, an "Overview" that only lists the headings below it, a "Conclusion" on reference material, and a "Prerequisites" heading with nothing under it.
Test each section against the one before it: if a reader who read the previous section learns nothing new here, it is padding. Length should follow from what the reader has to do, never from the shape of a template.
**Incorrect (opens by announcing itself, closes by repeating itself):**
```markdown
## Overview
This section covers how to configure webhooks. We will look at
creating an endpoint, setting the retry policy, and verifying
signatures. Webhooks are an important part of the platform.
## Configure webhooks
Create an endpoint at any HTTPS URL you control...
## Summary
In this section, we covered how to configure webhooks, including
creating an endpoint, setting the retry policy, and verifying
signatures.
```
**Correct (one section, no framing around it):**
```markdown
## Configure webhooks
Create an endpoint at any HTTPS URL you control...
```
Reference: [Diataxis: the map is not the territory](https://diataxis.fr/)
rules/structure-one-idea-per-section.md
---
title: One topic per section
impact: CRITICAL
tags: structure, sections, focus, organization
---
## One topic per section
Each section advances one concept, feature, or step, keeping it focused, scannable, and individually linkable. Test: if you can't summarize a section in one sentence, it covers too much.
**Incorrect (two topics merged into one section):**
```markdown
## Authentication
To set up authentication, create an API key in the dashboard and
add it to your configuration file:
AUTH_KEY=your-key-here
If authentication fails, check these common issues:
- Expired API key: Generate a new key in the dashboard.
- Clock skew: Ensure your server time is within 5 minutes of UTC.
- IP allowlist: Verify your server IP is on the allowlist.
```
**Correct (each topic in its own section):**
```markdown
## Set up authentication
Create an API key in the dashboard and add it to your configuration
file:
AUTH_KEY=your-key-here
## Troubleshoot authentication errors
If authentication fails, check these common issues:
- **Expired API key**: Generate a new key in the dashboard.
- **Clock skew**: Ensure your server time is within 5 minutes of UTC.
- **IP allowlist**: Verify your server IP is on the allowlist.
```
The same test applies to the page: reaching H5 or H6 means it covers too many topics, so split it into multiple documents.
Reference: [Write the Docs: Content organization](https://www.writethedocs.org/guide/writing/style-guides/)
rules/structure-procedures.md
---
title: Write steps with imperative verbs
impact: CRITICAL
tags: structure, procedures, steps, imperative
---
## Write steps with imperative verbs
Start each step with an imperative verb (open, run, click, set, copy); number sequential steps. One action per step. For a sub-action, use lettered sub-steps.
**Incorrect (passive, descriptive steps):**
```markdown
## Changing the port
1. The configuration file needs to be opened.
2. The port value should be changed to the desired number.
3. The server needs to be restarted for the new settings.
```
**Correct (imperative verbs, goal stated up front):**
```markdown
## Change the port
To change the port your application listens on:
1. Open `config/server.yaml` in a text editor.
2. Set the `port` value to the desired number, for example `8080`.
3. Save the file and restart the server:
```bash
systemctl restart myapp
```
```
Reference: [Google developer documentation style guide: Procedures](https://developers.google.com/style/procedures)
rules/structure-quick-start.md
---
title: Include a quick start for getting-started docs
impact: CRITICAL
tags: structure, quick-start, onboarding, getting-started
---
## Include a quick start for getting-started docs
Every getting-started or README doc needs a minimal Quick Start (3-5 steps) early, before detailed explanations. Readers arrive with high motivation and low patience: get them to results fast, then link to the full guide.
**Incorrect (buries the first command under background sections):**
```markdown
# Getting started
## Overview
Acme CLI is a tool for managing cloud deployments...
## Architecture
The CLI communicates with the Acme API using...
(six more sections before the first command)
```
**Correct (Quick Start gets the reader to "Hello World" fast):**
```markdown
# Getting started
## Quick start
1. Install the CLI:
```bash
brew install acme-cli
```
2. Log in to your account:
```bash
acme login
```
3. Deploy the starter template:
```bash
acme deploy --template hello-world
```
Your app is live at the URL shown in the output. For detailed
setup options, see [Configuration](configuration.md).
```
Reference: [Write the Docs: Getting started guide template](https://www.writethedocs.org/guide/)
rules/voice-defaults.md
---
title: Voice defaults checklist
impact: CRITICAL
tags: voice, active-voice, contractions, second-person, tone, reader-centric
---
## Voice defaults checklist
Default behaviors, codified as the project standard:
- [ ] **Active voice, present tense**: actor before action. Use passive only when the actor is unknown or irrelevant.
- [ ] **Contractions**: use common ones (don't, it's, you'll); avoid unusual ones (mightn't, shan't).
- [ ] **Second person**: address the reader as "you." Reserve "the user" for someone other than the reader. "We" is fine for the authoring organization ("we recommend") and, in tutorials only, for the tutor-learner voice Diataxis describes ("In this tutorial, we build...").
- [ ] **Professional, not promotional**: replace superlatives with measurable facts. No marketing hype, no "simply" or "easy."
- [ ] **Reader-centric framing**: lead with what the reader can accomplish, not what the product does.
**Incorrect (passive, formal, promotional, product-centric):**
```markdown
The configuration will be created by the system when the application
is started. It is not necessary for the user to redeploy. Our
blazing-fast platform simply supports parallel execution of up to 16 tasks.
```
**Correct (active, natural, reader-focused):**
```markdown
The system creates a configuration file when the application starts.
You don't need to redeploy. Run up to 16 tasks in parallel to finish
builds faster.
```
Reference: [Google developer documentation style guide: Voice and tone](https://developers.google.com/style/tone), [Diataxis: Tutorials](https://diataxis.fr/tutorials/)
rules/voice-no-anthropomorphism.md
---
title: Don't give software human qualities
impact: CRITICAL
tags: voice, anthropomorphism, precision
---
## Don't give software human qualities
Software doesn't think, want, know, try, remember, or decide. Anthropomorphic language is imprecise and misleads readers about what the system does. Use precise verbs: returns, sends, rejects, creates, deletes, validates, stores, triggers.
**Incorrect (anthropomorphic language):**
```markdown
The server thinks the request is invalid and tries to recover
gracefully. The scheduler knows when to run the next job and wants
to keep the queue balanced. The load balancer prefers healthy nodes
and remembers which ones failed recently.
```
**Correct (precise technical verbs):**
```markdown
The server rejects the request and returns a 400 error with a
validation message. The scheduler checks the cron expression to
determine the next run time and distributes jobs across the queue.
The load balancer routes traffic to healthy nodes and skips nodes
that failed a health check in the last 60 seconds.
```
Reference: [Google developer documentation style guide: Anthropomorphism](https://developers.google.com/style/anthropomorphism)
rules/voice-no-jargon.md
---
title: Avoid jargon or define it on first use
impact: CRITICAL
tags: voice, jargon, acronyms, accessibility
---
## Avoid jargon or define it on first use
Spell out and define a necessary technical term on first use, then use the short form freely. Don't stack multiple undefined terms in one sentence: each compounds the reader's confusion.
**Incorrect (undefined acronyms and stacked jargon):**
```markdown
Enable the RBAC module to configure ACLs for your tenants. The
IdP handles SSO federation, so make sure the SAML assertions
include the correct NameID format.
```
**Correct (terms defined on first use):**
```markdown
Enable the role-based access control (RBAC) module to configure
access control lists (ACLs), rules that define which users can
access which resources, for your tenants.
The identity provider (IdP) handles single sign-on (SSO) federation.
Make sure the SAML assertions include the correct NameID format.
See the [SAML reference](saml-reference.md) for supported formats.
```
Reference: [Google developer documentation style guide: Jargon](https://developers.google.com/style/jargon)
rules/voice-requirements-language.md
---
title: Use must for requirements and should only for recommendations
impact: CRITICAL
tags: voice, requirements, must, should, precision
---
## Use must for requirements and should only for recommendations
Readers act on the modal verb. "Must" states a requirement: the task fails without it. "Should" states a recommendation the reader can weigh. The failure is using "should" for a requirement, which lets a reader skip the step and then debug the result. Don't add "please" to instructions: it implies the step is optional and adds nothing when it isn't.
The rule is not "ban should." Google's word list keeps "should" for expected or recommended practice. Flag it only where the sentence describes something that breaks when skipped.
**Incorrect (a requirement dressed as a suggestion, plus "please"):**
```markdown
You should set the API key before making requests. Please ensure
the configuration file has the correct permissions. You should back
up the database before upgrading; the upgrade is irreversible.
```
**Correct (requirements as must, a real recommendation as should):**
```markdown
You must set the API key before making requests. The configuration
file must have `600` permissions. Back up the database before
upgrading; the upgrade is irreversible. You should set the timeout
to at least 30 seconds on slow networks.
```
Reference: [Google developer documentation style guide: Word list (must, should, please)](https://developers.google.com/style/word-list), [RFC 2119](https://datatracker.ietf.org/doc/html/rfc2119) for specifications
SKILL.md
---
name: docs-writing
description: Writes and audits technical docs with Diataxis type gating, runnable examples, and 51 documentation rules. Use when asked to "review my docs", "write an API reference", or improve a documentation page. For a whole README use readme-creator; for agent instructions use agents-md.
---
# Documentation Writing
- **IS:** writing and auditing technical documentation quality (Diataxis doc types, voice, structure, clarity, runnable code, formatting, navigation, content hygiene, agent-readable docs) for docs sites, API references, documentation-site tutorials, how-to docs, and existing READMEs.
- **IS NOT:** editorial blog tutorials or articles (use the external `ghostwriter` skill with platform `blog`), a README from scratch or a whole-README rewrite (use `readme-creator`; a README that needs its prose fixed in place stays here), AGENTS.md or CLAUDE.md instructions (use `agents-md`), marketing and landing-page copy (use `copywriting`), or the product's own error strings and CLI output (use `dx-audit`; this skill covers the docs that describe them).
## Mode dispatch
- Reviewing docs? → Audit workflow.
- Writing or rewriting a page? → Writing workflow.
- "Improve" or "fix" docs? → Audit first, then apply fixes yourself.
## Classify before anything else
Doc type gates which rules apply, so classify every file first. Use the Diataxis compass: does the page serve **action** (doing) or **cognition** (understanding), and is the reader **acquiring** a skill or **applying** one?
| | Acquisition (learning) | Application (working) |
|---|---|---|
| **Action** | Tutorial | How-to guide |
| **Cognition** | Explanation | Reference |
A page that answers differently for different sections is mixed; `structure-diataxis` handles the split. Classify by the reader's task, not the filename. A README can orient or reference; a getting-started page can be a tutorial or a goal-directed how-to.
## Audit workflow
Track this checklist:
```text
Docs audit progress:
- [ ] Step 1: Scope to changed files unless a full sweep was requested
- [ ] Step 2: Classify each doc with the compass; name the audience
- [ ] Step 3: Run CRITICAL categories (voice-, structure-), skipping rules the type-gating table excludes
- [ ] Step 4: Run HIGH categories (clarity-, code-)
- [ ] Step 5: Run remaining in-scope categories (format-, nav-, scan-, hygiene-, review-)
- [ ] Step 6: Report per the output contract, by severity
```
Load rule files by category prefix (`rules/voice-*.md`, then `rules/structure-*.md`, ...) only for in-scope categories. After applying fixes, rerun the rules that produced findings before finalizing.
## Writing workflow
Track this checklist:
```text
Docs writing progress:
- [ ] Step 1: Pick one Diataxis type per file with the compass; name the audience and what they can do afterwards
- [ ] Step 2: Read the defaults bundles (voice-defaults, clarity-defaults, scan-defaults) plus the structure- and code- rules the type-gating table keeps
- [ ] Step 3: Draft: bottom line up front, quick start for getting-started docs, runnable example per concept, next steps for tutorials and how-tos
- [ ] Step 4: Apply the type-relevant rules to concrete weaknesses in the draft
- [ ] Step 5: Verify: run every example, resolve every link, confirm parameter names and defaults against the implementation; quote the command output
```
Step 5 is the exit criterion: a doc ships when its examples ran and its links resolved, not when it "reads well". Length follows what the reader has to do, not the template: drop a section the page does not need rather than filling it.
## Type-gating table
These rules apply only to the listed types. Flagging them elsewhere tells the author to break Diataxis.
| Rule | Applies to |
|------|-----------|
| `structure-quick-start` | Getting-started pages, READMEs |
| `structure-next-steps`, `structure-procedures` | Tutorials, how-to guides |
| `code-multiple-languages` | Reference and how-to pages for a multi-SDK API |
| `scan-three-column-api` | API reference |
| `hygiene-experimental-label`, `hygiene-planned-label` | Reference and how-to pages for unstable or unshipped features |
| `nav-agent-readable` | Docs sites (not a single README) |
Everything else applies to every type. Tutorials additionally get the `we` allowance in `voice-defaults`; reference pages get the signature-block allowance in `structure-heading-overview`.
## Rule categories by priority
| Priority | Category | Impact | Prefix | Rules |
|----------|----------|--------|--------|-------|
| 1 | Voice & Tone | CRITICAL | `voice-` | 4 |
| 2 | Structure & Organization | CRITICAL | `structure-` | 9 |
| 3 | Clarity & Language | HIGH | `clarity-` | 5 |
| 4 | Code Examples | HIGH | `code-` | 8 |
| 5 | Formatting & Syntax | MEDIUM-HIGH | `format-` | 7 |
| 6 | Navigation & Linking | MEDIUM-HIGH | `nav-` | 7 |
| 7 | Scanability & Readability | MEDIUM | `scan-` | 2 |
| 8 | Content Hygiene | MEDIUM | `hygiene-` | 6 |
| 9 | Review & Testing | LOW-MEDIUM | `review-` | 3 |
For the full rule list per category, read `rules/_sections.md`. The `*-defaults.md` files (voice, clarity, scan, review) are multi-check bundles, 2-5 baseline checks each.
## Output contract (audit mode)
```markdown
## Documentation Audit Findings
### path/to/file.md
- [CRITICAL] `voice-defaults`: Passive voice obscures who performs the action.
- Fix: Rewrite "The configuration is loaded by the server" as "The server loads the configuration."
### path/to/clean-file.md
- ✓ pass
```
- Group by file; order by severity within each file.
- Use `file:line` when available.
- Every finding names the rule, states the issue, proposes a fix. No fix, not reportable.
- List clean files as `✓ pass` so the author knows they were checked.
## Gotchas
- Doc-type misclassification is the top false-positive source. A missing quick start on an explanation page, or a "Next steps" section demanded of a reference page, is a finding against Diataxis, not for it. Check the type-gating table before reporting.
- Cite the specific failing check in a `*-defaults.md` bundle ("`voice-defaults`: passive voice"), not just the filename, or the author can't locate the issue.
- Load rule files by prefix for in-scope categories only. Loading the whole folder before scope is known floods context and buries the CRITICAL findings under MEDIUM ones.
- "Should" is not a bug. Google's current word list uses "should" for a recommendation and "must" for a requirement; flag "should" only where the sentence states a requirement. Flagging every "should" produces a wall of false positives.
- An example key that looks live (`sk_live_...`) gets pasted into real code and tripped by secret scanners. Sample credentials use the provider's test prefix (`sk_test_...`) or an explained placeholder (`YOUR_API_KEY`); see `code-placeholders`.
- A hand-typed "Last updated: 2024-03-01" that nobody maintains reads as "abandoned" and is worse than no date. Only recommend `hygiene-update-metadata` when the date can come from the build or VCS.
- A "This guide is part of the X series" opener on every page of a docs site duplicates the sidebar and breadcrumb the site already renders. `nav-breadcrumb-context` is for plain Markdown in a repo and for pages with a prerequisite the reader must have met.
- Don't rewrite content you were asked to review; report and propose fixes unless the user said "improve" or "fix".
- Don't audit unchanged files unless a full sweep was explicitly requested; unscoped findings drown the real ones.
## Related skills
- `readme-creator`: a README from scratch or a full rewrite; this skill audits and fixes existing ones in place.
- `agents-md`: AGENTS.md/CLAUDE.md instruction files (execution-first, not reader-facing docs).
- `copywriting`: marketing, landing-page, and product copy.
- `dx-audit`: the product's own error messages, CLI output, and API ergonomics; this skill covers how the docs describe them.
- `optimise-seo`: implementing `llms.txt`, AI-crawler policy, and Markdown routes in a Next.js App Router site; `nav-agent-readable` says what the docs should expose, that skill builds it.
- Optional external `ghostwriter` where installed: editorial tutorials, how-to articles, thought leadership, and long-form posts, drafted from the `blog` platform profile.
- Optional external `blodemd` where installed: scaffolds/deploys MDX docs sites; this skill governs content quality inside them.
Maintenance only: `evals/evals.json` contains regression scenarios for changes to this skill; it does not load during a user task.