Run Ideation Project
Arguments: $ARGUMENTS
Orchestrate execution of all phases in an ideation project by driving the
deterministic Workflow engine at ${CLAUDE_PLUGIN_ROOT}/workflows/execute-contract.mjs.
This skill does the three things the sandboxed engine cannot: read the
contract, run the git log skip pre-pass, and own interactive failure-gating +
resume. The engine does everything between — topological wave planning, parallel
dispatch, and schema-validated per-phase results. You do not compute waves or
parse RESULT: text yourself — the engine returns a structured summary.
Parse arguments:
- Optional: path to
contract.md(e.g.,docs/ideation/my-project/contract.md) - If omitted, auto-detect by globbing
./docs/ideation/*/contract.md - If multiple contracts found, use
AskUserQuestionto select one
Step 1: Locate & Parse the Contract
- Resolve the contract path (argument or glob). Derive the project directory from it — for
docs/ideation/my-project/contract.md, that'sdocs/ideation/my-project/. - Read the sibling
contract-data.jsonin that directory. Itsexecution.phasesarray already holds each phase'stitle,specPath,prereqs, andrisk— this is the manifest. Also readprojectName,slug,approvalMode("express"= single-confirmation approval, no per-artifact human review — drivesstrictin Step 3), andbranch. - If
branchis set, re-assert the checkout before anything else touches git:git branch --show-current— if it differs,git switch <branch>(create withgit switch -cif missing). This must happen before the Step 2 pre-pass: both the skip detection and the phase commits belong on the isolation branch, on every entry, including fresh-session re-runs where the user has since switched away. (Isolation-branch semantics — creation, resume-vs-fresh, delete-not-revert — are owned by ideation's Express finish path; this step only re-asserts the checkout.) - Validate each
specPathexists. If any are missing, report which and ask the user whether to continue without them or abort. - Fallback if
contract-data.jsonis absent (older projects with onlycontract.md): parse the## Execution Plansection ofcontract.md— phase titles, spec paths from the/ideation:execute-spec <path>lines, and blocking relationships from the dependency graph — and build the same phase list. Also read the header's**Approval**line:Express→ treat asapprovalMode: "express"(setstrictin Step 3). If you cannot parse it, abort with guidance to re-run ideation.
Step 2: Git Skip Pre-Pass
For each phase, run git log --oneline -F --grep="<specPath>" with the full slug-qualified spec path (e.g. docs/ideation/<slug>/spec-phase-1.md). That exact form only — a loose --grep="<slug>" false-positives on any commit that merely mentions the project, and a bare filename (spec-phase-1.md) collides across projects, since every ideation project has one. The grep is sound because execute-spec's Commit section requires the slug-qualified specPath verbatim in every phase commit body. Treat a phase as complete only on a match; add each matched phase's title to a completedPhases list. Report what's being skipped:
Skipping "Phase title" (already committed: abc1234)
The engine excludes these from dispatch, so a resumed run only executes what remains.
Step 3: Build the Engine args
Assemble the manifest exactly per ${CLAUDE_PLUGIN_ROOT}/workflows/README.md:
{
"projectName": "...",
"slug": "...",
"projectDir": "docs/ideation/<slug>/",
"strict": false, // true when contract-data.json has approvalMode: "express"
"phases": [
{
"title": "...",
"specPath": "...",
"prereqs": ["<other titles>"],
"risk": "low",
"files": ["path/a.ts", "path/b.ts"], // every path this phase declares it touches
},
],
"completedPhases": ["<titles from Step 2>"],
}
prereqsare phase titles — passcontract-data.json's values straight through; do not remap to indices.strict: true(express contracts only) makes the engine run each phase fail-closed, because no human reviewed the specs. The decision-point semantics live in the gate-behavior table (${CLAUDE_PLUGIN_ROOT}/workflows/README.md) — do not restate them here. Omit or setfalsefor interactively approved contracts.- Before invoking, sanity-check that every
prereqsentry matches some phasetitle(or acompletedPhasesentry), and that no two phases share atitle. If a title doesn't resolve or appears twice, it's a manifest bug — report it rather than dispatching a broken graph (the engine will otherwise throw "Unknown prereq" or "Duplicate phase title(s)"). agentNames(Claude Code only — omit in pi): the engine dispatches scout/reviewer/builder stages byagentType, and Claude Code plugin-scopes those asideation:scout/ideation:reviewer/general-purpose— these are the engine's defaults, so a CC manifest omitsagentNamesentirely. In pi, omit the field too, for a different reason: the pi engine host (extensions/engine.ts) doesn't use a name registry at all — it readsagents/*.mddirectly and passes each stage's tools and system prompt per spawn.
Populate files from each spec's File Changes table
The engine uses files to serialize phases that would otherwise run in the same
wave but touch the same file — without it, two same-wave phases can contaminate
each other's git diff HEAD review and race on the git index at commit time. So
for each phase, read its specPath and extract every path listed in the
spec's File Changes tables — New Files, Modified Files, and Deleted Files —
into that phase's files array. Pass the paths through verbatim (the specs in one
repo use consistent relative paths; no resolution or normalization).
- Missing or unparseable File Changes section: set
files: []and tell the user that phase is being treated as parallel-safe (it will never be serialized against another phase, so an undeclared file overlap there could slip through). The engine also logs a warning when a multi-phase wave contains a file-less phase. filesis optional for the engine — omitting it (or[]) is identical to the old behavior. Old manifests keep working unchanged.
Step 4: Invoke the Engine
- Call the engine with
argsset to the manifest object from Step 3 (pass it as an actual JSON value, not a stringified one). The invocation differs by harness — both run the sameworkflows/execute-contract.mjs:- Claude Code: call the
Workflowtool withscriptPathset to the engine's absolute path (runecho ${CLAUDE_PLUGIN_ROOT}/workflows/execute-contract.mjsviaBashand confirm the file exists). The engine runs in the background and notifies on completion; watch progress with/workflows. - pi: call the
run_ideation_contracttool (registered by the plugin's bundledextensions/engine.ts) with the manifest as its parameters. Synchronous — the summary comes back as the tool result, in this turn, ready for the failure gate in Step 5. The tool runs the same engine file with stage agents spawned on the first-party in-process runtime; there is no agent-registry step.
- Claude Code: call the
- Tell the user before it starts: how many phases, how many already skipped, and that you'll pause only if a phase fails.
- Claude Code only: capture the returned
runId— you need it for same-session resume. In pi, resume is the git skip pre-pass (Step 2) plus re-invoking the tool.
If the engine is unavailable (the Workflow feature not enabled in this Claude Code, or the pi plugin's engine extension failed to load): degrade gracefully — tell the user, then walk the phases yourself in dependency order using /ideation:execute-spec <specPath> per phase (the contract's per-phase commands), committing each before the next. For express contracts, carry the --strict semantics into this path too (per the gate-behavior table in ${CLAUDE_PLUGIN_ROOT}/workflows/README.md). This is the legacy manual path.
Step 5: Handle the Summary
The engine returns { completed, noops, failed, skipped, results } — plus an optional error field. Print all four buckets.
If the summary carries error: planning itself failed (dependency cycle, unknown prereq, duplicate phase title) — every bucket is empty and no phase ran. This is a run-level failure, never an empty success: report the error message verbatim and stop. Interactive: fix the manifest (the message names the offending titles) and re-run from Step 1. Unattended: report and halt — do not proceed to the Completion Report, and do not treat empty buckets as a finished run.
noopsare done, not failed. A NO-OP phase produced a genuinely empty diff (the repo already satisfies its spec) — review was skipped, nothing was committed, and dependents were not blocked. Treatcompleted + noopsas the set needing no further work; re-dispatching a no-op phase loops forever.- Each entry in
resultscarriesreviewStatus(passed/validation-only/failed/skipped-empty-diff/not-run), awarningsarray that leads itssummarystring, andreviewCycles. AnyreviewStatusother thanpassedon a committed phase means unreviewed or partially reviewed code landed — that must reach the Completion Report, never be collapsed into a bare PASS. - Effort tracks risk (informational — the engine handles it): a phase with
risk: "high"runs its build and fix stages ateffort: 'high'; review always runs ateffort: 'high'.riskcomes straight fromcontract-data.json, so it is worth passing through accurately.
Write the run record — before the failure gate
When the summary carries no run-level error and its results array is non-empty, write and render the run record now, ahead of the branching below. That ordering is the point: the failure gate's "Stop here" and unattended-halt branches never reach the Completion Report, and a failed walk-away run is the record most worth keeping. Do not move this into the report step — the reviewer findings, warnings, and reviewStatus values in results exist nowhere else once the run ends. (Nothing to record: a run-level error means no phase ran, and an empty results means every phase was already committed and skipped by Step 2 — no run to report either way, and the generator refuses both records by design.)
Choose the stem.
{projectDir}run-{date}.jsonwith today's date fromdate +%Y-%m-%d— read it, never recall it, because thedatefield below must match the stem. Both files live in the project directory, not elsewhere: the report linkscontract.htmland the notes with relative hrefs. If that file exists, append-2,-3, … until one is free: one record per engine summary, so a within-session retry gets its own pair instead of overwriting the failed run it is retrying. The.htmlsibling takes the same stem. Remember both paths — the Completion Report re-renders these, not a freshly recomputed pair.Write the JSON with exactly these nine top-level keys and no others.
${CLAUDE_PLUGIN_ROOT}/test-fixtures/run-report/run-record.jsonis a complete worked example — read it once rather than guessing the nesting. Unknown keys are silently ignored, never reported, so an invented field (there is deliberately nomode) becomes a fact nobody ever sees:projectName,slug— fromcontract-data.json;date— the sameYYYY-MM-DDstring as the stem.branch— the branch Step 1 asserted, ornullwhen the contract declared none. Never substitutegit branch --show-current: the field means "the branch this contract declared", not "where the shell happens to be".baseBranch— the repo's default branch, read fromgit symbolic-ref --short refs/remotes/origin/HEADwith theorigin/prefix stripped; when that ref is unset,mainifgit branch -l mainshows it exists, elsenull. The report renders its review command asgit diff {baseBranch}...{branch}, so a wrong base is worse than none —nullwhenbranchisnullor the base can't be established, and the report simply omits the command.strict— the boolean you put in the Step 3 manifest.summary— the engine's{completed, noops, failed, skipped, results}object verbatim, never summarized, trimmed, or reordered. The generator cross-checks every bucket againstresults[]and refuses any disagreement, so a "helpful" condensation is a hard error, not a nicety.verify: null— verification has not run yet; the report renders an explicit "not run" state for it.notesFiles— the bare filenames matching{projectDir}implementation-notes-*.htmlright now,[]when none. The report links them relative to itself, so adocs/…prefix or an absolute path renders a dead link, and a://anywhere fails validation outright.
This skill has no
Writetool, so write it with Bash and a quoted heredoc delimiter, with the closing delimiter at the start of its own line — phase summaries and findings routinely carry backticks,$, and literal markup, and an unquoted delimiter would let the shell expand them:cat > {recordPath} <<'JSON' {…the record…} JSONRender it as its own Bash call — never
&&-chained with the write, so a denial of one is visible and doesn't silently skip the other:node ${CLAUDE_PLUGIN_ROOT}/scripts/run-report-gen.ts --input {recordPath} --output {htmlPath}If the generator rejects the record, it prints every violation with its JSON path (
summary.results[2].reviewStatus: …) and writes nothing. Fix the JSON against those indices and re-run — the errors name exactly what disagrees. Never bypass validation, and never hand-author the HTML: the generator is the only renderer and there is no fallback template. If the command is denied by permissions, print the exact! node …command so the user can render the record whenever they like, note the skipped render, and continue.Then proceed to the gate below exactly as before. The JSON is already on disk, so neither a rejection nor a denial may block or delay the run's handling.
If failed is empty (and no error): proceed to the Completion Report.
If failed is non-empty: this is the failure gate. Present it via AskUserQuestion:
Question: "Phase(s) {failed titles} failed. {one-line summary from results[].summary}. Dependent phases {skipped titles} were skipped. How to proceed?"
Options:
- "Retry failed phases" — Re-run the engine; it resumes from where it stopped.
- "Stop here" — Halt. Completed phases are already committed.
- "Accept and finish" — Treat failures as acknowledged; report and finish.
Unattended (driven by a /goal wrapper, or any run with no interactive user): do not block on AskUserQuestion — apply "Stop here" semantics: report the four buckets and halt. Completed phases are already committed and durable; retry belongs to whoever is driving (a /goal wrapper re-runs this skill, and the Step 2 git pre-pass resumes past everything committed).
If "Retry failed phases":
- Same session: re-invoke the engine — in Claude Code, the
Workflowtool withresumeFromRunId: <runId>and the samescriptPath(cached passing phases return instantly; only the failed/unreached phases re-run). In pi, callrun_ideation_contractagain with the same manifest — the Step 2 git pre-pass excludes everything already committed, so only what remains re-runs. - New session, or resume rejected: simply re-run this skill from Step 1 — the Step 2 git pre-pass re-derives
completedPhasesfrom the commits, so already-committed phases are skipped regardless. This is the cross-session resume path.
If "Stop here": report completed vs. remaining and exit.
If "Accept and finish": include the unresolved findings in the Completion Report under "Acknowledged Issues" and finish.
Completion Report
After the engine finishes (or execution stops), present a summary. Warnings come first: if any result has a non-empty warnings array or a reviewStatus other than passed/skipped-empty-diff, lead the report with them — a validation-only commit prints its WARNING — UNREVIEWED CODE COMMITTED line verbatim at the top, never a bare PASS. Reporting that truth is the point of reviewStatus.
## Execution Complete
{⚠ one line per warning, verbatim from results[].warnings — omit the block only when there are none}
### Completed Phases
- {title} — {commitHash} (review: {reviewStatus}, {reviewCycles} cycle(s))
### No-Op Phases
- {title} — spec already satisfied; nothing to commit
### Skipped Phases
- {title} — blocked by failed {prereq}
### Failed Phases
- {title} — {summary}
### Summary
{N} of {M} phases completed successfully ({K} no-ops need no further work).
Then verify the contract (when contract-data.json exists): run node ${CLAUDE_PLUGIN_ROOT}/scripts/verify.mjs {projectDir}/contract-data.json and quote its final line — VERIFY {slug}: commits=A/B pass=N fail=M judgment=K — verbatim in the report. Exit 0 (fail=0 and commits=B/B) is the completion predicate; if the script cannot run, say "verification not run", never "Complete" on the engine summary alone. Scope caveat: it checks this one contract's acceptance criteria, not repo health.
Then enrich the run record written in Step 5 — set its verify to { "line": "<the VERIFY line verbatim>", "exitCode": <the command's exit status> }, using the integer status of that Bash call rather than a status inferred from the line's counts, and re-render the same stem with the same command:
node ${CLAUDE_PLUGIN_ROOT}/scripts/run-report-gen.ts --input {recordPath} --output {htmlPath}
The output overwrites in place, which is the intent: one record per run, re-rendered once verification has run. Leave notesFiles as written. When verification did not run at all — no contract-data.json, or the script was denied — leave verify: null and let the report say so; a fabricated VERIFY line is worse than an honest blank.
Watched runs only: open {htmlPath} for ambient visibility, as a separate Bash call from the render and never as an approval step. Unattended runs (a /goal wrapper, or any run with no interactive user) skip the open — never the write.
If all phases completed and verification passed:
All {N} phases complete. Run `git log --oneline -{N}` to see the commits.
Learning Capture (watched runs only)
Unattended runs explicitly skip this step (a /goal wrapper, or any run
with no interactive user) — never prompt, never write
docs/ideation/learnings.md; the run's notes wait for the interview engine's
unmined-notes surfacing at the next interactive intake.
On a watched run, after the Completion Report, run the Learning Capture step —
aggregate this run's phase notes and apply the filter — per
${CLAUDE_PLUGIN_ROOT}/references/learning-filter.md, the single owner of the
procedure and the learnings.md lifecycle.
Key Principles
- The engine orchestrates; the skill prepares and gates. Wave planning, parallelism, and result handling are deterministic JS in
workflows/execute-contract.mjs. This skill builds theargs, runs the git pre-pass, and handles the human-in-the-loop moments the sandbox can't. - No wave math, no
RESULT:parsing here. Passprereqsthrough untouched; read the structured summary the engine returns. - The contract is the source of truth — phase order, dependencies, and spec paths all come from
contract-data.json(contract.mdExecution Plan as fallback). - Subagents get clean contexts — the engine runs each phase as five sibling agent stages (scout → build → review ⇄ fix → commit), each a fresh-context agent; the build stage runs execute-spec's build+verify halves as
--headless, or--headless --strictwhen the manifest setsstrict(semantics: the gate-behavior table inworkflows/README.md). No phase inherits another's context. - Gate on failures, not successes — the happy path is fully hands-off; the engine runs everything still reachable and only the skill pauses, after the run, when something failed.
- Already-committed phases are durable — each phase commits independently. The git pre-pass makes resume work across sessions; in Claude Code,
resumeFromRunIdmakes it instant within a session.