ca-implement-plan
Build a saved implementation plan in an isolated worktree, milestone by milestone: an external Claude reviewer checkpoints each milestone (so defects surface before more code is built on top of them) and gates the final PR promotion, with the human present. Pause for the human at every point marked ASK.
Inputs
PLAN— absolute path to the plan markdown file. Ask the human if it was not provided.MAX_ROUNDS— optional cap on final Claude review rounds before forcing a stop. Default 2. Milestone checkpoint reviews do not count against it.
Resolve the skill's own bundled scripts from its install dir:
SKILL_DIR="${CODEX_HOME:-$HOME/.codex}/skills/ca-implement-plan"
Step 0 — Confirm isolation (never skip)
Run only inside a dedicated worktree on a ca/ branch, never on the default branch or the shared checkout.
ROOT="$(git rev-parse --show-toplevel)"
BR="$(git -C "$ROOT" rev-parse --abbrev-ref HEAD)"
echo "root=$ROOT branch=$BR"
case "$BR" in ca/*) ;; *) echo "NOT ISOLATED: branch '$BR' is not a ca/ branch — STOP" >&2; exit 1;; esac
case "$ROOT" in */.claude/worktrees/ca/*) ;; *) echo "NOT ISOLATED: '$ROOT' is not a ca worktree — STOP" >&2; exit 1;; esac
If either check fails (branch not ca/*, or the tree is not under .claude/worktrees/ca/),
STOP and tell the human to launch from an isolated worktree first:
bash "$SKILL_DIR/scripts/new-worktree.sh" "$PLAN"
# then run codex inside the printed worktree path and invoke $ca-implement-plan again
Proceed only once on a ca/<plan-id> branch in its own worktree.
Step 1 — Read and anchor the plan
-
Read
PLANin full.PLANmust be the original plan file (e.g.docs/ca/plans/<id>.md), not a staged copy namedplan.md— the id is derived from its basename, and a copy namedplan.mdwould collapse the id toplan. Ensure a copy and checksum exist at$ROOT/.ca/runs/<id>/:ID="$(basename "$PLAN" .md)"; RUN="$ROOT/.ca/runs/$ID"; mkdir -p "$RUN" [ "$ID" = plan ] && echo "WARN: id is 'plan' — pass the original plan path, not a staged copy" >&2 cp "$PLAN" "$RUN/plan.md"; shasum -a 256 "$PLAN" | awk '{print $1}' > "$RUN/plan.sha256" -
Restate the goal and the ordered task list in two or three sentences. Note each task's test command.
-
Resolve the milestone grouping: use the plan's
## Milestonessection if present; otherwise group the tasks yourself at 2–4 natural boundaries (layer/dependency seams, each leaving the tree green). A plan of roughly 4 tasks or fewer is a single milestone — the loop then skips checkpoints entirely and behaves as implement-everything-then-final-review. -
If the plan contradicts the actual code (a referenced file is gone, an interface drifted), ASK the human before building.
Step 2 — Implement milestone by milestone (test-first, checkpoint-reviewed)
Within each milestone, implement its tasks in plan order:
- Write the failing test the task specifies. If the task specifies no test, write one yourself before implementing — unless the task has no testable behavior, in which case note that in the commit body; never silently skip test-first. Run it. Confirm it fails for the stated reason.
- Write the minimal implementation. Run the test. Confirm it passes.
- Run the task's lint/build/typecheck if specified. Capture real output — never claim success without evidence.
- Commit with a conventional message and the repo's co-author footer.
Hard rules while implementing:
- Stay inside this worktree. Never edit
.env, credentials, keys, or other secret files. - Keep the diff scoped to the plan. Do not refactor unrelated code.
At the end of each milestone except the last (a single-milestone plan goes straight to Step 3), run a checkpoint review so defects are caught before more code is built on top of them:
-
Push, and on the first milestone open the draft PR (the reviewer reviews the PR; the draft state is the fail-closed gate — it is promoted to ready only after the final review approves):
git -C "$ROOT" push -u origin "$BR" gh pr view "$BR" >/dev/null 2>&1 || \ gh pr create --draft --base "${CA_BASE:-main}" --head "$BR" --title "feat: $ID" --body-file "$RUN/plan.md" PR="$(gh pr view "$BR" --json number --jq .number)" echo "draft PR: #$PR" -
Call the checkpoint review (
M= the milestone number just completed). The same TWO PRECONDITIONS as the final review apply (Step 3) — a resolvable/ca:review-prplus network + authenticatedgh. If the script fails because the reviewer was unreachable, ASK the human whether to run it where network works or to skip the remaining checkpoints and rely on the final review — do not treat an unreachable reviewer as a realblockedverdict.bash "$SKILL_DIR/scripts/claude-review.sh" \ --plan "$RUN/plan.md" --pr "$PR" --worktree "$ROOT" \ --mode checkpoint --round "$M" --out "$RUN/review-checkpoint-$M.json"Checkpoint mode judges only the milestones built so far (round
M= milestones 1..M); unbuilt later tasks are not defects. -
Read the verdict JSON. Address every
blocking: truefinding now — fix, re-run the affected tests, commit, push — before starting the next milestone. There is no checkpoint re-review (the final review verifies the fixes). Record non-blocking findings for the PR summary. On a malformed file, treat it as blocked and ASK the human.
Step 3 — Self-review, ensure the draft PR, then the final Claude review
-
Self-review the diff against the plan: every task covered, tests present and green, no placeholder left.
-
Push, and make sure the draft PR exists (checkpointed runs opened it at milestone 1; a single-milestone run opens it here — the draft state is the fail-closed gate, promoted to ready only after Claude approves):
RUND=1 git -C "$ROOT" push -u origin "$BR" gh pr view "$BR" >/dev/null 2>&1 || \ gh pr create --draft --base "${CA_BASE:-main}" --head "$BR" --title "feat: $ID" --body-file "$RUN/plan.md" PR="$(gh pr view "$BR" --json number --jq .number)" echo "draft PR: #$PR" -
Call the reviewer on the PR in final mode (only final rounds count against
MAX_ROUNDS). By default this is the existing Claude-only path. WhenCA_DUAL_REVIEW=1, run the opt-in dual-model path: Codex reviews the PR from an offline host-built prompt, Claude performs its normal blind final review separately, and a separate Claude synthesis call adjudicates Codex's advisory findings into the single gatingca_claude_review.v1verdict.TWO PRECONDITIONS — important. Both must hold or no review is produced:
- Skill resolvable:
claude -p /ca:review-pronly works if the ca Claude plugin is installed in the user's Claude config, orCA_CLAUDE_PLUGIN_DIRpoints at theca/claudedir (the script then passes--plugin-dir). If neither, the skill won't load. - Network +
gh:claude -preaches the Anthropic API, and the review fetches the PR viagh pr diff, so both network and an authenticatedghare required. Codex's default-s workspace-writesandbox blocks network, so the call fails inside a normal sandboxed session. Provide network by launching Codex with it permitted for this command, or run the command in a host terminal whereghis authenticated.
Tell the human which arrangement you are relying on before running it.
META="$RUN/review-round-$RUND.meta.json" FINAL="$RUN/review-round-$RUND.json" BLIND="$RUN/review-round-$RUND.blind.json" CODEX="$RUN/review-round-$RUND.codex.json" if [ "${CA_DUAL_REVIEW:-0}" != "1" ]; then printf '{"dual_review":false,"codex":{"status":"disabled"},"synthesis":{"status":"disabled"}}\n' > "$META" bash "$SKILL_DIR/scripts/claude-review.sh" \ --plan "$RUN/plan.md" --pr "$PR" --worktree "$ROOT" \ --mode final --round "$RUND" --out "$FINAL" else set +e bash "$SKILL_DIR/scripts/codex-review.sh" \ --plan "$RUN/plan.md" --pr "$PR" --worktree "$ROOT" \ --round "$RUND" --out "$CODEX" CODEX_RC=$? set -e # If an earlier final round produced a Codex leg but this round did not, # the contract requires a machine-readable prior_findings_rechecked:false. PRIOR_RECHECK="" for prior in "$RUN"/review-round-*.codex.json; do [ -f "$prior" ] && [ "$prior" != "$CODEX" ] && PRIOR_RECHECK=',"prior_findings_rechecked":false' done case "$CODEX_RC" in 0) CODEX_COVERAGE="$(python3 - "$CODEX" <<'PY' - Skill resolvable:
import json, sys
d = json.load(open(sys.argv[1]))
print(d.get("coverage", "partial"))
PY
)"
CODEX_FINDINGS="$(python3 - "$CODEX" <<'PY'
import json, sys
d = json.load(open(sys.argv[1]))
print(len(d.get("findings", [])))
PY
)"
if [ "$CODEX_COVERAGE" = "full" ] && [ "$CODEX_FINDINGS" = "0" ]; then
printf '{"dual_review":true,"codex":{"status":"clean_no_synthesis","coverage":"full"},"synthesis":{"status":"skipped_clean"}}\n' > "$META"
bash "$SKILL_DIR/scripts/claude-review.sh"
--plan "$RUN/plan.md" --pr "$PR" --worktree "$ROOT"
--mode final --round "$RUND" --out "$FINAL"
else
printf '{"dual_review":true,"codex":{"status":"used","coverage":"%s"},"synthesis":{"status":"pending"}}\n' "$CODEX_COVERAGE" > "$META"
bash "$SKILL_DIR/scripts/claude-review.sh"
--plan "$RUN/plan.md" --pr "$PR" --worktree "$ROOT"
--mode final --round "$RUND" --out "$BLIND"
bash "$SKILL_DIR/scripts/synthesize-review.sh"
--blind "$BLIND" --second-opinion "$CODEX" --plan "$RUN/plan.md"
--pr "$PR" --worktree "$ROOT" --round "$RUND" --out "$FINAL"
printf '{"dual_review":true,"codex":{"status":"used","coverage":"%s"},"synthesis":{"status":"used"}}\n' "$CODEX_COVERAGE" > "$META"
fi
;;
1)
printf '{"dual_review":true,"codex":{"status":"invalid","reason":"schema_validation_failed"%s},"synthesis":{"status":"skipped_codex_invalid"}}\n' "$PRIOR_RECHECK" > "$META"
bash "$SKILL_DIR/scripts/claude-review.sh"
--plan "$RUN/plan.md" --pr "$PR" --worktree "$ROOT"
--mode final --round "$RUND" --out "$FINAL"
;;
3)
printf '{"dual_review":true,"codex":{"status":"unavailable","reason":"codex_unavailable_or_oversized"%s},"synthesis":{"status":"skipped_codex_unavailable"}}\n' "$PRIOR_RECHECK" > "$META"
bash "$SKILL_DIR/scripts/claude-review.sh"
--plan "$RUN/plan.md" --pr "$PR" --worktree "$ROOT"
--mode final --round "$RUND" --out "$FINAL"
;;
4)
printf '{"dual_review":true,"codex":{"status":"unavailable","reason":"input_fetch_failed"},"synthesis":{"status":"not_run"}}\n' > "$META"
echo "codex review input fetch failed; STOP and ask the human to retry/fix gh inputs" >&2
exit 1
;;
*)
printf '{"dual_review":true,"codex":{"status":"unavailable","reason":"unexpected_exit_%s"%s},"synthesis":{"status":"skipped_codex_unavailable"}}\n' "$CODEX_RC" "$PRIOR_RECHECK" > "$META"
bash "$SKILL_DIR/scripts/claude-review.sh"
--plan "$RUN/plan.md" --pr "$PR" --worktree "$ROOT"
--mode final --round "$RUND" --out "$FINAL"
;;
esac
fi
The script fails loudly (non-zero) with an actionable reason if no valid review is produced — if
it reports the API was unreachable, **STOP and ASK** the human to run the review step where
network is allowed; do not treat an unreachable reviewer as a real "blocked" verdict.
4. Read `review-round-$RUND.json` and `review-round-$RUND.meta.json`. The review JSON shape is
documented in `references/review-contract.md`
(`verdict`, plus `findings` where each has `blocking: true` or `false`). On a malformed file,
treat it as blocked and **ASK** the human.
## Step 4 — Address feedback and loop
- If `verdict` is `approve`, or no finding has `blocking: true`, go to Step 5.
- Otherwise address every blocking finding. For a finding you judge incorrect, do not silently skip
it — record your disagreement to surface in the PR summary, and **ASK** the human if it is material.
- Re-run the affected tests, commit, and **push** (this updates the draft PR's diff), increment the
round, and call Claude again on the same PR (Step 3.3 — reuse `$PR`, never open a second PR).
- Stop when approved, when the round count reaches `MAX_ROUNDS` (default 2 — the initial review plus
at most one fix-and-re-review round), or when two consecutive rounds produce an identical diff. On a forced stop,
**leave the PR as a draft** and **ASK** the human whether to keep going or take it from here.
This is one continuous session, so prior rounds are already in context — still re-read the latest
review JSON and the current PR diff so decisions rest on the current files, not memory alone.
## Step 5 — Promote the PR to ready, with an exchange summary
The PR already exists (opened as a draft at the first milestone, or in Step 3). Once the final
review approved — or produced no blocking findings — promote it to ready:
```bash
gh pr ready "$PR" # promote the draft PR to ready-for-review
If the loop hit a forced stop with unresolved blocking findings, leave it as a draft instead and say so.
Then post the Claude/Codex exchange — every checkpoint and final round (verdicts, what each one fixed, any disputed findings) — as a PR comment, and report the PR link to the human:
bash "$SKILL_DIR/scripts/post-summary.sh" "$RUN" "$PR"
ASK the human to review and merge.
Step 6 — Cleanup after merge (only on confirmation)
Delete nothing until the human confirms the PR merged. The preferred path is the Claude-side
/ca:clean-worktrees (it owns the cleanup guardrails and verifies the merge itself). If the human
asks you to clean up from here instead, derive the main checkout first — git worktree remove
must run from OUTSIDE the worktree being removed:
MAIN="$(git worktree list --porcelain | head -1 | sed 's/^worktree //')" # first entry = main checkout
[ -n "$MAIN" ] && [ "$MAIN" != "$ROOT" ] || { echo "cannot resolve main checkout — use /ca:clean-worktrees" >&2; exit 1; }
cd "$MAIN"
git worktree remove "$ROOT" # no --force; refuses uncommitted changes
git branch -d "$BR" # -d refuses unmerged; never -D
git worktree prune
If the PR was closed unmerged, clean nothing and tell the human.
References
references/review-contract.md— the JSON Claude returns and howblockinggates the loop.