SuperAPI AI Directory

harness-plan

Coding & development

View repository

Updated 10-15 16:51

About this skill

harness-plan — Epic Planning for the Harness Control Loop

Fills the gap between harness-init (environment setup) and harness-loop (autonomous sprints). Runs once per epic. Re-runs continue the same epic draft from wherever it was interrupted.

The skill orchestrates the Planner sub-agent through three artifacts:

  1. product-spec.md — What / Why / Out of Scope / Constraints (interactive)
  2. roadmap.md — sprint decomposition with bundling: split|bundled
  3. One tracker Issue per sprint (when _config.yml.tracker != none)

After this skill finishes, the project is ready for /harness-loop.

Required Reading — Open BEFORE doing the step

Claude Code tends to skim SKILL.md. For each Planner dispatch below you MUST open and read the listed prompt template + reference file(s) before invoking the sub-agent. Planner is dispatched as fresh invocations per phase — never a single long session.

Step Planner phase Required file(s) to open
Step 3.5 Foundation readiness (no sub-agent) references/foundation-sprint-guide.md, ../harness-init/references/scripts.md §foundation-readiness.sh
Step 4 interview references/prompt-templates/planner-interview.md, references/product-spec-guide.md
Step 5 roadmap references/prompt-templates/planner-roadmap.md, references/roadmap-guide.md (foundation-sprint-guide.md when sprint-0 is inserted)
Step 7 contract-draft × N sprint (parallel-safe) references/prompt-templates/planner-contract.md, ../harness-init/references/rubric-presets.md (foundation-sprint-checklist.md for type: foundation sprint-0)
Step 8 Tracker Issue creation references/issue-create.md

.ja.md variants exist for Japanese projects; pick language-matched pair.

Language Rules

Resolve narrative language: explicit override (in English, 日本語で) > project mandate in CLAUDE.md / AGENTS.md > current user prompt > fallback skill source language (English).

Never infer from machine text (git log, progress.md, JSON keys, state enums, file paths, commands). Keep such tokens unchanged. AskUserQuestion body uses resolved language; option labels/descriptions stay bilingual (English / 日本語).

Reference files exist as <name>.md (English) and <name>.ja.md (Japanese). Pick the pair matching the resolved language for narrative guidance; YAML schemas and templates are language-agnostic.

Prerequisites

Before any generation step, check:

  1. Harness initialised — .harness/_config.yml and .harness/templates/product-spec.md must exist. If not, instruct the user to run /harness-init first and stop.
  2. Git repo — git rev-parse --is-inside-work-tree must succeed.
  3. jq available — command -v jq. Required for _state.json reads/writes (all state IO uses jq for JSON parsing).
  4. Tracker pre-flight — if _config.yml.tracker == github, verify gh auth status now to fail fast. See issue-create.md §Pre-flight.
  5. Fresh agent registry after /harness-init — if Task(subagent_type="generator"|"evaluator"|"planner") is missing or falls back to a general-purpose agent, /clear is insufficient. Fully exit Claude Code, relaunch the repo with claude --resume, then retry /harness-plan.

If any check fails, stop with a clear error. Do not partially generate.

Boot Sequence

Always execute before any write:

  1. git log --oneline -20
  2. tail -30 .harness/progress.md (if it exists)
  3. cat .harness/_state.json (if it exists)

If _state.json.phase ∈ {product-spec-draft, roadmap-draft, roadmap-approved, issues-pending}, the skill offers resume vs restart via AskUserQuestion. harness-plan runs prior to harness-loop so mode is not yet decided — the Boot Sequence here always behaves interactively. If the user invoked with --auto-approve-roadmap, resume is chosen automatically; otherwise the user confirms.

Execution Flow (10 steps)

Step 1: Detect Existing Epic

if .harness/<any-epic>/product-spec.md exists:
  AskUserQuestion:
    question: "Existing epic(s) detected. What to do?" /
              "既存の epic を検出しました。"
    options:
      - "Continue the most recent epic / 最新 epic を継続"
      - "Start a new epic / 新規 epic を開始"
      - "Cancel / キャンセル"

"Continue" picks the epic named in _state.json.current_epic, or the most recently modified .harness/<epic>/ directory if state is silent. "Start new" prompts for epic name (Step 2). "Cancel" exits cleanly.

With --auto-approve-roadmap, the skill always continues when state is populated, else starts new with a derived epic name (kebab-case from the first 3 non-stopword tokens of the user prompt).

Step 2: Epic Name

AskUserQuestion:
  question: "Epic name (kebab-case)?" / "Epic 名(kebab-case)?"
  options:
    - "<suggested from prompt, e.g., auth-suite> (Recommended)"
    - "Let me type it / 手入力"

Validation:

  • Kebab-case regex ^[a-z][a-z0-9-]{2,40}$
  • Not already present under .harness/
  • If invalid, re-ask once with the regex in the message

Create .harness/<epic>/ and switch _state.json.current_epic to this name.

Step 3: Copy product-spec Template

Copy .harness/templates/product-spec.md → .harness/<epic>/product-spec.md if the target does not exist. If it exists from a previous run, keep it — Step 4 resumes in place.

Step 3.5: Foundation Readiness Check

Run .harness/scripts/foundation-readiness.sh --epic <epic> before any Planner dispatch. It writes .harness/<epic>/foundation-readiness.md, emits a JSON summary on stdout, and may set _state.json.foundation_readiness / foundation_sprint_needed.

Use references/foundation-sprint-guide.md §Foundation readiness check for the exact JSON schema, severity matrix, RED-path AskUserQuestion options, --auto-approve-roadmap behavior, and state-write timing. Re-runs and --replan use the same flow.

Step 4: Interactive product-spec Drafting (Planner phase=interview)

This is the only conversational Planner phase — a single long session dedicated to the interview. All subsequent phases (roadmap generation, contract drafting, ruling) are dispatched as separate fresh Planner invocations to avoid context bloat.

Invoke the Planner sub-agent with the planner-interview prompt template (substitute {{EPIC_NAME}}, {{USER_REQUEST}}, {{PROJECT_TYPE}}). Also reference product-spec-guide.md for section structure.

The Planner walks the five sections in order — Why, What, Out of Scope, Constraints, Success Signals — asking section-specific AskUserQuestion rounds and writing answers into the matching section of .harness/<epic>/product-spec.md.

Compact resilience (per Planner role contract): the Planner appends each user response to .harness/progress.md as it collects them, so even if context compaction fires mid-interview, a resume can reconstruct the conversation from progress.md tail + existing product-spec.md draft.

Key enforcement (from the guide):

  • Reject "How" leakage (framework / library / schema picks) and redirect to Constraints only if externally mandated
  • Bullet count target for What: 3–7
  • Success Signals may be empty; mark success_signals: unspecified in frontmatter if so

Set _state.json.phase = "product-spec-draft" at step start. Keep it at "product-spec-draft" until the Planner declares the spec complete via the cross-check checklist; the transition out happens when Step 5 begins (phase is advanced to "roadmap-draft" there).

Before moving to Step 5, the Planner runs the cross-check checklist from the guide. Any "no" answer re-opens the relevant section via AskUserQuestion. If the skill was invoked with --auto-approve-roadmap (interview was pre-filled or resumed without user intervention), emit a TODO(product-spec): line to progress.md instead of re-opening — the user must resolve before harness-loop starts.

Commit: git add .harness/<epic>/product-spec.md && git commit -m "harness-plan: product-spec for <epic>".

Step 5: Planner Generates roadmap.md (fresh Planner, phase=roadmap)

Invoke a new fresh Planner sub-agent (not a continuation of the interview session from Step 4 — that session has already exited). Use the planner-roadmap prompt template (substitute {{EPIC_NAME}}).

Inputs (the fresh Planner reads from disk):

  • .harness/<epic>/product-spec.md (finalized in Step 4)
  • roadmap-guide.md — decomposition / bundling doctrine

Output: .harness/<epic>/roadmap.md with YAML frontmatter listing sprints and bundling flags. See the guide §roadmap.md Output Format for the canonical schema.

The Planner must:

  • Emit split by default; bundle only with a written bundling_reason citing one of the four coupling axes (schema, auth, UI, contract)
  • Enforce reciprocal bundled_with references
  • Cap sprints at 6 per epic; if more, pause and advise the user to split the epic. With --auto-approve-roadmap, truncate with a TODO(epic-split) note in progress.md for post-hoc resolution
  • Cap bundle size at 3 sprints
  • For each sprint, decide generator_backend per the rubric and AskUserQuestion flow in roadmap-guide.md §Backend Recommendation and planner-roadmap.md prompt template. The Planner asks the user via AskUserQuestion (interactive mode only) per sprint — options are <rubric primary> (Recommended) + <_config.yml.generator_backend> (epic default) + remaining enum (deduplicated). In continuous / autonomous-ralph / scheduled mode, the rubric primary is auto-confirmed without prompting (Pre-flight Gates forbid AskUserQuestion). With --auto-approve-roadmap, backend is auto-confirmed regardless of mode (still respects the rubric). Bundle peers share the primary peer's choice — one question per bundle, not per peer. When _config.yml.sprint_level_generator_override == false (legacy compat), skip rubric + AskUserQuestion entirely and write generator_backend: null for every sprint

Set _state.json.phase = "roadmap-draft" at step start.

Step 6: Roadmap Approval

The roadmap approval gate is always interactive. The mode value (interactive / continuous / autonomous-ralph / scheduled) is selected at harness-loop startup, not at harness-init, so harness-plan has no basis for implicit acceptance. The only exception is explicit user override via the --auto-approve-roadmap flag (see §Usage).

Default path (no flag):

AskUserQuestion:
  question: "Roadmap: <N> sprints, <M> bundle groups. Approve?" /
            "Roadmap: <N> sprints, <M> bundle。承認?"
  (Show Sprint Summary table from roadmap.md as description)
  options:
    - "Approve / 承認"
    - "Request changes / 変更を要求"
    - "Cancel / キャンセル"
  • Approve → proceed to Step 7
  • Request changes → collect free-form change request via a follow-up AskUserQuestion; Planner regenerates roadmap.md with the feedback. Loop at most 3 times — after that, abort to Cancel with a note in progress.md
  • Cancel → write progress.md line, leave roadmap.md as draft, exit

With --auto-approve-roadmap flag: skip the prompt and proceed to Step 7 directly. Append one progress line recording auto-approval ([<ts>] auto-approved via --auto-approve-roadmap). The user takes responsibility for pre-reviewing roadmap.md before invoking this mode.

Set _state.json.phase = "roadmap-approved" on approval (either path).

Commit: git add .harness/<epic>/roadmap.md && git commit -m "harness-plan: roadmap for <epic>".

Step 7: Draft Sprint Contracts (fresh Planner × N, phase=contract-draft)

For each sprint in roadmap.md.sprints, dispatch one fresh Planner sub-agent using the planner-contract prompt template. Substitute {{EPIC_NAME}}, {{SPRINT_NUMBER}}, {{SPRINT_FEATURE}}, {{SPRINT_BUNDLING}}, {{SPRINT_BUNDLED_WITH}}, {{SPRINT_GOAL}}, {{RUBRIC_PRESET}}.

Each invocation is independent — no shared context between sprints, each Planner reads only product-spec + roadmap + its own sprint metadata. This is the file-mediated peer-processes discipline the harness series follows throughout.

Parallel execution: Sprints are independent, so dispatch them in parallel if the Task tool supports it. For N sprints the total wall time approaches the slowest single contract-draft time rather than N×. If parallel dispatch fails, fall back to sequential — correctness is identical either way.

Preparation (before dispatching):

  1. Create directory .harness/<epic>/sprints/sprint-<n>-<feature>/ for each sprint
  2. Copy .harness/templates/shared_state.md into each sprint directory
  3. Create empty feedback/ and evidence/ subdirectories in each sprint directory

Each fresh Planner then writes contract.md into its own sprint directory, following its role contract. Draft fields:

  • sprint, feature, bundling, bundled_with (from roadmap)
  • goal, acceptance_scenarios (elaborated per sprint)
  • rubric with axes selected from rubric-presets.md based on _config.yml.rubric_preset — but threshold left as ? placeholder
  • max_iterations left as ? placeholder
  • status: pending-negotiation

The ? placeholders are filled by the Negotiation phase inside harness-loop (Generator ⇄ Evaluator, with Planner ruling as tiebreaker). harness-plan must NOT set threshold / max_iterations values itself.

Step 8: Create Tracker Issues

Set _state.json.phase = "issues-pending" at step start so resume after a mid-loop failure can locate the correct entry point. Dispatch on _config.yml.tracker. Full detail in issue-create.md:

  • github → gh issue create per sprint, with duplicate detection and epic-link formatting. On every successful create, append to _state.json.sprint_issues[<n>] atomically so resume can skip already-created sprints
  • gitlab → record pending payloads to .harness/<epic>/pending-issues.md (an epic-level ledger file owned by harness-plan). shared_state.md is sprint-scoped and does not exist yet at this phase — never write there. No CLI call in v1
  • none → skip; emit one progress line noting tracker-free mode

gh CLI absence: if tracker == github and gh is missing, abort the skill. Do not silently fall back to gitlab / none — the user's tracker choice is load-bearing for the audit trail.

On failure mid-loop, _state.json.phase stays at "issues-pending" and partial sprint_issues is preserved. Resume re-enters Step 8 and the duplicate-detection path in issue-create.md skips the already-created sprints.

Step 9: Finalize Handoff Cursor in _state.json

Step 8 already transitioned phase to "ready-for-loop" and built up sprint_issues incrementally. Step 9 finalizes the remaining cursor fields that harness-loop reads on its Boot Sequence:

{
  "current_epic": "<epic>",
  "current_sprint": 1,
  "phase": "ready-for-loop",
  "iteration": 0,
  "last_agent": "planner",
  "next_action": "harness-loop:negotiate-sprint-1",
  "completed": false,
  "pending_human": false,
  "aborted_reason": null
}

Preserve all other fields written by harness-init (max_iterations, max_wall_time_sec, max_cost_usd, allowed_mcp_servers, etc.). The mode field is not set here — it is written by harness-loop on startup. Use a merge, not a replace, via jq '.foo = "bar"' on the existing file.

Step 10: Summary Report

Emit to the user:

  • Epic directory path: .harness/<epic>/
  • Product-spec path
  • Roadmap path, with sprint count and bundle group summary
  • Tracker issues created (count and URLs if GitHub)
  • Recommended next action: /harness-loop to start sprint 1

Also append one progress line:

[<ts>] harness-plan: epic=<name> sprints=<N> bundles=<M> tracker=<github|gitlab|none>

Error Handling

Situation Response
.harness/_config.yml missing Error: "Run /harness-init first."
jq not found Error: "Install jq — all harness tooling requires it."
Epic name collision Re-ask by default; append -N suffix with --auto-approve-roadmap
Planner exceeds 6 sprints Pause by default; truncate with TODO note with --auto-approve-roadmap
Bundle size > 3 Hard-split by Planner; log decision to progress.md
Reciprocal bundled_with mismatch Planner regenerates once; fail if still broken
gh CLI missing when tracker=github Abort per issue-create.md. Do not fall back to gitlab / none
Roadmap approval cancelled Leave draft roadmap.md, exit. Re-run continues
Write failure mid-flow Partial state preserved; progress.md records failure line; resume-safe

Usage

Read references/usage-and-approval.md completely (.ja.md for Japanese) before parsing invocation flags. It defines resume, replan, and the single-source semantics of --auto-approve-roadmap.

What harness-plan does NOT do

  • Does not run any sprint — that is harness-loop's job
  • Does not negotiate the sprint contract rubric — that happens inside harness-loop's Negotiation phase
  • Does not create PRs — PRs are a harness-loop output
  • Does not re-configure hooks or scripts — re-run /harness-init for that
  • Does not pick the Generator backend, evaluator tools, or hook level — those are _config.yml values owned by harness-init

References