Skip to content

Build: plan.md, then implement

The build stage turns an accepted spec.md into a committed plan.md and then into an implementation in an isolated worktree. Plan mode lets the agent read the codebase without editing it. A named engineer accepts the plan before application code changes, and repository instructions, hooks, and a verifier keep the result checkable.

This page is for the developer who runs the agent and the tech lead who sets the team’s plan gate. You ask for a partial-refund endpoint and get back a 900-line diff. It edits the frozen v1/ package, bumps a dependency the platform team owns, and tests only the happy path. A five-minute read of a plan would have caught all three, but there was no plan, only the diff.

Traditional: an engineer reads the design and starts coding. How the change will be made stays in their head, and the reviewer first sees the finished diff, when rework is slow.

AI-native: the agent writes the plan in a read-only mode, the engineer corrects it while changing course is still editing a document, and the accepted version is committed as plan.md. Review later checks the diff against that file.

  • A plan.md contract with files, order, risks, rollback, and proof commands, and a named owner for each open decision.
  • Three copy-paste prompts: draft the plan read-only, attack it, and execute it in a worktree.
  • The exact plan-mode and worktree commands for Claude Code, Codex, and Cursor.
  • A rule for where knowledge belongs: instruction file, skill, hook, or CI.
  • Checks that prove the build without a human reading every line.
  • An accepted intent.md from Plan and an accepted spec.md from Design. A small change can skip both, but not the plan.
  • A project instruction file the agent reads at session start: CLAUDE.md or AGENTS.md, or a Cursor project rule.
  • One command that runs the tests or the build locally. See Test for the feedback loop.
  • A clean branch. The artifact chain describes where intent.md, spec.md, and plan.md live in the repository.

A plan is acceptable when an engineer who never saw the conversation could implement the change from it alone. Reject any plan that does not name:

  • the problem and non-goals, inherited from intent.md and spec.md;
  • the files and symbols to inspect or change;
  • ordered implementation steps and the dependencies between them;
  • data migration, compatibility, security, and rollback risks;
  • the deterministic checks that prove it, plus any visual or manual proof and who does it;
  • open decisions and the person who must make each one.

A skeleton you can adopt as-is:

# Plan: PAY-412 partial refunds
Spec: intent/PAY-412/spec.md · Approver: Dana Kowal (routine)
## Non-goals
- No change to the v1/ refund endpoint (frozen).
## Files
- src/main/java/com/acme/payments/v2/RefundController.java (new endpoint)
- src/main/java/com/acme/payments/v2/RefundService.java (partial amount rule)
- src/itest/java/com/acme/payments/v2/RefundIT.java (new)
- src/main/resources/db/migration/V42__refund_amount.sql (new)
## Steps
1. Add the Flyway migration V42__refund_amount.sql (additive column only).
2. Add RefundService.partialRefund: BigDecimal validation, and an
idempotency key on retry so a repeated request refunds once.
3. Expose POST /v2/refunds/{id}/partial.
## Risks and rollback
- Migration is additive; rollback is a code revert, the column stays.
- Double refund on retry: idempotency key checked in step 2.
## Proof
- make test && make itest (exit 0)
- RefundIT covers: full, partial, over-refund rejected, retry is idempotent.
## Open decisions
- Currency rounding mode for JPY: owner Finance (Ola Nowak).

Keep the plan beside the change, not only inside the tool. A plan that lives only in a chat or in a tool’s private plan store cannot serve CI, reviewers, or the next agent.

Plan in read-only mode, then commit plan.md

Section titled “Plan in read-only mode, then commit plan.md”
  1. Start the session in plan mode, so the agent can read and explore but cannot edit files.

  2. Give the agent intent.md and spec.md, and ask for a plan that follows the contract above.

  3. Interrogate the plan. Ask what the change could break, which step carries the most risk, and which alternatives the agent rejected and why.

  4. Run the challenge prompt below, ideally in a fresh session, so the critique is not coloured by the reasoning that produced the plan.

  5. Iterate until the plan names real files, symbols, and proof commands. Then accept it.

  6. Make the first write after acceptance plan.md, and commit it before any application code.

  7. Implement. When the implementation has to depart from the plan, stop, update plan.md with the reason, and get the approver to accept the new direction. Commit the updated plan with the code that needed it.

The tools differ in how you enter plan mode and in what the agent can do while it is there.

Enter plan mode with Shift+Tab (the status bar shows ⏸ plan mode on), or run /plan followed by your request, which switches the session into plan mode. You can also start the session in it:

Terminal window
claude --permission-mode plan

Claude reads files and can run read-only exploratory commands. It runs them without prompting only when auto mode is available. It does not edit your source until you approve the plan. When the plan is ready, Claude asks how to proceed: Yes, and use auto mode (when auto mode is available; otherwise Yes, auto-accept edits), Yes, manually approve edits, or No, keep planning. Press Ctrl+G to edit the proposed plan in your text editor before you answer.

Because edits stay blocked while planning, Claude cannot save plan.md during plan mode. Choose Yes, manually approve edits and make the first approved edit plan.md; switch to a less interrupting mode once the plan is committed.

To make plan mode the starting mode for a project’s terminal sessions, set "defaultMode": "plan" under permissions in .claude/settings.json. Checked against Claude Code 2.1.283.

Run the implementation in an isolated worktree

Section titled “Run the implementation in an isolated worktree”

A worktree gives each session its own checkout and branch, so two sessions cannot overwrite each other’s files. Split work that touches different files across sessions. Work that shares files runs in one session, one step after another.

Terminal window
claude -w pay-412-partial-refunds

The worktree is created under .claude/worktrees/ on a new branch. A worktree is a fresh checkout, so gitignored files such as .env are missing; list them in a .worktreeinclude file at the project root to copy them in automatically. Use a repository helper instead when setup also needs separate ports or local database state.

After the plan is accepted, run the session in the least permissive mode that can complete the approved slice. Permissions and sandboxes compares the modes, and Git worktrees covers cleanup and port collisions.

Encode what the team knows in files the agent reads

Section titled “Encode what the team knows in files the agent reads”

Knowledge that used to sit in people’s heads becomes a file the agent reads at the start of every session. /init drafts one in Claude Code and in Codex; in Cursor, ask the agent to draft a project rule from the repository.

  1. Generate a starting file, then cut it to what a new joiner needs on day one: build, test, and lint commands, the conventions that matter, and the mistakes the agent keeps making.

  2. Commit it at the repository root so the whole team shares one version.

  3. When the agent makes the same mistake twice, add the correction.

  4. Remove every line the agent would follow anyway. Pruning CLAUDE.md and AGENTS.md shows how to test which lines earn their place.

# Payments service
## Commands
- Build: make build
- Test: make test (unit), make itest (integration, needs Docker)
- Lint: make lint
## Conventions
- Java 21, Spring Boot 3. No new Lombok.
- Money is always BigDecimal, never double.
- Every endpoint needs an integration test in src/itest.
## Things the agent gets wrong
- Do not bump dependency versions; the platform team owns them.
- The v1/ package is frozen; changes go in v2/.

Codex reads AGENTS.md. Claude Code reads CLAUDE.md. From v2.1.277 (the latest channel on 2026-09-26; v2.1.281 on Bedrock, Google Cloud, Foundry and LLM gateways), Claude Code reads AGENTS.md when a project has no CLAUDE.md. Cursor reads its Rules. AGENTS.md and CLAUDE.md covers keeping one file for several tools.

Where does a rule belong: instruction file, skill, hook, or CI?

Section titled “Where does a rule belong: instruction file, skill, hook, or CI?”

An instruction file and a skill are advisory: the agent reads them and usually complies. A hook or a CI check runs as code and holds for the calls it matches. Put each rule at the cheapest layer that makes it hold.

The rule is…Put it inExample
A fact the agent needs every sessionCLAUDE.md / AGENTS.md / project rule“Test with make test”
A procedure applied only to some tasksA skill (SKILL.md)The team’s API error format; a security checklist
A constraint that must never be broken during editingA hookNo edits under v1/; format after every edit
A constraint on the merged resultCI and branch protectionTests pass; coverage does not drop

Skills follow the open Agent Skills standard (skill-name/SKILL.md); in Claude Code a project skill lives in .claude/skills/NAME/. See agent skills for writing and sharing them.

During implementation, hooks block edits to protected paths, run the formatter and linter after file edits, and keep credentials out of the diff. Keep build-phase hooks fast and scoped to the file that changed; heavier checks belong at commit or in CI. A hook that asks a human for approval belongs in Deploy, because an approval prompt during build puts a person back on the critical path of every parallel session.

Hooks are configured in .claude/settings.json. This PreToolUse hook blocks edits to the frozen package:

{
"hooks": {
"PreToolUse": [
{
"matcher": "Edit|Write",
"hooks": [
{ "type": "command", "command": "${CLAUDE_PROJECT_DIR}/.claude/hooks/protect-frozen.sh" }
]
}
]
}
}
#!/bin/bash
# .claude/hooks/protect-frozen.sh (chmod +x; needs jq)
FILE=$(jq -r '.tool_input.file_path // empty')
case "$FILE" in
*/com/acme/payments/v1/*)
echo "v1/ is frozen; put the change in v2/ (see plan.md)" >&2
exit 2 ;;
esac
exit 0

Exit code 2 blocks the tool call and shows the message to Claude, so the agent corrects course instead of stopping. The Edit|Write matcher does not see shell writes: a Bash call such as sed -i or cp into v1/ passes. Back the rule with the CI check below or a sandbox rule that denies writes to the path.

A hook guards the session; CI guards the merge. This check fails any pull request that touches the frozen package, whichever tool wrote the change:

Terminal window
git diff --name-only origin/main...HEAD | grep -q 'com/acme/payments/v1/' \
&& { echo "v1/ is frozen; put the change in v2/"; exit 1; }
exit 0

Check out with fetch-depth: 0 so that origin/main...HEAD resolves; a shallow clone has no merge base, and this check and the plan.md check below both fail.

Use hooks as deterministic guardrails has more handlers and the team rollout.

A parallel session is another full agent in its own worktree. A subagent runs inside one session with its own context window and tool limits: a verifier, a researcher, a simplifier. Start with two or three parallel sessions; the ceiling is how many streams one person can accept plans for and review. Running many agents at once covers the tooling that supervises them, and Spec-driven frameworks compared shows how Spec Kit and OpenSpec formalise the plan.md contract.

Turn repeated jobs into subagents checked into Git. In Claude Code they are Markdown files in .claude/agents/, and Explore and Plan are built in; Codex has /subagents; Cursor has Subagents. An example verifier for Claude Code:

---
name: verifier
description: Runs the app and checks the change against plan.md before the session reports done
tools: Bash, Read
---
Start the app with make run. Exercise the behaviour plan.md describes
and the two nearest neighbouring flows. Report what you ran, what you
saw, and any behaviour that does not match plan.md. Do not fix
anything; report only.

Bash lets the verifier write files, so “report only” is an instruction, not a guarantee. For a hard limit, run it in a sandbox that denies writes; see Permissions and sandboxes.

The verifier is not the feedback loop. The loop runs throughout the task; the verifier is a fresh context at the end, so its verdict is not coloured by the assumptions that produced the code. Custom subagents covers tool limits and model choice.

How do you prove the build without reading every line?

Section titled “How do you prove the build without reading every line?”

The plan is what makes the diff checkable: every claim a reviewer needs to test is written down before the code exists.

  • Plan before code. plan.md appears in Git history before, or in the same commit as, the first application change. A CI step can check that the pull request touches a plan.md. Run it only when the pull request carries your non-trivial label. Review still checks the commit order:

    Terminal window
    git diff --name-only origin/main...HEAD | grep -q 'plan\.md$' \
    || { echo "Non-trivial change without a committed plan.md"; exit 1; }
  • Proof ran in the session. The execution report lists every proof command from plan.md with its exit code. Missing commands mean missing evidence, not passing tests.

  • A fresh verifier agreed. The verifier subagent’s report is attached to the pull request.

  • The diff matches the plan. A review agent compares the changed files with the plan’s Files section and flags anything unlisted. Deviations must appear in plan.md with a reason.

Who signs off. The engineer accepts routine plans. Higher-risk work (migrations, auth, money, shared interfaces) goes to a tech lead or architect before implementation starts. Accepting a plan grants no authority to merge or deploy; those stay behind branch protection and the production gate described in Governance and autonomy. Everything a session does is attributed to the engineer who ran it.

Leading indicators: share of changes that merge from the first implementation pass, and time from plan acceptance to merged pull request. Lagging indicators: rework cycles per change, and how often the merged diff still matches plan.md. Metrics for an AI-native SDLC defines both.

What breaks in a plan-first build, and how to recover

Section titled “What breaks in a plan-first build, and how to recover”

The plan is generic. It says “update the service layer” instead of naming files. Reject it and rerun the drafting prompt; a plan without paths and symbols cannot be checked against a diff.

Claude Code cannot save plan.md while planning. Plan mode blocks edits, including the plan file. Approve with Yes, manually approve edits, write plan.md first, and commit it before any other edit.

The plan lives only inside the tool. A plan in a chat, or in a tool’s own plan store, disappears for CI and reviewers. Save it into the repository as plan.md before the first application commit.

The plan goes stale mid-implementation. The agent discovers a second caller or a missing index. Stop the session, update plan.md with the reason, get the approver to accept the change, then continue. The execution prompt above makes the agent stop instead of improvising.

Planning is mistaken for permission. Plan mode controls when the agent may edit; it does not govern commands after approval, credentials, merge, or deploy. Configure the sandbox, approvals, branch protection, and production gates separately.

The new worktree does not build. A fresh checkout lacks .env and local state. Add the files to .worktreeinclude in Claude Code, or use a repository setup script that copies them and assigns ports.

Parallel sessions collide. Two sessions edited the same file and one merge undoes the other. Re-split the work by file ownership in plan.md, and run shared-file steps in one session.

A build hook slows every edit. A full test run on each file edit stalls the loop. Keep the hook to the changed file; move whole-suite checks to commit or CI.

The tool has no plan mode in the current context. Some surfaces or headless runs do not offer it. Use the read-only drafting prompt with a read-only sandbox, such as codex exec -s read-only (see the Codex tab above for the permission-profile equivalent).