.agents/skills/write-openspec-docs/writing.md
Structure, voice, tone, and language for OpenSpec's user docs. This file is the primary style authority for the docs tree. The tree's own README owns structure (the page map and which page teaches what); when this file and that README disagree, the README wins. no-ai-slop owns the generic slop patterns; this file owns what OpenSpec's docs specifically look and sound like.
Every rule below applies one of these; when rules collide, the principles decide.
ls. The concept is the gloss, never the name.Name the questions or exact product terms a reader would bring to the section ("does init touch .gitignore?", "how do I add a tool later?", generates). Each answer or term must be findable by scanning, heading to anchor to fact, without reading paragraphs. If finding a fact means reading sentences, restructure; prose that passes needs no bullets, and no amount of bullets saves a section that fails.
Brevity happens at drafting time, not review. A page that needs heavy cutting in review gets rewritten, and a rewrite costs more than writing it spare the first time. Start from the shortest version that answers and expand only where a real reader question goes unanswered.
Every slot is earned before it's written:
The glance test is the backstop, not the method. Scroll the rendered page and read it as shapes: short units, air between anchors, no screen-filling block of anything. A page that looks like work loses its reader before the first sentence; if yours does, something above got in without earning its slot. For table or layout changes, check both desktop and narrow widths; the Markdown source can't show cramped columns, poor wrapping, or horizontal scrolling.
openspec/ folder at the repo root"), never by concept alone ("the planning folder"). The concept gloss can wait for the item's expansion. When a location varies (per tool, per OS), anchor it with a real folder or two (".agents/, .claude/") and link the full list.Every load-bearing fact sits on an anchor: something the eye lands on without reading. A fact a reader might come back for never lives only in the middle of a paragraph. The anchors these docs use:
**1. Remove the package.**) for multi-step tasks.**Term**: fact bullets for options, properties, and locations.A full screen of content with no anchor is a wall, even when every sentence in it is true.
No house template: facts go on anchors (enumerable content defaults to a list or table); explanation, reasoning, and judgment go in prose. The content picks the form:
**Term**: fact bullets.Across every form:
Paragraphs are glue between anchors, not containers for facts.
The narrator is a colleague who has run every command on the page, hit the failure modes personally, and is telling you what they know. Not a marketer, not a tutorial host, not a manual.
openspec/ folder: pause first."Same voice everywhere; structure and temperature shift:
Guides and Customize pages share one skeleton: a one-line what, a link to the Quickstart (never a recap), the 80% path, then Advanced. The exception is the concepts page, which is an explanation, not a task guide; its shape follows its concerns.
Reference describes the product for a reader who is already working with it. The outline follows the machinery: file, block, field; command group, command, option; object, property, value. Use the product's exact names as headings when readers will search for those names. Reader-question headings belong to task pages unless the question itself is the established lookup term.
Cover the full contract without packing several facts into one sentence, cell, or paragraph.
Before writing prose:
For each field, option, command, or file, record the identity facts that apply:
Then record the behavior facts that apply:
Don't create empty sections or table columns for facts that don't apply.
Open with a complete table or list. Expand an item below the inventory only when its behavior can't fit cleanly in the overview. Put the expansion under the item's exact name so the table of contents works as an index.
For field and option references, Field | Contract is the safe table shape when definitions need sentences. Add more columns only when every cell is short and comparable. If columns split one coherent definition into fragments or wrap badly at a narrow width, use fewer columns or move the detail below the inventory.
An example illustrates one mapping, rule, or result. It doesn't become a sequence the reader follows or a narrative about completing a task. A complete example may follow the contract when seeing the elements together helps lookup.
Put the concrete default path or value in the primary slot. Put environment variables and uncommon overrides afterward.
State the observable consequence of a limit. If OpenSpec ignores a misspelled field, say that validation passes and the field has no effect. If a value falls back, name the value OpenSpec uses.
OpenSpec spans the terminal and the AI chat, and readers mix them up. Label every snippet:
In your terminal:
openspec init
In your AI chat:
/opsx:propose add-rate-limit
Where the reader could doubt it worked (a fresh install, a first run, a command with no output of its own), end with the concrete success signal: the line the command prints, the file that now exists, what the agent says next. Where the outcome is obvious, stop; an unneeded success line is noise.
Pages are plain markdown; GitHub and the site both render them. JSX components (<Callout>, <Tabs>, <details>) don't render; never use them.
npm; the site renders it as npm/pnpm/yarn/bun tabs with a copy button per tab (remarkNpm in website/source.config.ts, which also persists the reader's choice across blocks). On GitHub the fence degrades to the plain npm command.> [!NOTE], > [!TIP], > [!IMPORTANT], > [!WARNING], > [!CAUTION]); GitHub styles them natively and the site renders them as callouts (remarkGfmAlert in website/lib/remark-gfm-alert.ts). Never place one directly under the page title: the sync lifts the leading blockquote into the page description.openspec-propose), which is the same in every tool. A command spelling (/opsx:propose) appears only as a labeled per-tool example, never as the generic instruction; commands are headed for deprecation and their spellings vary per tool..agents/ folder in file-path examples; a tool-specific folder (.claude/) appears only when the example is about that tool.> blockquote the site lifts into the page description), keep it plain, concrete, and true of the finished page.A fact lives on exactly one page; everywhere else links to it. A second copy is a future contradiction. The tree's README says which page owns what; when in doubt, link.
When unsure how something should scan or sound, match these:
docs-lab/start/setup.md: section shape, inventory-then-expand, enumerable facts on bullets.docs-lab/start/installation.md, the Uninstalling section: multi-step tasks with bold numbered lead-ins.