Goal
Turn what you and the user have been discussing, or the user's idea, into a single self-contained spec — part PRD (user-facing intent), part technical spec (decisions already settled).
Hard Rules
- Before drafting, grill the user on the open technical decisions (see Flow → Grill the technical decisions) — material choices the conversation left unresolved that the codebase can't settle. Outside those, if a gap blocks the writeup, resolve it and note the assumption rather than reopening the interview.
- Placeholder language. No
TODO,TBD,for later,v2, "we'll figure out later", or equivalent. If a decision is open: resolve it now (one targeted question or spawn a subagent to investigate), or move it to Out of scope with a sentence on why. - Every subagent this skill spawns runs at the standard tier (see References → Subagents) unless otherwise specified.
- Write the draft to a fresh OS temp file:
$(mktemp -t crank-spec).md. Do not write into the working directory unless the user explicitly asks. Tell the user the path once. - Reference real files as
path:linewherever you have them.
Guidelines
- Synthesize from the conversation; don't re-litigate what's settled.
Simplify first
Before locking Technical decisions, hunt for the re-framing that makes the change smaller — not the design that best organizes its complexity. The strongest version of a feature is often a natural extension of a module that already exists; prefer the design that deletes complexity over the one that rearranges it.
Treat each of these as a design problem to resolve in the spec, never a detail to leave for the implementer:
- A one-off boolean, nullable mode, or special-case branch threaded through an existing flow. Re-frame the state model so the branch disappears, or route the behavior behind the module that owns the concept.
- Feature-specific logic landing in a shared path. Move the ownership boundary so the feature becomes part of the module that owns the concept, instead of a check scattered through code that shouldn't know about it.
- A near-duplicate of something the codebase already has. Reuse the canonical helper the grounding subagents reported; a bespoke twin is architectural drift.
- An interface that leans on optionality, casts, or silent fallbacks. Make the invariant explicit instead — if a field is sometimes absent, the spec says when and why.
Working code that makes the surrounding code harder to reason about is a spec bug, not an implementation detail.
Design lens
Apply to any module that is new (the grounding subagents reported no analogous surface) or named in the Refactor scope (an existing module the spec intends to reshape). For a module that merely extends existing prior art and isn't in the Refactor scope, follow the established pattern and skip this lens. For an in-scope module, before you name the chosen design:
- Deletion test (VOCABULARY.md). Run it on the module: if deleting it wouldn't scatter complexity across its callers, it's a pass-through — fold it into the caller and don't spec it as a module.
- Design it twice. Sketch the module two ways under different binding constraints so they genuinely diverge — e.g. one minimize the interface: 1–3 entry points, max capability each; the other maximize flexibility: let the caller compose the behavior. Pick the deeper one. Record the chosen shape, one sentence on why it beat the alternative, and one sentence on what it gives up (the alternative's strongest property). A second sketch that's a near-twin of the first means the constraint wasn't binding — re-sketch it.
- Seam & dependencies. Classify each dependency the module crosses: in-process (no seam — test through the interface directly), local-substitutable (test stand-in like PGLite/in-memory FS — internal seam), remote-but-owned or true-external (define a port at the seam; production adapter + test adapter).
Keep the interface as the test surface (see Deliverables → Testing approach): the seam you name here is the one the tests drive.
References
Subagents
If exploring the codebase could answer a question — does this surface exist, what's the exact signature, is a claim you're about to write into the spec actually true — dispatch a standard subagent to find out rather than digging in your own context.
This skill spawns subagents at two tiers — resolve each to your harness (Claude Code / Codex / Cursor) per SUBAGENT-TIERS.md. standard = codebase grounding and exploration; heavy = the adversarial spec review.
Set the tier explicitly on every spawn — never leave it to default. (In Claude Code an unset model silently inherits the orchestrator's heavy/opus tier, spending heavy budget on bulk work; SUBAGENT-TIERS.md carries the per-harness values.)
Vocabulary
Shared design language across the crank pipeline, defined once in VOCABULARY.md. This skill leans on module, interface, depth (leverage / locality), the deletion test, seam, port / adapter, and the implementation-detail test — read their meanings there.
HTML review guide
The interactive-review render steps live in HTML-REVIEW.md; Flow → Hand back follows it when the user opts in.
Spec skeleton
The compact markdown skeleton lives in SPEC-TEMPLATE.md; Flow → Draft uses it as the starting shape, then scales or omits sections per Deliverables.
Adversarial review brief
The heavy review prompt lives in SPEC-REVIEW-BRIEF.md; Flow → Adversarially review loads it only at that step.
Deliverables
A single self-contained spec written to the temp file (see Hard Rules). Include whichever sections apply, scaled to the topic (a bug fix is 20 lines; a new subsystem is denser):
- Problem — what the user is trying to solve, in their words.
- Solution — the proposed change, in user-facing terms.
- User stories —
As an <actor>, I want <feature>, so that <benefit>, when distinct actors or user goals clarify the scope. Omit for small bugs, internal refactors, or technical changes where the real contract is the acceptance criteria plus Technical decisions. - Acceptance criteria — a numbered list of independently checkable statements, one per behavior: every interaction, keybinding, alias, edge case, state transition, and validation. Each criterion must be falsifiable by an agent or a named human smoke check — "works correctly" is not a criterion; "pressing
Esccloses the dialog without saving" is. This list is the contract the plan's Coverage table and execute's final review key off: a behavior not listed here is invisible to every downstream check. - Technical decisions — every architecturally-meaningful call landed on: modules touched, interfaces, schemas, data flow, dependencies (pinned), failure modes. Name the chosen option and one sentence on why; when a real alternative was on the table, also name what the chosen option gives up — a decision recorded with only its upside reads as unexamined and invites re-litigating. For each layer touched (DB, IPC, renderer state, renderer queries), name the existing surface the change goes through and cite the prior-art
file:line—repository function: …,IPC endpoint: …,renderer hook: …,query key: …. If the grounding subagents reported no analogous surface, say so explicitly. Inline prototype snippets when they pin a decision more precisely than prose (type shape, reducer, schema, query) — the decisive slice, not a demo. - Testing approach — what makes a good test for this work (external behavior, not internals), which seams to test, prior art in the codebase. Name the same code path real users hit: if the listener attaches to
window, dispatch onwindow; if a click traverses a button withrole/tabindex, click that element. A test that fires synthetic events past the production seam is a dead feature in disguise. Steer the plan and implementer away from an implementation-detail test (defined in VOCABULARY.md) toward a behavior test driven through the seam. The plan slices the acceptance criteria into separate test-then-code cycles, so this section sets the bar each cycle's test must clear — it doesn't restate the criteria. - Refactor scope (architecture-improvement specs only) — when the spec's goal is to change existing structure (deepen a module, consolidate, extract, re-seam), name the existing modules / files / boundaries that are intentionally in play, each with the
pathand one line on the reshape intended. This is the explicit allowlist that opens those modules to redesign downstream; anything not listed keeps its current boundary. Tests move with the seam: name the existing tests the reshape supersedes — the plan deletes them and writes new ones at the deepened interface, rather than layering new over old. Omit this section entirely for ordinary feature/fix specs. - Out of scope — what was discussed and explicitly punted.
Flow
Create a task for each step below and mark each one complete as you finish it — update them live as you go, not in a batch at the end — so the user can watch progress.
1. Ground in the codebase
Before drafting Technical decisions, dispatch standard subagents in parallel — one per layer the change touches (database, api, frontend, tests, etc.) — to find the existing surface in the codebase. Pass each one this brief verbatim:
Find one or two existing features that do something analogous and report:
- the exact surface they use (database, api, frontend, tests, etc.);
- the
file:lineof that surface; - one sentence on the convention you observed;
- any canonical helper or utility an implementer would be expected to reuse for this work (
file:line), if one exists.
Don't propose a design — just surface what already exists. If no analogous surface exists, say so.
Synthesize their findings into the Technical decisions section. The spec inherits the surfaces they reported. A spec that says "the handler calls db.update(...) directly" when the investigator found every analogous endpoint routes through repo.X has already shipped an idiom-break that code review will catch.
2. Grill the technical decisions
Grounding tells you what already exists; grilling settles what's still open. After grounding, before you draft Technical decisions, list the technical decisions that are both material (they change the shape of the implementation) and unsettled (the conversation didn't land them and the grounding subagents didn't answer them).
Interview the user on each, one question at a time, in plain chat text — not the structured-question UI, so you have room to show your reasoning. Lead with your recommended answer and the trade-off it accepts, and wait for the response before the next question. Offer discrete options to frame a genuine choice, never as a neutral menu — your pick still leads.
This is targeted, not a fresh interview — only the open technical decisions, and only the ones the codebase can't answer for you. Explore first: if a subagent can settle a question, dispatch one (see References → Subagents) rather than spending the user's attention on it. If the conversation came through crank:crank-brainstorm, the brief's Open questions list is your agenda — walk it. Resolve every material item before drafting: a decision you grill into the open now is one the adversarial reviewer and the plan don't have to re-litigate, and one less Assumption: line standing in for a real choice.
Don't re-open decisions the conversation already settled, and don't grill on detail the chosen idiom dictates — if grounding found the surface, follow it. Grill where the call is genuinely the user's: a trade-off between viable options, a constraint only they know, a priority that tips the design.
3. Draft
Read SPEC-TEMPLATE.md, then write the spec to the temp file, section by section per Deliverables, scaled to the topic. Before locking Technical decisions, apply the Simplify first and Design lens guidelines (see Guidelines) to every in-scope module.
4. Adversarially review
Read SPEC-REVIEW-BRIEF.md. Spawn one heavy subagent resolved per SUBAGENT-TIERS.md and pass it the spec's absolute path plus the brief verbatim.
Quote the reviewer's summary line back to the user.
5. Hand back
Ask first, then offer the file menu. Before rendering anything, ask the user whether they'd like an interactive HTML review of the spec. Recommend it (it's the easiest way to comment per acceptance criterion and tick scope cuts back in), but render the HTML only if they say yes. Then offer the file menu either way.
Open the review (only if the user opted in). Read the rendering guide in this skill's directory — HTML-REVIEW.md — then follow it to render the spec as the .html sibling of the temp file and open it — give each acceptance criterion its own comment box. Tell the user the HTML path and that they can comment per section, tick any out-of-scope cut that should be in, hit Export comments →, and paste the block back — you'll apply it to the spec and re-render.
In chat prose, offer:
- Keep the temp file (default) — the path is already known; user can hand it to the plan skill, feed it elsewhere, or move it later.
- Copy into the repo — copy to a user-named path under the working directory.
- Print inline and delete — paste the final contents into the chat and remove the temp file (and its
.htmlsibling, if one was rendered).
When the user pastes a block beginning > Source: <path>, apply it per HTML-REVIEW.md's "Applying a pasted review" — in a spec, a requested IN scope item folds in as a real acceptance criterion / decision (or push back with a reason; never drop it silently).
Then stop. Do not auto-invoke other skills or continue past the handback.