| name | design-doc |
| description | Run an assumptions-first architecture design and ship it as an interactive design doc. Ground truths land in a register with stable IDs, every design fork goes through a question round with a recorded escape hatch, an adversarial review attacks the middle draft, and every estimate is gated on a named spike. The deliverable is a registers.json-driven single-file HTML doc plus a generated PDF, written as a humble proposal that explains and asks for feedback. Use when asked to "write a design doc", "architecture proposal", "help me design <system>", "redesign <system>", "assumptions-first design", or to turn a systems discussion into a reviewable design document. |
| allowed-tools | Bash(python3:*, ls:*, cat:*, pdftoppm:*, wrangler:*, npm:*, open:*, wlm:*, slop-cop:*, uvx:*, ssh:*, rsync:*), Read, Write, Edit, Glob, Grep, AskUserQuestion |
design-doc
An architecture design is a stack of decisions on top of a stack of assumptions. This skill runs the design as a conversation โ the user decides everything โ and renders the result as a document where every claim traces back to an assumption, a decision, and the question round that produced it.
One command drives the mechanical parts:
TOOL="python3 ${CLAUDE_PLUGIN_ROOT}/skills/design-doc/scripts/design.py"
$TOOL scaffold --title <name>
$TOOL scaffold --example
$TOOL check <dir>
$TOOL pdf <dir>
$TOOL snapshot <dir> --note "โฆ" --item "โฆ"
Read reference/method.md before Phase 1, reference/writing.md before Phase 5, and reference/publish.md before Phase 6 โ the method file is the round/register protocol, the writing file is the voice contract, the publish file is the hosting flow. reference/schema.md is the field-by-field contract for the JSON files; scaffold the tinyq example when you want a filled register next to the schema.
Terminology
- Register โ a structured list in
registers.json whose entries have stable IDs: A# assumptions, DQ# decisions, Q# open questions, V# spikes, c-<slug> architecture cards.
- Round โ one AskUserQuestion exchange that settles one or more design questions, logged verbatim in
qa-log.json.
- Supersession โ a changed decision gets a new entry and the old one gets
s: "superseded" plus a by pointer. History stays legible because nothing is edited in place.
- Star (
โ
) โ marks the load-bearing assumption, the one whose failure invalidates the document.
- Spike โ a named, time-boxed experiment (
V#) that turns an estimate marked (E) into a measured number.
- Statuses: decisions are
resolved | superseded | open; assumptions are working | validate ("needs validation" โ someone outside the document has to say yes).
Scope
This skill stops at the design. Its outputs are a decision record and a document; implementation code is a different task that starts after the proposal survives review. Four gates keep the record honest:
- Design forks are the user's to decide. Every fork goes through a round, even when one option looks obviously right: the record of why is worth more than the saved exchange.
qa-log.json is verbatim and append-only. Clean up spelling in answers, change nothing of substance, and skip explain-only exchanges; it is a decision log, not a transcript.
- The adversarial review artifact stays out of the rendered doc. Only the decisions it drove ship; a findings section reads as self-congratulation and tells the reader nothing.
- The doc carries no vanity counts ("27 entries", "9 spikes pending"). Numbers appear when they describe the system, not the effort.
Phase 0 โ Scaffold and diagnosis
Run $TOOL scaffold --title <name>. Every design doc lives in its own fresh directory โ scaffold creates ./<slug>/ (pass an explicit path as a positional argument to put it elsewhere) and refuses a non-empty target, so one design never mixes into another's files or an existing project's. Interview the user about the current system before proposing anything: what exists, what hurts, and why. Write the diagnosis into NOTES.md as root causes rather than symptoms ("durability latency is S3 latency" rather than "writes are slow"), then explain it back and let the user correct it. Designing against a wrong diagnosis wastes every later phase.
Exit criteria: the project directory exists; the user has read the diagnosis and agrees with it.
Phase 1 โ Assumptions
Before the first round, check the available-skills list for cc-present:present (the /cc-present board skill) and invoke it with the Skill tool when listed; the interview runs as a live board from the very first question, with AskUserQuestion only as the no-cc-present fallback. Collect ground truths through rounds: constraints the design must satisfy, facts about scale and workload, things the user believes but hasn't verified. Each becomes an A# entry with status working or validate; star the load-bearing one, and record who has to confirm each validate entry as an open item. When the user flags an assumption as shaky, that flag goes in the entry; resolving it on their behalf would defeat the point of the register.
Exit criteria: the user confirms the register covers what they know; $TOOL check is clean.
Phase 2 โ Design rounds
Design by question rounds, one fork at a time. Rounds run on a live cc-present board: before the first round (in Phase 1 and again here), check the available-skills list for cc-present:present (the /cc-present board skill) and invoke it with the Skill tool โ its instructions govern composing and pushing the board, and every round from then on goes through a board (one card per question, a choice block whose option hints carry the consequences). AskUserQuestion is the fallback surface, used only when cc-present:present is absent from the skill list. Either way the shape holds: options carry real consequence descriptions, exactly one is marked "(Recommended)", and the last option is always "Add to open list" โ an escape that actually enqueues a Q# entry rather than forcing a choice. The rounds are an interview, not a survey: point out flaws in the current draft, suggest alternatives the user didn't name, push back where the evidence disagrees, and decide nothing yourself. Each answer becomes a DQ# with the resolution, the rejected alternatives, and the round number. When a later round changes an earlier decision, supersede: a new DQ#, a by pointer on the old one.
Exit criteria: no undecided fork remains outside the open list; every DQ# traces to a round in qa-log.json.
Phase 3 โ Adversarial review
Attack the middle draft, before polish makes flaws harder to see. Use the codex plugin skill when it's available; otherwise spawn a fresh-context subagent with no stake in the design and a brief to attack it as a skeptical senior engineer: correctness bugs, missing failure modes, unjustified numbers. Save the output verbatim as <reviewer>-review-<date>.md, index each finding in the findings register (data only โ never rendered), and disposition every one: a new decision, an open item, or a recorded rejection with a reason. Then run the reviewer again on the updated registers: dispositions change the design, and a changed design grows new flaws. One pass is the floor, not the norm.
Exit criteria: every finding has a disposition, and the latest pass produced nothing that changes a decision.
Phase 4 โ The quantitative story
Latency is one axis a design can be measured on, not the default. The doc carries a small library of quantitative components; pick the ones that describe this system, skip the rest (the doc hides empty sections), and skip the phase entirely when the design has no quantitative story:
- Request paths (
paths, plus the scaleMarks strip) โ for designs whose story is latency: p50/p95 segments per step, summed and traceable.
- Load ceilings (
ceilings) โ for designs whose story is load: each resource gets a ceiling, its first observable symptom, and the guard in front of it.
- Number tables (
numbers) โ any other axis, each table with its own columns: a throughput budget, storage growth, a cost model, freshness windows.
Two rules hold whichever components are in play. Every unmeasured number is marked (E) and gated on a named V# spike, because an estimate nobody plans to measure is a guess wearing a costume. And under load the system backpressures, rejects, or goes stale; a design whose overload mode corrupts data is off-design.
Exit criteria: no (E) without a spike; every ceiling row has a guard.
Phase 5 โ The document
Read reference/writing.md first; the voice contract lives there. Fill meta in registers.json (title, date, slug, banner for the starred assumption). Hand-draw the system SVG and replace the placeholder between the <!--SYSD--> markers. The diagram is the one part of the HTML you edit; everything else renders from the JSON. Write the doc content in two passes: structure and de-jargoning first, then a separate tone pass whose test for every sentence is "does this solicit feedback, or make a claim?"; the doc exists to be corrected, not admired. Run the voice gate โ a required step, not an available upgrade: wlm profile list first, always; with a profile, read the style card before drafting, then run wlm -p <profile> adversary critique on the exported Markdown during the tone pass and fold in or explicitly reject every flag; without one, the fallback contract applies. The numbered checklist with exact invocations is in reference/writing.md. Run slop-cop check after each pass. Then $TOOL pdf <dir> and look at the pages with pdftoppm: a structural check tells you the PDF exists; only your eyes tell you it renders.
Exit criteria: $TOOL check is clean; the doc renders over python3 -m http.server 8641; the PDF is built and visually inspected; the voice gate ran โ wlm profile list was checked, and with a profile every adversary-critique flag is folded in or rejected with a reason.
Phase 6 โ Publish and handoff
Stage a clean deploy folder holding only the files meant to ship:
$TOOL snapshot . --note "<headline>" --item "<one change, for the reader>"
mkdir -p dist
cp design-doc.html dist/index.html
cp registers.json qa-log.json NOTES.md design-doc.pdf dist/
cp -R history dist/
The snapshot stamps this publish as a revision; from the second one onward, a returning reviewer lands directly in the diff against the revision they last read, unchanged content tucked behind a toggle. The note and --item bullets are the first thing that reader sees: write them in plain language for someone coming back after days โ what changed and what it means for them, never round numbers or register IDs (the diff panel already lists those). The contract with a worked example is in reference/publish.md, and revision prose passes the same voice gate as the doc โ wlm profile list, style card, adversary critique over the drafted note โ before the snapshot is stamped.
Ask the user where it goes, then follow reference/publish.md: local serving is python3 -m http.server 8641; public hosting is wrangler deploy when authenticated, or wrangler deploy --temporary, which returns a claim URL that expires in 60 minutes; hand that to the user immediately. After deploying, one lightweight check (the page loads with the right title) is enough; exhaustive per-asset probing after a confirmed deploy is noise. Add a changelog entry to NOTES.md naming the deploy name and live URL โ later register edits redeploy through the same name to the same URL, with the exact sequence in reference/publish.md.
Exit criteria: the user has the URL or serve command; the changelog records what shipped; the revision snapshot is recorded, its note and items having passed the voice gate first.
Common issues
- PDF step fails with "no Chrome found" โ install Chrome/Chromium or set
CHROME=/path/to/chrome. $TOOL pdf exits 2 with instructions.
- Doc shows a "Data not loaded" screen โ it was opened as
file://; browsers block local-file fetch. Serve the folder over HTTP as the screen says.
- wlm voice profile absent โ the fallback voice contract in
reference/writing.md applies. A missing profile excuses the style card, not the wlm profile list check that discovered it.