Replace plan-agent:build's no-plan dead end with an entry into the plan pipeline that already exists — proposal, plan, review, implement — and stop discovery from silently adopting a stale spec when the user named no plan.
Today a bare /plan-agent:build either dead-ends or silently adopts whatever stale spec it finds; after this it offers what it found and, when you want something new, walks proposal to plan to review to implementation without you re-invoking anything. Done when tests/plugins/test-build-skill.sh passes with the new checks and a no-argument run in a repo with no plans reaches the proposal gate instead of stopping.
Read and implement all steps in the plan at docs/plans/add-plan-authoring-to-build.md — Let `build` author a plan when none is specified. Verify against the plan's Tests, Verification, and Acceptance Criteria before reporting done. If everything passed, mark completion in docs/plans/add-plan-authoring-to-build.md — tick each step's [x] marker and each criterion's - [x], set status: completed — and re-render the HTML from the spec. If any check failed, leave status: in-progress and say which.
More ways to run this plan — goal & workflow prompts, file path
Achieve this goal: Let `build` author a plan when none is specified. The plan at docs/plans/add-plan-authoring-to-build.md describes one approach — use it as reference, but optimize for the outcome. Verify against the plan's Tests, Verification, and Acceptance Criteria before reporting done. If everything passed, mark completion in docs/plans/add-plan-authoring-to-build.md — tick each step's [x] marker and each criterion's - [x], set status: completed — and re-render the HTML from the spec. If any check failed, leave status: in-progress and say which.
add-plan-authoring-to-build.html
docs/plans/add-plan-authoring-to-build.html
docs/plans/add-plan-authoring-to-build.md
Context
The story behind this plan — what prompted the work and why it matters now.
build resolves a plan three ways and every failure branch stops, with the body instructing the user to go run /plan-agent:implementation-plan by hand. The pipeline that instruction points at is already wired top-down: build-proposal hands off to implementation-plan, whose Step 8 menu invokes review-plan and build as real Skill() calls. Only the proposal-to-plan seam is still a printed prompt rather than a call, so this work is a second entry point into an existing pipeline rather than a new one.
The design decisions were settled in docs/proposals/chain-plan-authoring-into-build.md. Three matter here. Option A — delegate to the existing head and let control return through the Step 8 menu — was chosen over having build sequence the stages itself, which would need a new suppress-menu flag on implementation-plan plus a re-entrancy guard across three skills. The proposal stage is gated by one question, because build-proposal triages a Tier 0 idea by answering directly and producing no document, which would leave the chain holding nothing. The trigger is an absent plan argument, not an empty plans directory, and discovery therefore offers its result instead of adopting it.
The chain is reachable only from the slash command. /plan-agent:build a todo app treats the objective as a command parameter and enters the chain; the same words typed as plain text do not. Ambient activation keeps exactly the behaviour it has today — it requires a plan that already exists and routes elsewhere when there is none. This is a deliberate narrowing: build is the most overloaded verb in software, and any ambient trigger wide enough to catch "build a todo app" also catches "build fails on CI", "build the docker image", and "rebuild the index", none of which belong in a proposal loop. Making the objective a parameter removes that whole failure class instead of trying to word around it, and it keeps build from competing with implementation-plan for free-text routing.
The objective still forces the discovery skip in step 3: whether it arrives as a parameter or not, an objective means unrelated todo specs are noise.
One risk is accepted rather than solved: the chained run crosses roughly ten interactive gates at floor, and suppressing the redundant ones requires flags on sibling skills, which is the boundary this plan deliberately does not cross.
One risk is solved here: skill model: overrides apply for the rest of the turn and do not unwind when the skill finishes, so a chained run would leave the source-writing stage on whichever model the last planning skill declared. Step 8 fixes that.
Files that change
Every file this plan touches, and what happens to each one.
kit/plugins/plan-agent/skills/build/SKILL.mdmodified chain entry, discovery offer, hoisted guard, frontmatterkit/plugins/plan-agent/README.mdmodified thebuildrow and section both describe the old no-chain behaviourtests/plugins/test-build-skill.shmodified checks covering the new behaviour.claude-plugin/marketplace.jsonmodified plan-agent MAJOR version bumpkit/plugins/plan-agent/CHANGELOG.mdmodified entry for the new activation pathCLAUDE.mdmodified thebuildclause in the plugin table
Steps
The step-by-step work, in order — each step says what to do, why it matters, and how to check it worked.
git-agent:ship-autonomous runs every pre-flight guard before any mutation and this should match.todo specs are noise. When the offer does run, rank candidates newest-created: first, show at most the top three plus None of these — author a new plan, and say how many were suppressed.
status: alone with no notion of subject, so in a repo carrying ten todo specs an objective like "a todo app" would be answered with a menu of unrelated plans — and AskUserQuestion caps at four options, so the unbounded offer cannot render at all; gating on objective-absence removes the relevance problem rather than trying to solve it, leaving the offer for the case it was designed for, a bare build resuming interrupted work.[<plan path>] [<objective>] [--dir <path>], treating a leading token as an objective only when it has no .md or .html suffix and no /, and add Skill to the allowed-tools frontmatter line.
build has no objective input today, and authoring a plan needs one — while a path-shaped token must keep hitting the stop from step 2 rather than being read as prose; note that the no-slash rule misreads an objective containing one (add A/B testing support) as a path, so the stop message must name the misparse rather than only listing paths tried.grep -m1 '^allowed-tools:' kit/plugins/plan-agent/skills/build/SKILL.md includes Skill, the argument-hint frontmatter shows the objective slot, and the rule states what happens to a slash-bearing objective.$ARGUMENTS, and the model-invocation path keeps its current contract — it requires a plan that already exists and routes to /plan-agent:implementation-plan when there is none, never entering the chain.
$ARGUMENTS is empty on the model path, so an ambient chain would have to infer an objective from conversation, and the trigger word build is overloaded enough that inference would pull in compile and CI requests; scoping the chain to the command makes the objective explicit by construction and leaves ambient routing untouched, so no existing invocation changes meaning.build path through step 3's None of these — author a new plan), ask for one with AskUserQuestion before anything else, because both the proposal-versus-direct gate and the delegated skills are meaningless without it; then the proposal-versus-direct gate asked on every chained entry, a proposal path that invokes build-proposal then implementation-plan with objective-led text naming the proposal path, a direct path that invokes implementation-plan with the objective, and a return path that re-resolves the produced spec by path.
.md token would drop implementation-plan into conversion mode and produce a plan with no actionable steps, and re-running discovery on return would ask the user about the plan they just watched being authored; --dir must not be forwarded to build-proposal, which resolves its own proposals directory.Skill(skill: "plan-agent:build-proposal" and Skill(skill: "plan-agent:implementation-plan", and its return path says the spec is resolved by path rather than by discovery. The step must also state the abandonment contract: if the chain is abandoned between stages — a tool error, a session drop, or the user backing out after a proposal is written but before a plan exists — the proposal file is left in place uncommitted and build reports its path rather than cleaning it up.Exit — I'll implement later stops with the plan at status: todo, and Run as workflow stops because implementation-plan has already emitted the workflow prompt and marked the plan in-progress — the return path reports the produced plan's path and stops without implementing in both cases.
Exit would build what they declined, and Run as workflow would run an ordinary in-session build racing the workflow they launched. This overrides the proposal's Appendix A, which as written has the outer build proceed.Exit and Run as workflow cases and says it stops on each, and the three resume cases from Appendix A are still handled.model: opus to build/SKILL.md's frontmatter.
review-plan or implementation-plan last set; pinning it makes build re-assert on activation and gives the stage that writes source files a deterministic model.grep -c '^model: opus$' kit/plugins/plan-agent/skills/build/SKILL.md returns 1.description frontmatter unchanged, and rewrite only the Overview's "the execution half of implementation-plan, which authors a plan and stops" so it says the command form can author through the chain while ambient activation still requires an existing plan. Scope — do not delete — the Model-invocation bullet's "Requires a plan that already exists — if there is no plan file, stop and route to /plan-agent:implementation-plan <objective>"; add that this applies to the model path only.
build from claiming free-text "build X" requests and colliding with compile and CI phrasing. The route-away instruction is not stale prose but the ambient contract itself, so it must survive; only the Overview, which describes the skill as a whole, is now wrong.grep -c 'stop and route to' kit/plugins/plan-agent/skills/build/SKILL.md returns 1, git diff shows the description: line untouched, and the Overview names the command-versus-ambient split.build row in the component table, which reads "implements an existing plan and runs its gates", and the build section body, which opens "Implements a plan that already exists", plus its usage examples so at least one shows a no-plan invocation.
grep -c 'a plan that already exists' kit/plugins/plan-agent/README.md returns 0, and the usage block shows an objective-only invocation.version in .claude-plugin/marketplace.json by one major, add a CHANGELOG.md entry under kit/plugins/plan-agent/, and update the build clause in the root CLAUDE.md plugin table.
.claude/rules/marketplace.md classifies "changing argument format or activation behavior" as MAJOR, and step 4 changes the argument format while step 3 changes what an existing no-argument invocation does — ambient activation is deliberately unchanged, so the argument grammar and the discovery behaviour are what carry the bump; a MINOR would understate a breaking change to a published plugin.BASE_REF=main node scripts/check-plugin-versions.mjs exits 0 and the new version's major component is exactly one greater than the value on main.FAILURES counter convention and check 6's section-scoped pattern: sed the owning section, squeeze newlines, then grep within it.
bash tests/plugins/test-build-skill.sh exits 0, and reverting any one of steps 1, 2, 3, 4, 6, 7, or 8 makes it exit 1 naming that check.Tests
The tests that prove the change does what it promises.
build with no plan argument enters the chain instead of dead-ending. File: tests/plugins/test-build-skill.sh; Type: smoke (static); Asserts: build/SKILL.md carries the Step 1b chain entry with both delegating Skill calls, presents discovery as an offer, places the dirty-tree guard ahead of the chain, terminates on every non-implementing Step 8 choice, pins model opus, keeps both stop branches, and retains the model-invocation route-away instruction exactly once; Run: bash tests/plugins/test-build-skill.shDefinition of done
The plan counts as done when every statement below is true — check each one off as you verify it.
Final check
One last pass to confirm the whole change works end to end.
Run bash tests/plugins/test-build-skill.sh and confirm it exits 0 with the new checks reported as PASS alongside the nine existing ones. Then confirm the checks are load-bearing rather than decorative: temporarily delete the Step 1b heading from build/SKILL.md, re-run, and confirm the script exits 1 naming that check, then restore the file.
Exercise the headline scenario in this repo, not a scratch one, because it is the case the design nearly failed: with a dozen unrelated todo specs present, run /plan-agent:build a todo app. Confirm it shows no discovery offer at all and reaches the proposal-versus-direct gate. Then type build a todo app as plain text with no command and confirm it does not enter the chain — ambient activation must still require an existing plan and route away without one. Then run a bare /plan-agent:build with no objective and confirm the offer appears capped at three candidates with the suppressed count stated.
Exercise the objective end-to-end in a scratch directory with an empty plans directory. Invoke /plan-agent:build add a health check endpoint and confirm it asks the proposal-versus-direct question rather than stopping with a routing message. Answer Straight to plan authoring and confirm implementation-plan opens with that objective — not in conversion mode, which would be visible as a plan whose steps restate proposal headings instead of naming real actions. Then repeat in a directory holding exactly one todo spec and confirm that spec is offered with an author-a-new-plan option rather than being adopted silently. Run the chain once more and answer Exit — I'll implement later at the Step 8 menu; confirm the run stops with the plan at status: todo and git status --porcelain showing no source files written. Repeat with an objective broad enough to trip the renderer's workflow heuristic (five or more files across three or more directories, so the Run as workflow option appears) and answer it; confirm build reports the plan path and stops rather than starting an in-session build alongside the emitted workflow prompt. Then run a bare /plan-agent:build in the empty directory and confirm it asks for an objective before the proposal-versus-direct gate rather than invoking a planning skill with an empty one. Finally invoke /plan-agent:build add A/B testing support and confirm the slash-bearing objective either reaches the chain or stops with a message naming the misparse — never a bare list of paths tried, which would leave the user with no idea their objective was read as a filename.
Finally run BASE_REF=main node scripts/check-plugin-versions.mjs and confirm it exits 0, proving the marketplace bump landed and did not regress against main.
Wrapping up
Three gates that must all pass before this plan is marked completed.
Completion Report
- Criterion 3, the discovery offer
- bare
buildwith onetodospec stopped, offered the candidate alongside an author-a-new-plan option, stated that nothing was suppressed, wrote no files, and left the spec atstatus: todo. - Criteria 4 and 6, objective suppresses discovery
/plan-agent:build a todo appwith an unrelatedtodospec present showed no offer at all and reached the proposal-versus-direct gate.- Criterion 5, ambient activation unchanged
- plain-text
build a todo appwrote an app directly and created no plan file, so the chain was never entered. - Criterion 7, the typo stop
/plan-agent:build docs/plans/does-not-exist.mdstopped naming both resolution attempts and refused the chain.- The chain is productive, not merely reachable
/plan-agent:build add a health check endpointin an empty repo authored a plan and implemented a workingnode:httpserver end to end. Two defects were found by running it, both fixed and re-verified:- Gates had no stated behaviour when
AskUserQuestionis unavailable - two headless runs resolved the same missing tool in opposite ways, one adopting the lone discovery candidate (the exact silent pickup this plan removes) and one stopping; build/SKILL.md now states the fallback, check 18 guards it, and re-running both scenarios produced a clean stop-and-report.
- The misparse example was wrong
- the rule tests the leading token, so this plan's own example
add A/B testing supportparses cleanly as an objective; only a slash in the first token misparses, the example is nowA/B testing for checkout, and a live run confirms the stop names the misparse and offers a reworded form. Three further defects were found by automated review on PR #470, all fixed and guarded by checks 10, 12, and 13: Implement nowre-entered the preconditionsSkill()is synchronous, so the nested build had already finished; the branch would have asked whether to redo just-completed work, or restarted a run stopped viaMark in-progress and stop. It now reports the nested result and stops, overriding this plan's step 7 claim that all three Appendix A resume rows stay live.- The hoisted dirty-tree guard fired on the Step 8 callback
implementation-planwrites the spec and HTML before calling back, so the guard saw them as uncommitted work and prompted at exactly the post-interview moment the hoist prevents, or stopped the chain headless. Plan artifacts are now excluded; verified by running a build against an uncommitted spec plus HTML, which proceeded silently tocompleted.- The proposal path assumed an artifact
- a Tier 0 idea is answered directly with no document written, leaving the chain calling
implementation-planwith a path that does not exist. It now falls through to the direct path with the original objective. The following remain verified by construction rather than execution, needing an interactive session no harness here can provide, and are checked off under Step 3.4 on the user's explicit instruction: - Criteria 2 and 10, the no-specs chain entry and the objective prompt on the
None of thesepath - both sit behind the discovery offer, which is now confirmed to stop rather than self-resolve in a headless run.
- Criteria 8 and 9, the two non-implementing Step 8 terminations (
ExitandRun as workflow) - asserted statically by check 13. The option's full label carries an em-dash, which the report renderer reads as its own item separator, so it is named in prose here rather than quoted.