| name | draft-plan |
| disable-model-invocation | false |
| argument-hint | [output FILE] [rounds N] [auto] [brainstorm|quiz] <description...> |
| description | Draft a high-quality plan through iterative adversarial review: research, draft, review, devil's-advocate, refine — repeated until convergence. Output is a plan file ready for /run-plan execution. |
| metadata | {"version":"2026.06.04+53c471"} |
/draft-plan [output FILE] [rounds N] <description...> — Adversarial Plan Drafter
Produces high-quality plan documents through iterative adversarial
refinement. Multiple agents research, draft, review, poke holes, and refine
until the plan is solid enough to execute with /run-plan.
The insight: /run-plan executes a plan faithfully — so plan quality IS
output quality. Investing in adversarial plan refinement pays off massively
downstream. A weak plan executed perfectly is still a weak result.
Ultrathink throughout.
Preflight — top-level dispatch required
This skill internally dispatches reviewer + devil's-advocate + refiner sub-agents in parallel. It MUST run in a context that has the Agent (or Task) tool available.
Before doing any other work, verify your tool list contains Agent or Task. If neither is present, STOP and report:
ERROR: /draft-plan requires top-level Agent dispatch capability. This invocation is running as a subagent (no Agent tool — verified by inspecting your tool list). Subagents cannot dispatch sub-subagents (Anthropic design — https://code.claude.com/docs/en/sub-agents).
Re-invoke as one of:
- User slash command:
/draft-plan <description...>
- Top-level
Skill tool: Skill(skill="draft-plan", args="<description...>")
- Inline orchestration by a top-level Claude that has
Agent
Do NOT continue. Single-agent inline degradation produces rubber-stamp findings without the adversarial diversity this skill's value depends on. The CLAUDE.md memory anchor feedback_multi_agent_skills_top_level.md is the recurring failure mode this preflight catches.
Do not proceed past this preflight without Agent access.
Worktree preamble — ensure isolation when main is protected
Before parsing arguments, ensure a worktree exists when main is protected.
Dispatch the canonical helper; if main_protected: true and the caller is
in main, the helper creates a worktree and prints its path on stdout
(empty string when no worktree is required). When non-empty, cd into it
and export ZSKILLS_PATHS_ROOT so downstream path-resolution anchors on
the worktree, not main:
MAIN_ROOT=$(cd "$(git rev-parse --git-common-dir)/.." && pwd)
TOPLEVEL=$(git rev-parse --show-toplevel)
HELPER="$ZSKILLS_SKILLS_ROOT/create-worktree/scripts/ensure-worktree.sh"
if [ ! -x "$HELPER" ]; then
echo "draft-plan: ensure-worktree.sh missing at $HELPER — run /update-zskills to repair" >&2
exit 11
fi
if [ -z "${OUTPUT_FILE:-}" ]; then
if [ -f "${CLAUDE_PLUGIN_ROOT}/skills/update-zskills/scripts/zskills-paths.sh" ]; then
export CLAUDE_PLUGIN_ROOT="${CLAUDE_PLUGIN_ROOT}"
. "${CLAUDE_PLUGIN_ROOT}/skills/update-zskills/scripts/zskills-paths.sh"
else
. "$CLAUDE_PROJECT_DIR/.claude/skills/update-zskills/scripts/zskills-paths.sh"
fi
for tok in $ARGUMENTS; do
case "$tok" in
*/*.md) OUTPUT_FILE="$tok"; break ;;
*.md) OUTPUT_FILE="$ZSKILLS_PLANS_DIR/$tok"; break ;;
esac
done
: "${OUTPUT_FILE:=$ZSKILLS_PLANS_DIR/$(date +%Y%m%d-%H%M%S)_PLAN.md}"
fi
TRACKING_ID=$(basename "$OUTPUT_FILE" .md | tr '[:upper:]' '[:lower:]' | tr '_' '-')
WT_PATH=$(bash "$HELPER" \
--prefix draftplan \
--pipeline-id "draft-plan.${TRACKING_ID}" \
--allow-resume \
--purpose "draft-plan auto-worktree; isolate plan drafting from main" \
"${TRACKING_ID}")
RC=$?
if [ "$RC" -ne 0 ]; then
echo "ensure-worktree failed (rc=$RC) for /draft-plan" >&2
exit "$RC"
fi
if [ -n "$WT_PATH" ]; then
cd "$WT_PATH" || { echo "draft-plan: cd $WT_PATH failed" >&2; exit 1; }
export ZSKILLS_PATHS_ROOT="$WT_PATH"
fi
Arguments
/draft-plan [output FILE] [rounds N] [auto] [brainstorm|quiz] <description...>
- output FILE (optional) — where to write the plan. Default:
$ZSKILLS_PLANS_DIR/<slug-from-description>.md
- rounds N (optional) — max review/refine cycles. Default: 3. The
process exits early if a round converges (no substantive new issues).
- auto (optional positional token) — after the worktree auto-commit
succeeds in Phase 6, dispatch
/land-pr to push the branch, open a PR,
monitor CI, and auto-merge. Without auto, the plan is committed in the
worktree but no PR is opened — caller must land manually. Mirrors the
auto token in /run-plan, /do, /fix-issues.
- description (required) — everything after the recognized keywords.
Can be brief ("add dark mode") or detailed ("implement thermal domain
with conduction, convection, radiation, and multi-domain coupling to
electrical via Joule heating").
Detection: scan $ARGUMENTS from the start for recognized patterns:
output followed by a path — explicit output file
- The first token ending in
.md — output file (only when it's the
first argument, before the description starts). This avoids false
positives on description words like README.md or CLAUDE.md.
If the token contains /, use as-is; otherwise resolve via
$ZSKILLS_PLANS_DIR/<token> (sourcing
.claude/skills/update-zskills/scripts/zskills-paths.sh from the
orchestrator's bash fence; falls back to plans/ when config silent).
rounds followed by a number — max review cycles
auto (whitespace-anchored, case-insensitive) — sets AUTO_FLAG=1
for Phase 6's /land-pr dispatch
brainstorm | quiz (a single mutually-exclusive steering selector,
recognized ONLY as a leading flag token — in the flag cluster before
the description begins, like output/rounds/auto, case-insensitive) —
sets STEERING_MODE to one of brainstorm or quiz (default: empty).
brainstorm loads the interactive brainstorm dialogue
(references/brainstorm.md) before Phase 1; quiz conducts an interactive
requirements interview before drafting by loading (references/quiz.md).
Both are pre-draft interactive interviews occupying the SAME steering seam,
so they are mutually exclusive — requesting both (in either order,
/draft-plan brainstorm quiz … or /draft-plan quiz brainstorm …) is a
fail-loud error (exit 2), not a silent mode drop (#936, #944). A
brainstorm/quiz appearing within the description text is part of
the description, NOT the flag — unlike auto, the selector is NOT detected
match-anywhere, so a description like "build a quiz app" or "Build a
brainstorm app for kids" does NOT trigger either mode (the leading-cluster
scan breaks at the first non-flag token; see the detection note below and
the Examples block) (#914). The exact-match case arms also mean
brainstorming/brainstormed/brainstorms/quizzes do NOT trigger.
- Everything else (from the first unrecognized non-flag token onward) is
the description
AUTO_FLAG=0
if [[ "$ARGUMENTS" =~ (^|[[:space:]])[aA][uU][tT][oO]($|[[:space:]]) ]]; then
AUTO_FLAG=1
fi
STEERING_MODE is detected by LEADING-FLAG recognition — NOT auto's
match-anywhere regex. brainstorm/quiz fire only when they appear
in the leading flag cluster, before the first description token. If a
description word "quiz"/"brainstorm" set the mode (the way a description
word "auto" trips AUTO_FLAG today — a pre-existing exposure NOT inherited
here), then an autonomously-dispatched call from /research-and-plan (which
passes the user description verbatim) or the /run-plan delegate
(/draft-plan plans/FOO.md <desc> auto) would hang the pipeline on an
interactive prompt with no human to answer. So the parse consumes the
leading cluster first and only then tests for the steering tokens. Tokenize
the leading cluster: consume, in any order, the output-file token
(either the literal output <path>, OR a bare leading *.md token),
rounds N (consuming the numeric value token after rounds, so the bare
N is not mis-read as the first description token), auto, brainstorm,
and quiz. The first token matching none of these begins the
description, and everything from there (including any later
quiz/brainstorm) is description, not a flag.
brainstorm and quiz are mutually exclusive — both are pre-draft
interactive interviews occupying the SAME steering seam (brainstorm makes
the post-research steering checkpoint skip, "user already steered in the
dialogue"; quiz REPLACES that same checkpoint with its interview loop,
branch (a) of the 3-way steering checkpoint below), and running both at once
is contradictory (two competing interviews, two separate
/tmp/draft-plan-{brainstorm,quiz}-$TRACKING_ID.md notes files both
claiming to be the pre-draft steering). Rather than represent-then-forbid
the illegal "both" state with two booleans, a single STEERING_MODE (one of
"", brainstorm, quiz) makes it unrepresentable: a set_steering
helper sets the mode the first time, and FAILS LOUD (exit 2) if a second,
DIFFERENT steering token is seen. Because both tokens are recognized by the
SAME leading-cluster scan (no token[0]-only anchor for brainstorm), the
conflict is detected regardless of order — /draft-plan brainstorm quiz X
and /draft-plan quiz brainstorm X BOTH exit 2 (#936, #944 — genuine
order-independence; the prior two-parser design only errored on the first
order). A repeated SAME token (brainstorm brainstorm X) is a harmless
no-op:
STEERING_MODE=""
set_steering() {
if [ -z "$STEERING_MODE" ]; then
STEERING_MODE="$1"
elif [ "$STEERING_MODE" != "$1" ]; then
echo "ERROR: brainstorm and quiz modes are mutually exclusive" >&2
exit 2
fi
}
expect_value=0
for tok in $ARGUMENTS; do
if [ "$expect_value" = "1" ]; then
expect_value=0
continue
fi
ltok=$(printf '%s' "$tok" | tr '[:upper:]' '[:lower:]')
case "$ltok" in
output) expect_value=1 ;;
rounds) expect_value=1 ;;
auto) ;;
brainstorm) set_steering brainstorm ;;
quiz) set_steering quiz ;;
*.md) ;;
*/*.md) ;;
*) break ;;
esac
done
Examples:
/draft-plan Add dark mode to the editor
/draft-plan THERMAL_PLAN.md Implement thermal domain → writes $ZSKILLS_PLANS_DIR/THERMAL_PLAN.md
/draft-plan plans/THERMAL_PLAN.md Implement thermal domain → same
/draft-plan output plans/THERMAL_PLAN.md rounds 5 Implement thermal domain
/draft-plan rounds 5 Implement thermal domain with multi-domain coupling
/draft-plan Fix the README.md formatting → description only, no output file detected
/draft-plan brainstorm Add dark mode to the editor → STEERING_MODE=brainstorm (leading flag) — runs the interactive brainstorm dialogue first
/draft-plan quiz Add dark mode to the editor → STEERING_MODE=quiz (leading flag) — runs the interactive requirements interview first
/draft-plan output p.md quiz rounds 5 Add dark mode → STEERING_MODE=quiz (quiz is in the leading flag cluster, in any order with output/rounds)
/draft-plan output p.md brainstorm Add dark mode → STEERING_MODE=brainstorm (brainstorm is in the leading flag cluster — composability parity with quiz; #944 — no longer token[0]-only)
/draft-plan output p.md build a quiz app → STEERING_MODE empty — "quiz" is a description word, not the leading flag (NEGATIVE example; the selector is leading-only, never match-anywhere like auto)
/draft-plan Build a brainstorm app for kids → STEERING_MODE empty — "brainstorm" is a description word; the leading-cluster scan breaks at the first non-flag token "Build" before it is reached (#914)
/draft-plan add dark mode quiz → STEERING_MODE empty — a trailing quiz is description text, not the flag. This is the accepted ergonomic limitation of leading-only parsing: to enable a mode, lead with the flag (/draft-plan quiz add dark mode).
/draft-plan brainstorm quiz Add dark mode → ERROR (exit 2) — both brainstorm and quiz are leading-cluster flags and the two modes are mutually exclusive (#936, #944). Fails loud instead of silently dropping one.
/draft-plan quiz brainstorm Add dark mode → ERROR (exit 2) — same conflict, OTHER order. Because both tokens are recognized by the same leading-cluster scan, the mutual-exclusion error is order-independent (#944 — the prior two-parser design errored only on the brainstorm quiz order and silently swallowed brainstorm here).
Pre-check — Existing file
If the output file already exists, read it first. The old plan IS research
input — it contains the original intent, structure, and possibly partial
progress. Tell the research agents: "An existing plan file exists at
<path>. Read it and incorporate its intent. This is a modernization,
not a blank-slate rewrite." The adversarial review should check that no
intent from the original was lost.
This handles the common case of modernizing old-format plans:
/draft-plan plans/OLD_PLAN.md Modernize with progress tracker and phases
Brainstorm mode (only when the brainstorm flag is present)
If STEERING_MODE = brainstorm, Read references/brainstorm.md via the
Read tool and execute its dialogue loop now, before Phase 1 — UNLESS the notes file
/tmp/draft-plan-brainstorm-$TRACKING_ID.md already exists with status: ready (a prior,
completed dialogue — proceed straight to Phase 1 using it as the seed). If the notes file
exists with status: in-progress (a dialogue interrupted by compaction/crash), RESUME it from
its captured state rather than restarting. If STEERING_MODE != brainstorm, ignore the reference file
entirely and proceed straight to Phase 1.
Quiz mode (only when the quiz flag is present) — research sequencing
When STEERING_MODE = quiz and running interactively (the same predicate as the
subagent-skip in "Post-research tracking" below — quiz never fires under a
subagent caller in practice, since the research-and-* callers don't pass
quiz), the conversational requirements interview runs FIRST, before any
research. The order is strictly: quiz interview → (on go-signal) the
existing Phase-1 research fan-out, seeded → Phase 2 draft. No research is
split, moved, or interleaved — the fan-out is simply gated behind the quiz
and seeded with the interview file. Concretely:
- Defer the research fan-out until the quiz produces its go-signal. Do
NOT dispatch the Phase-1 Research agents while
STEERING_MODE = quiz and the quiz
has not yet exited.
- Conduct the interview now. The conditional-load directive and the
quiz loop are emitted at the "Post-research tracking" seam below (Work
Item 5's 3-way branch); when
STEERING_MODE = quiz that seam is reached with the
research fan-out still deferred, the directive fires, and the interview
runs to its explicit go-signal.
- On the go-signal, run the existing Phase-1 fan-out UNCHANGED
(the same agents in "Research agents" below), with one additive behavior:
seed each fan-out agent with the durable interview file
/tmp/draft-plan-quiz-$TRACKING_ID.md as priors — "here is what the
interview established with the user; validate and extend it, do not
re-derive it." Also hand the fan-out the interview file's deferred
open-questions list so the Codebase / Prior-art agents resolve them. This
is the same seeding shape brainstorm mode uses (injecting the notes path
into each research prompt) — inject the literal
/tmp/draft-plan-quiz-$TRACKING_ID.md path into the prompts, never a
re-derived one.
- Then Phase 2 drafts from the seeded research.
When STEERING_MODE != quiz, research runs exactly as today — the full Phase-1
fan-out runs immediately, followed by the single-shot post-research
checkpoint. quiz.md is never read and no step.*.quiz marker is written.
The interview mechanics (loop state machine, question-style discipline,
go-signal contract, persistence + recovery-read, transcript-capture
distillation) live entirely in
references/quiz.md; this SKILL.md only orders
quiz-before-fan-out and passes the interview file in.
Phase 1 — Research (parallel agents)
Tracking fulfillment
Determine the tracking ID: use the ID passed by the parent skill if this
is a delegated invocation, or derive from the output file slug if standalone
(e.g., plans/FEATURE_PLAN.md → feature-plan). Create the fulfillment
file in the MAIN repo. The pipeline subdir follows Option B layout: when
delegated, parent exports ZSKILLS_PIPELINE_ID via env and we write into
the parent's subdir; when standalone, we own our own subdir:
if [ -f "${CLAUDE_PLUGIN_ROOT}/skills/update-zskills/scripts/zskills-resolve-config.sh" ]; then
export CLAUDE_PLUGIN_ROOT="${CLAUDE_PLUGIN_ROOT}"
. "${CLAUDE_PLUGIN_ROOT}/skills/update-zskills/scripts/zskills-resolve-config.sh"
else
. "$CLAUDE_PROJECT_DIR/.claude/skills/update-zskills/scripts/zskills-resolve-config.sh"
fi
MAIN_ROOT=$(cd "$(git rev-parse --git-common-dir)/.." && pwd)
PIPELINE_ID="${ZSKILLS_PIPELINE_ID:-draft-plan.$TRACKING_ID}"
PIPELINE_ID=$(bash "$ZSKILLS_SKILLS_ROOT/create-worktree/scripts/sanitize-pipeline-id.sh" "$PIPELINE_ID")
[ -n "$PIPELINE_ID" ] || { echo "tracking: empty PIPELINE_ID — refusing flat write" >&2; exit 1; }
mkdir -p "$MAIN_ROOT/.zskills/tracking/$PIPELINE_ID"
printf 'skill: draft-plan\nid: %s\noutput: %s\nstatus: started\ndate: %s\n' \
"$TRACKING_ID" "$OUTPUT_FILE" "$(TZ="${TIMEZONE:-UTC}" date -Iseconds)" \
> "$MAIN_ROOT/.zskills/tracking/$PIPELINE_ID/fulfilled.draft-plan.$TRACKING_ID"
Research agents
Dispatch multiple Explore agents in parallel to investigate the problem space.
Each agent gets the full description and a specific research focus:
Brainstorm feed-forward. When STEERING_MODE = brainstorm, inject the literal
path /tmp/draft-plan-brainstorm-$TRACKING_ID.md into EACH research
agent's PROMPT, with an instruction to read it as the primary design
seed (the user already steered the design in the brainstorm dialogue).
Inject the path into the prompts — NOT into the research consolidation
file, which does not exist until after dispatch. The Codebase / Patterns /
Prior-art agents still run to ground the design against the repo; the
Domain agent's "what to build" work is largely pre-answered by the
brainstorm and may be reduced to verification. When STEERING_MODE != brainstorm,
dispatch the agents normally with no notes-file injection.
-
Codebase agent — find all relevant source files, understand current
architecture, identify what exists and what needs to change. Map
dependencies, shared infrastructure, and potential conflicts.
-
Patterns agent — read existing plans in plans/ for format and style
reference. Read CLAUDE.md for constraints (no external solvers, no
bundlers, etc.). Identify conventions and rules that the plan must respect.
-
Domain agent — research the specific technical domain. For solver
work: understand the math, numerical methods, stability requirements.
For UI work: understand the interaction model, accessibility, existing
patterns. For infrastructure: understand the deployment, CI, hosting.
-
Prior art agent — check git history for related work, previous
attempts, known issues. Read any existing plan files or progress docs
that overlap with the description. Find relevant GitHub issues.
Consolidate the research into a single summary and write it to a file
(e.g., /tmp/draft-plan-research-<slug>.md). The <slug> comes from the
output filename if one was provided (e.g., FEATURE_EXPORT
→ /tmp/draft-plan-research-FEATURE_EXPORT.md). If no output
file was given, derive from the description. Do not rely on keeping the
research in memory — context compaction will degrade it across multiple
rounds of adversarial review. The file persists through all phases.
The summary should cover:
- What exists today (relevant code, architecture)
- What needs to change or be built
- Constraints and rules that apply
- Prior art and lessons learned
- Open questions or uncertainties
Similarly, write each round's review findings to files:
/tmp/draft-plan-review-<slug>-round-N.md — reviewer + devil's advocate findings
- Pass these file paths to the refiner agent so it has the full context
Scope check — is this too big for one plan? After consolidating
research, assess whether you can write a plan where every phase has
specific, implementable work items and testable acceptance criteria,
in roughly 6 or fewer phases. The question is not "can I list phases"
but "can I spec each phase precisely enough that an implementing agent
won't have to guess?"
Signs the task is too big for one plan:
- You'd need 8+ phases to cover the scope properly
- Phases would be vague ("implement thermal domain") because the breadth
prevents precise specs for each one
- The task contains 2+ sub-problems that share no files or infrastructure
- You find yourself thinking "Phase 4 will probably need to be broken
into sub-phases at implementation time"
Past failure: a Dashboard Blocks plan covered too much — phases were
vague, the implementing agent took shortcuts, and each phase had to be
broken into 5-10 sub-phases at execution time with no adversarial review.
Common pattern: tasks that add multiple block types alongside new engine
infrastructure (new solver, new domain, new codegen path) should
decompose into infrastructure-first plan + block additions via
/add-block delegate phases after the engine lands.
If the task is too big, present this to the user:
This task is too broad for one well-specified plan. It decomposes
into N sub-problems: [list them with one-line descriptions].
I recommend using /research-and-plan to handle the decomposition
and draft focused sub-plans for each. Proceed?
On approval, invoke /research-and-plan with:
- The original description (full, unmodified)
- The research file path (
/tmp/draft-plan-research-<slug>.md)
Then exit — do not proceed to Phase 2. /research-and-plan takes
over from here.
If the user says no (e.g., "just plan the first part"), narrow the scope
per their feedback and proceed to Phase 2 with the focused scope.
Post-research tracking
After consolidating research into the summary file, create the research
step marker:
if [ -f "${CLAUDE_PLUGIN_ROOT}/skills/update-zskills/scripts/zskills-resolve-config.sh" ]; then
export CLAUDE_PLUGIN_ROOT="${CLAUDE_PLUGIN_ROOT}"
. "${CLAUDE_PLUGIN_ROOT}/skills/update-zskills/scripts/zskills-resolve-config.sh"
else
. "$CLAUDE_PROJECT_DIR/.claude/skills/update-zskills/scripts/zskills-resolve-config.sh"
fi
MAIN_ROOT=$(cd "$(git rev-parse --git-common-dir)/.." && pwd)
PIPELINE_ID="${ZSKILLS_PIPELINE_ID:-draft-plan.$TRACKING_ID}"
PIPELINE_ID=$(bash "$ZSKILLS_SKILLS_ROOT/create-worktree/scripts/sanitize-pipeline-id.sh" "$PIPELINE_ID")
[ -n "$PIPELINE_ID" ] || { echo "tracking: empty PIPELINE_ID — refusing flat write" >&2; exit 1; }
printf 'completed: %s\n' "$(TZ="${TIMEZONE:-UTC}" date -Iseconds)" \
> "$MAIN_ROOT/.zskills/tracking/$PIPELINE_ID/step.draft-plan.$TRACKING_ID.research"
Skip this steering checkpoint when STEERING_MODE = brainstorm — the user
already steered the design, in depth, during the brainstorm dialogue, so
re-prompting here is redundant; proceed directly to Phase 2. (This is an
ADDITIVE condition; the existing "if running as a subagent, skip" branch
below is unchanged.)
This steering checkpoint is a 3-way branch. The branch sits AFTER the
scope-check decomposition exit above and BEFORE Phase 2:
(a) When STEERING_MODE = quiz and running interactively (the same predicate
as the subagent-skip in branch (c) below — interactive, not a subagent):
emit the conditional-load directive
Read references/quiz.md in full and follow its
procedure end-to-end. Do not proceed until you have read that file.
then conduct the quiz loop per that file, exiting to Phase 2 only on the
explicit go-signal. Note: per "Quiz mode" above, this branch is reached
with the Phase-1 research fan-out still DEFERRED — the interview runs first,
and the (seeded) fan-out runs only after the go-signal, before Phase 2.
(b) When STEERING_MODE != quiz (default), the existing single-shot checkpoint,
verbatim and unchanged:
Present the research summary to the user. If running interactively
(user invoked /draft-plan directly), wait for input:
Research complete. Summary written to /tmp/draft-plan-research-<slug>.md.
Here's the overview: [brief summary]
[Scope check result — either "this fits in one plan" or the decomposition recommendation above]
Anything to add, correct, or steer before I draft the plan?
Incorporate any user feedback, then immediately proceed to Phase 2.
Do not stop here. The checkpoint is a pause for steering, not the end of
the skill. After the user responds (even if they just say "looks good" or
"continue"), move to Phase 2 without being asked again.
(c) If running as a subagent (dispatched by /research-and-plan or
/research-and-go), skip the user checkpoint — proceed directly to
Phase 2. The decomposition was already approved by the user in the
parent skill. (Quiz never reaches this branch in practice since the
research-and-* callers don't pass quiz, but the guard is the same
predicate and must remain — it is load-bearing for those callers.)
Quiz exit — transcript capture + optional step marker
These steps run only when STEERING_MODE = quiz (i.e., branch (a) above ran the
quiz to its go-signal). A normal (non-quiz) run does NONE of this.
-
Transcript-capture merge (R5). After the quiz exits, merge the
distilled requirements into the existing research summary file
/tmp/draft-plan-research-<slug>.md as confirmed requirements (so each
seeded fan-out agent treats the interview-established intent as a given to
validate and extend), and — in Phase 6 — add a "Requirements captured
via quiz" subsection to the plan's ## Plan Quality section. Follow the
transcript-capture distillation spec in
references/quiz.md ("Transcript capture — distill,
don't dump"); do NOT duplicate that spec here — distill decisions plus
rationale, never a raw chat dump.
-
Optional step.*.quiz tracking marker (convention symmetry with the
research/review/finalize markers; informational only, NOT hook-gated).
Gated behind STEERING_MODE = quiz so a normal run never emits it. This clones the
FULL research-marker fence above — including the MAIN_ROOT +
PIPELINE_ID derivation and the pipeline-id sanitize call — NOT just
the printf, to avoid an empty-PIPELINE_ID flat-marker write:
if [ "${STEERING_MODE:-}" = "quiz" ]; then
if [ -f "${CLAUDE_PLUGIN_ROOT}/skills/update-zskills/scripts/zskills-resolve-config.sh" ]; then
export CLAUDE_PLUGIN_ROOT="${CLAUDE_PLUGIN_ROOT}"
. "${CLAUDE_PLUGIN_ROOT}/skills/update-zskills/scripts/zskills-resolve-config.sh"
else
. "$CLAUDE_PROJECT_DIR/.claude/skills/update-zskills/scripts/zskills-resolve-config.sh"
fi
MAIN_ROOT=$(cd "$(git rev-parse --git-common-dir)/.." && pwd)
PIPELINE_ID="${ZSKILLS_PIPELINE_ID:-draft-plan.$TRACKING_ID}"
PIPELINE_ID=$(bash "$ZSKILLS_SKILLS_ROOT/create-worktree/scripts/sanitize-pipeline-id.sh" "$PIPELINE_ID")
[ -n "$PIPELINE_ID" ] || { echo "tracking: empty PIPELINE_ID — refusing flat write" >&2; exit 1; }
printf 'completed: %s\n' "$(TZ="${TIMEZONE:-UTC}" date -Iseconds)" \
> "$MAIN_ROOT/.zskills/tracking/$PIPELINE_ID/step.draft-plan.$TRACKING_ID.quiz"
fi
Phase 2 — Draft
A single agent produces the initial plan based on the consolidated research
and user feedback. The plan MUST follow a format that /run-plan can
consume:
Required plan structure
Every plan file MUST begin with YAML frontmatter so that /run-plan can
track metadata (especially which GitHub issue to close on completion):
---
issue: N
title: Plan Title
created: YYYY-MM-DD
status: active
---
Frontmatter rules:
issue — include ONLY when /draft-plan is invoked from /fix-issues plan
(or any context that supplies a GitHub issue number). When invoked standalone
with no issue context, omit the issue: field entirely.
title — the plan title. Because the frontmatter is the authoritative
reference, do NOT duplicate the issue number in the # Plan: <Title> heading.
created — use the current date (YYYY-MM-DD).
status — always starts as active. /run-plan updates this to
complete when all phases finish, which also signals it to close the
linked GitHub issue (if one is present).
Phase heading format. Use ## Phase <N> — <Name> with an em-dash
(—) as the canonical separator. The aggregator (collect.py) also
accepts en-dash (–), colon (:), and hyphen (-) so hand-authored
plans are not silently demoted to Reference (issue #183), but every
template in this skill emits em-dash for consistency.
# Plan: <Title>
## Overview
Brief description of what this plan accomplishes and why.
## Progress Tracker
| Phase | Status | Commit | Notes |
|-------|--------|--------|-------|
| 1 — <name> | ⬚ | | |
| 2 — <name> | ⬚ | | |
| ... | ⬚ | | |
## Phase 1 — <Name>
### Goal
One-sentence summary of what this phase accomplishes.
### Work Items
- [ ] Item 1 — specific, implementable description
- [ ] Item 2 — ...
### Design & Constraints
Verbatim specs: formulas, state equations, algorithms, data structures,
API contracts. Everything an implementing agent needs to write the code
without guessing. If there are formulas, write the formulas. If there are
constraints, state them explicitly.
### Acceptance Criteria
- [ ] Criterion 1 — specific, testable condition
- [ ] Criterion 2 — e.g., "test free vibration x(t) = A cos(ωt) within 1e-6"
### Dependencies
Which phases must be complete before this one can start.
## Phase 2 — <Name>
...
Drafting rules
- Be specific, not vague. "Implement the Spring component using F = -kx
with internal displacement state" — not "implement springs."
- Include formulas and algorithms verbatim. The implementing agent will
have ONLY the plan text. If the formula isn't in the plan, the agent will
guess it wrong.
- Acceptance criteria must be testable. "Works correctly" is not testable.
"Test produces output matching analytical solution within 1e-6 tolerance"
is testable.
- Phase boundaries at natural breaks. Each phase should be independently
implementable and verifiable. Don't put shared infrastructure in Phase 3
if Phases 1 and 2 need it.
- Size phases for agent execution. Each phase should be completable by
one agent in under 2 hours: implement, test, verify, commit. Scope by
concrete units (components, files, features) — NOT by time estimates.
A phase with ~3-5 components and ~500 lines of new code is right. A
phase with 15 components or 2000+ lines should be split. Do NOT use
time estimates ("this will take 2 weeks") — LLMs cannot estimate time.
Use scope: number of components, files touched, test count.
- Estimate scope honestly. If a phase has 7 components, list all 7. Don't
say "implement key components" and hope the agent picks the right ones.
- Dependencies must be explicit. If Phase 3 needs the base class from
Phase 1, say so.
Phase 3 — Adversarial Review (parallel)
Dispatch two agents simultaneously against the current draft. Each gets the
full draft AND the research summary from Phase 1.
Reviewer agent
Checks for completeness, correctness, and feasibility:
- Are all work items specific enough to implement without guessing?
- Do acceptance criteria cover every work item?
- Are dependencies correct and complete?
- Is the phase ordering optimal? (shared infrastructure first?)
- Are there missing phases? (tests? documentation? integration?)
- Is the scope realistic per phase? (1000+ lines each is the norm)
- Does the plan respect all constraints from CLAUDE.md and research?
- Are formulas and algorithms correct?
- Will
/run-plan be able to parse phases and status from this format?
Evidence discipline. When a finding makes an empirical claim (a
file, function, tool, or library has/lacks property X), include a
concrete Verification: line — the exact file:line, grep, schema
quote, or command output that reproduces the evidence. The refiner will
re-run these checks before acting. Structural/judgment findings don't
need a reproducer but should say Verification: judgment — no verifiable anchor explicitly. Never write an empirical-sounding claim without
something the refiner can independently re-check.
Devil's advocate agent
Genuinely adversarial — tries to find ways the plan will fail:
- Wrong assumptions — what does the plan assume that might not be true?
("Assumes the solver handles stiff systems" — does it actually?)
- Missing edge cases — what will break at implementation time that the
plan doesn't address? ("What happens when the spring constant is zero?")
- Deferred hard parts — are the difficult items buried in later phases
or hidden behind vague language? ("Phase 4: integrate everything" — that's
where all the complexity is.)
- Hidden dependencies — does Phase 3 actually need something from Phase 2
that isn't listed as a dependency?
- Overly optimistic scope — is "implement 7 components" in one phase
actually feasible? Should it be split?
- Specification gaps — "when an agent tries to implement Phase 2b using
ONLY the text in this plan, what will go wrong? What will it have to guess?"
- Integration risks — will phases that work in isolation break when
combined? What integration testing is missing?
- Constraint violations — does any phase implicitly require an external
library, a build step, or something else CLAUDE.md prohibits?
The devil's advocate must produce specific, actionable findings — not
generic concerns. "Phase 3 might be complex" is useless. "Phase 3 says
'implement the DAE solver' but doesn't specify the index reduction method,
matrix factorization approach, or convergence criteria — the agent will have
to guess all three" is actionable.
The same evidence discipline from the reviewer section applies:
every empirical claim needs a Verification: line. The devil's advocate
is especially prone to generating plausible-sounding-but-false claims
because its job is pattern-generating failure modes, not verifying them.
Discipline is load-bearing here — the refiner will re-check, and claims
whose evidence doesn't reproduce will not drive fixes.
Post-review tracking
After both reviewer and devil's advocate agents return their findings,
create the review step marker:
if [ -f "${CLAUDE_PLUGIN_ROOT}/skills/update-zskills/scripts/zskills-resolve-config.sh" ]; then
export CLAUDE_PLUGIN_ROOT="${CLAUDE_PLUGIN_ROOT}"
. "${CLAUDE_PLUGIN_ROOT}/skills/update-zskills/scripts/zskills-resolve-config.sh"
else
. "$CLAUDE_PROJECT_DIR/.claude/skills/update-zskills/scripts/zskills-resolve-config.sh"
fi
MAIN_ROOT=$(cd "$(git rev-parse --git-common-dir)/.." && pwd)
PIPELINE_ID="${ZSKILLS_PIPELINE_ID:-draft-plan.$TRACKING_ID}"
PIPELINE_ID=$(bash "$ZSKILLS_SKILLS_ROOT/create-worktree/scripts/sanitize-pipeline-id.sh" "$PIPELINE_ID")
ROUND="${ROUND:-1}"
[ -n "$PIPELINE_ID" ] || { echo "tracking: empty PIPELINE_ID — refusing flat write" >&2; exit 1; }
printf 'round: %s\ncompleted: %s\n' "$ROUND" "$(TZ="${TIMEZONE:-UTC}" date -Iseconds)" \
> "$MAIN_ROOT/.zskills/tracking/$PIPELINE_ID/step.draft-plan.$TRACKING_ID.review"
Phase 4 — Refine
A single agent receives:
- The current draft
- The reviewer's findings
- The devil's advocate's findings
Verify-before-fix (mandatory)
Before touching the draft, the refiner must attempt to reproduce the
cited evidence for each finding. The reviewer/DA produce hypotheses;
the refiner is the gate that tests them against reality.
For each finding with an empirical claim:
- Read the
Verification: line and run its check (Read the file, run
the grep, check the schema, run the command).
- Record outcome: Verified (evidence reproduces → act on finding),
Not reproduced (evidence does not match reality → justify, do NOT
fix based on this finding), or No anchor (empirical-sounding
claim without verifiable citation → scrutinize, either locate an
anchor yourself or justify-not-fix).
Judgment findings (marked Verification: judgment) skip step 1 and go
straight to fix-or-justify on merit.
Why this exists. Devil's-advocate findings are by role generated to
be plausible failure modes, not verified truths. Past failure: a DA
claimed a tool's input lacked a field, citing a tangentially-related
file; the refiner accepted and rewrote a whole phase on a false premise.
A 30-second check of the tool schema would have caught it. Verify-before-fix
is the gate that turns "the DA said so" into "I checked."
Address every finding
It produces an improved draft that addresses every finding. For each
finding, it must either:
- Fix it — update the plan to resolve the issue
- Justify — explain why it's not actually a problem (with evidence,
including the "evidence did not reproduce" and "claim not verifiable"
cases from the verify-before-fix block)
It may NOT ignore findings or defer them. The refiner's output is the new
draft for the next round.
The refiner's output must include a disposition table listing each
finding with an Evidence column (Verified / Not reproduced / No anchor
/ Judgment) and the disposition (Fixed / Justified + reason).
Phase 5 — Convergence Check
Convergence is the orchestrator's judgment, not the refiner's self-call. Do NOT accept "CONVERGED" from the refiner agent as authoritative — the refiner just refined; it is biased toward declaring its own work done. This is a recurring failure mode in practice.
After each round of review + refinement:
-
Count remaining substantive issues from the refiner's disposition table (Justified-not-fixed entries plus any gaps the refinement introduced).
-
Check convergence:
- 0 substantive issues → converged → next phase.
- >0 substantive issues AND rounds < max → another review+refine cycle. Honor the user's rounds budget; don't stop early.
- Only short-circuit before max rounds when remaining substantive issues are genuinely 0.
-
Track round history — keep a log of each round's findings and
resolutions. This goes into the final plan's quality section.
Phase 6 — Finalize
-
Add a Plan Quality section to the end of the plan:
## Plan Quality
**Drafting process:** /draft-plan with N rounds of adversarial review
**Convergence:** [Converged at round M / Max rounds reached]
**Remaining concerns:** [None / List of unresolved issues]
### Round History
| Round | Reviewer Findings | Devil's Advocate Findings | Resolved |
|-------|-------------------|---------------------------|----------|
| 1 | 5 issues | 7 issues | 12/12 |
| 2 | 2 issues | 3 issues | 5/5 |
| 3 | 0 issues | 0 issues | Converged|
When STEERING_MODE = quiz, also add a "Requirements captured via quiz"
subsection to this Plan Quality section — a short record (a few lines)
noting the requirements were elicited through a quiz interview and listing
the key decisions/assumptions confirmed. This documents how the plan's
requirements were grounded without reproducing the conversation. Produce it
from the transcript-capture distillation per
references/quiz.md ("Transcript capture — distill,
don't dump"); distill, do NOT dump the raw chat. When STEERING_MODE != quiz, omit
this subsection entirely.
-
Write the plan file to the output path. The user can't review what
they can't read — plans are often too large to meaningfully summarize
in chat. Write first, then let the user read the actual file.
-
Auto-commit the plan file (if in a worktree). When $TOPLEVEL is
a worktree (not main), stage and commit $OUTPUT_FILE so the plan is
captured on the feature branch rather than left as a dirty file in
main. This is the file-write-time backstop that pairs with the
preamble's structural isolation:
[ -n "${WT_PATH:-}" ] && cd "$WT_PATH" 2>/dev/null || true
MAIN_ROOT=$(cd "$(git rev-parse --git-common-dir)/.." && pwd)
TOPLEVEL=$(git rev-parse --show-toplevel)
if [ "$TOPLEVEL" != "$MAIN_ROOT" ]; then
export ZSKILLS_PATHS_ROOT="$TOPLEVEL"
if [ -f "${CLAUDE_PLUGIN_ROOT}/skills/update-zskills/scripts/zskills-paths.sh" ]; then
export CLAUDE_PLUGIN_ROOT="${CLAUDE_PLUGIN_ROOT}"
. "${CLAUDE_PLUGIN_ROOT}/skills/update-zskills/scripts/zskills-paths.sh"
else
. "$CLAUDE_PROJECT_DIR/.claude/skills/update-zskills/scripts/zskills-paths.sh"
fi
case "$OUTPUT_FILE" in
"$MAIN_ROOT"/*) OUTPUT_FILE="$TOPLEVEL/${OUTPUT_FILE#"$MAIN_ROOT/"}" ;;
esac
FILE_REL=""
if FILE_REL=$(realpath --relative-to="$TOPLEVEL" "$OUTPUT_FILE" 2>/dev/null) \
&& [ -n "$FILE_REL" ]; then
case "$FILE_REL" in /*) FILE_REL="" ;; esac
fi
if [ -z "$FILE_REL" ]; then
ABS_FILE=$(cd "$(dirname "$OUTPUT_FILE")" 2>/dev/null && pwd)/$(basename "$OUTPUT_FILE")
case "$ABS_FILE" in
"$TOPLEVEL"/*) FILE_REL="${ABS_FILE#"$TOPLEVEL"/}" ;;
*) echo "draft-plan: cannot normalize $OUTPUT_FILE vs $TOPLEVEL" >&2; exit 1 ;;
esac
fi
case "$FILE_REL" in
/*|../*) echo "draft-plan: $OUTPUT_FILE is outside worktree $TOPLEVEL" >&2; exit 1 ;;
esac
git -C "$TOPLEVEL" add "$FILE_REL"
STAGED=$(git -C "$TOPLEVEL" diff --cached --name-only)
if [ "$STAGED" != "$FILE_REL" ]; then
echo "draft-plan: unexpected staged set: $STAGED (expected $FILE_REL only)" >&2
exit 1
fi
if [ -f "${CLAUDE_PLUGIN_ROOT}/skills/update-zskills/scripts/zskills-resolve-config.sh" ]; then
export CLAUDE_PLUGIN_ROOT="${CLAUDE_PLUGIN_ROOT}"
. "${CLAUDE_PLUGIN_ROOT}/skills/update-zskills/scripts/zskills-resolve-config.sh"
else
. "$CLAUDE_PROJECT_DIR/.claude/skills/update-zskills/scripts/zskills-resolve-config.sh"
fi
BASE=$(basename "$FILE_REL" .md)
case "draft-plan" in
draft-plan) COMMIT_MSG_SUBJECT="docs(plans): draft $BASE" ;;
refine-plan) COMMIT_MSG_SUBJECT="docs(plans): refine $BASE" ;;
draft-tests) COMMIT_MSG_SUBJECT="docs(tests): draft test specs for $BASE" ;;
esac
if [ -n "$COMMIT_CO_AUTHOR" ]; then
git -C "$TOPLEVEL" commit --trailer "Co-Authored-By: $COMMIT_CO_AUTHOR" -m "$COMMIT_MSG_SUBJECT"
else
git -C "$TOPLEVEL" commit -m "$COMMIT_MSG_SUBJECT"
fi
fi
-
Auto-land via /land-pr (when auto was passed). Issue #581: in
projects with execution.landing: pr + main_protected: true, the
plan file is committed in the worktree (above) but never reaches main
without a /land-pr dispatch. When the user passed the auto
positional token, dispatch /land-pr so the branch pushes, a PR
opens, CI runs, and auto-merge lands the plan on main — making
/draft-plan plans/X.md auto && /run-plan plans/X.md auto work end
to end. Without auto, the worktree commit stands and the caller
lands manually.
if [ "${AUTO_FLAG:-0}" = "1" ] && [ "$TOPLEVEL" != "$MAIN_ROOT" ]; then
if [ -f "${CLAUDE_PLUGIN_ROOT}/skills/update-zskills/scripts/zskills-resolve-config.sh" ]; then
export CLAUDE_PLUGIN_ROOT="${CLAUDE_PLUGIN_ROOT}"
. "${CLAUDE_PLUGIN_ROOT}/skills/update-zskills/scripts/zskills-resolve-config.sh"
else
. "$CLAUDE_PROJECT_DIR/.claude/skills/update-zskills/scripts/zskills-resolve-config.sh"
fi
BRANCH_NAME=$(git -C "$TOPLEVEL" rev-parse --abbrev-ref HEAD)
SLUG="$TRACKING_ID"
PR_TITLE="docs(plans): draft $(basename "$OUTPUT_FILE" .md) via /draft-plan"
BODY_FILE="/tmp/pr-body-draft-plan-${SLUG}-$$.md"
RESULT_FILE="/tmp/land-pr-result-draft-plan-${SLUG}-$$.txt"
cat > "$BODY_FILE" <<BODY
Summary
`/draft-plan` produced a plan file at `$OUTPUT_FILE` via $ROUND round(s)
of adversarial review (reviewer + devil's-advocate + refiner).
See the plan's `## Plan Quality` section for round history and remaining
concerns.
Test plan
-
Plan index — do not touch. The plan index is regenerated from
source-of-truth by /plans rebuild and auto-refreshed by /plans
Mode: Show when the source has changed. No manual update needed
here.
-
Present the result:
Plan drafted in N rounds (converged / max rounds reached).
[Remaining concerns if any]
Written to plans/THERMAL_PLAN.md — open it up and let me know
what to change.
Execute with: /run-plan plans/THERMAL_PLAN.md
Or with scheduling: /run-plan plans/THERMAL_PLAN.md auto every 4h now
Post-finalize tracking
After writing the plan file, create the finalize step marker and
update the fulfillment file to complete:
if [ -f "${CLAUDE_PLUGIN_ROOT}/skills/update-zskills/scripts/zskills-resolve-config.sh" ]; then
export CLAUDE_PLUGIN_ROOT="${CLAUDE_PLUGIN_ROOT}"
. "${CLAUDE_PLUGIN_ROOT}/skills/update-zskills/scripts/zskills-resolve-config.sh"
else
. "$CLAUDE_PROJECT_DIR/.claude/skills/update-zskills/scripts/zskills-resolve-config.sh"
fi
MAIN_ROOT=$(cd "$(git rev-parse --git-common-dir)/.." && pwd)
PIPELINE_ID="${ZSKILLS_PIPELINE_ID:-draft-plan.$TRACKING_ID}"
PIPELINE_ID=$(bash "$ZSKILLS_SKILLS_ROOT/create-worktree/scripts/sanitize-pipeline-id.sh" "$PIPELINE_ID")
[ -n "$PIPELINE_ID" ] || { echo "tracking: empty PIPELINE_ID — refusing flat write" >&2; exit 1; }
printf 'completed: %s\n' "$(TZ="${TIMEZONE:-UTC}" date -Iseconds)" \
> "$MAIN_ROOT/.zskills/tracking/$PIPELINE_ID/step.draft-plan.$TRACKING_ID.finalize"
printf 'skill: draft-plan\nid: %s\noutput: %s\nstatus: complete\ndate: %s\n' \
"$TRACKING_ID" "$OUTPUT_FILE" "$(TZ="${TIMEZONE:-UTC}" date -Iseconds)" \
> "$MAIN_ROOT/.zskills/tracking/$PIPELINE_ID/fulfilled.draft-plan.$TRACKING_ID"
Key Rules
- The plan is the spec. Every implementing agent will have ONLY the plan
text. If it's not in the plan, it doesn't exist. Write formulas, not
descriptions of formulas. Write acceptance criteria, not hopes.
- Devil's advocate must be genuinely adversarial. Not a rubber stamp.
The goal is to find real problems before
/run-plan hits them at
implementation time. A comfortable review is a useless review.
- Every finding must be addressed. The refiner cannot ignore or defer
reviewer/devil's-advocate findings. Fix it or justify why it's not a
problem.
- Verify findings before baking fixes. Reviewer and DA findings are
hypotheses, not mandates. The refiner must reproduce each empirical
claim before acting on it. A plausible-sounding claim with no
verifiable anchor — or one whose evidence doesn't reproduce — is not
a mandate to fix. Devil's advocate findings are especially prone to
confidently-false empirical claims because the role incentivizes
plausibility, not truth.
- Convergence is the orchestrator's call based on the refiner's disposition table, not the refiner's self-declaration. Run all budgeted rounds unless issues drop to 0.
- Respect constraints. The plan must not require anything CLAUDE.md
prohibits: no external solvers, no bundlers, no external dependencies
without approval.
- Phase boundaries at natural breaks. Each phase must be independently
implementable and verifiable by
/run-plan.
- Progress tracker is mandatory.
/run-plan needs it to track status.
Start all phases as ⬚ (not started).
- Ultrathink throughout. Every agent in the process should use careful,
thorough reasoning.
Edge Cases
- Description is very brief ("add dark mode") — the research phase does
the heavy lifting, exploring what "dark mode" means for this codebase
- Description is very detailed (multi-paragraph brief) — research agents
validate and expand rather than starting from scratch
- Plan converges in round 1 — great, write it. Don't force unnecessary
rounds.
- Plan doesn't converge after max rounds — write it with the "remaining
concerns" section. The user decides whether to proceed or refine further.
- User adds context after research — incorporate it into the draft.
Don't ignore user input.
- Output file already exists — warn the user, ask before overwriting
- Plan is too large for one file — suggest splitting into multiple plan
files, one per major phase group