| name | post-execute |
| description | End-to-end ship pipeline for a finished slice of work: commit → code-review → execution-report → triage + fix → PR → CI → deep PR review → triage + fix → CI → merge. Use when the user says 'ship it', 'post-execute', 'take this to a merged PR', or invokes /ship:post-execute. Runs after the code is written and working — it does not implement features. |
| version | 0.1.0 |
Post-Execute — ship the slice
Input: $ARGUMENTS (optional slice/branch name; inferred from the branch when omitted)
This runs after the implementation is done and working. It takes finished work all the way to a
merged PR without further prompting, stopping only at the hard gates in
STOP CONDITIONS.
It is repo-agnostic. Everything project-specific — the gate command, where reviews are written,
the merge strategy — comes from a resolved PROJECT PROFILE.
Never hardcode another repo's conventions into this run.
PHASE 0a — Preflight (never skip)
Run these before touching anything. Any failure is a STOP.
git rev-parse --abbrev-ref HEAD
git status --porcelain
gh auth status
- On the base branch → STOP. Slice work never lands directly on
main/master. Report the
branch and ask before doing anything else.
- Clean tree AND no unpushed commits → STOP. There is nothing to ship; say so rather than
opening an empty PR.
gh not authenticated → STOP. Every phase from 4 on depends on it. Tell the user to run
gh auth login themselves — it is interactive and will hang if you run it.
- Determine the slice name from
$ARGUMENTS, else from the branch (m15-slice7-sso →
m15-slice7-sso). This name keys the review, report and plan filenames.
PHASE 0b — Resolve the project profile
Resolve, in this order, and state what you resolved in one line before proceeding so the user can
correct a bad guess before it does damage:
.claude/post-execute.json in the target repo — the explicit per-repo profile. If present it
wins outright. Full schema: PROFILE.md.
- Auto-detection from the repo itself (below).
- Documented defaults (below).
If a field is still unresolved and the phase that needs it is about to run, STOP and ask. Do not
invent a gate command — running the wrong one and reporting it as passed is the single worst failure
mode of this pipeline.
Auto-detection rules
| Field | Detect by | Default if absent |
|---|
baseBranch | gh repo view --json defaultBranchRef -q .defaultBranchRef.name | main |
gate.fast | a package.json script matching /^(repo-health:fast|check:fast|verify:fast)$/ | fall through to gate.full |
gate.full | a script named repo-health, check, verify, ci, or validate; else test | ask |
prePush | scripts named lint, format:check, typecheck that are not already inside gate.full | [] |
paths.plans | first existing of .agents/plans, docs/plans, plans | none (skip the plan read) |
paths.codeReviews | first existing of .agents/code-reviews, docs/code-reviews | .agents/code-reviews |
paths.executionReports | first existing of .agents/execution-reports, docs/execution-reports | .agents/execution-reports |
projection.file | SUMMARY.md, ROADMAP.md, or CHANGELOG.md at the repo root | none (skip Phase 2's projection step) |
checks | gh pr checks on any recent PR, or workflow names in .github/workflows/*.yml | whatever gh pr checks reports at runtime |
merge.strategy | gh repo view --json mergeCommitAllowed,squashMergeAllowed,rebaseMergeAllowed |
Read the repo's CLAUDE.md if one exists. It is the source of truth for conventions and often
states the gate command, the branching rule and the merge strategy directly. A profile that
contradicts CLAUDE.md is wrong — say so and use CLAUDE.md.
Sub-commands — bundled, so this plugin has no external dependencies
The five commands this pipeline delegates to ship with it (see
VENDORED.md). There is nothing to install and no degraded mode on a clean
machine.
| Step | Profile key | Bundled default |
|---|
| Commit | commands.commit | /ship:commit |
| Working-tree review | commands.codeReview | /ship:code-review |
| Apply fixes | commands.codeReviewFix | /ship:code-review-fix |
| Report | commands.executionReport | /ship:execution-report |
| PR review | commands.prReview | /ship:prp-review <pr-number> --agents all |
A profile may override any of these to point at a different reviewer (the upstream /lril:* set,
a repo's own command, anything). Overrides are resolved exactly like every other profile field.
If a resolved command is missing — an override naming something uninstalled, or a broken install —
do the work inline rather than failing, and state which you did in the final summary. This matters
most at Phase 6: /ship:prp-review --agents all fans out specialist agents over the PR diff, whereas
an inline fallback is you reading it once, with full memory of the implementation's reasoning — which
is precisely the familiarity that pass exists to defeat. "Ran the specialist reviewer" and "read the
diff myself" are different levels of evidence and must never be reported as the same thing.
Announce the resolution at Phase 0b, alongside the rest of the profile, so a degraded run is known
before the work starts rather than discovered at Phase 6.
PHASE 1 — Commit
Run the fast gate first, so a broken commit never enters history:
<gate.fast>
Then commit via commands.commit (or inline).
If the repo defines a dbGate (or any gate whose integration layer can self-skip), do not run it
yet — it runs once, before the push, in Phase 4.
PHASE 2 — Review and report
Run both, in this order:
commands.codeReview → writes <paths.codeReviews>/<slice>.md
commands.executionReport → writes <paths.executionReports>/<slice>.md
GATE HAZARD — the one that fails CI most often.
Many repos enforce consistency between a shipped-work marker and a status projection file. If
projection.file is set, the report and the projection flip must land in the same commit.
Update every block the file uses (a status section and a roadmap section, typically), following
the style the neighbouring entries already use. Leave the PR number as a placeholder now; Phase 4
fills it in.
Worked example — 420AI: scripts/check-summary.mjs fails repo-health when an execution report
exists for a slice not marked ✅ in SUMMARY.md, in both §0 and §6.
PHASE 3 — Triage with the user, fix what they chose, report the table
Read the review findings in full, then run the TRIAGE GATE. You do not
decide what gets fixed — the user does. Your job is to recommend, then execute their choice.
Once the selection is settled, apply the fixes via commands.codeReviewFix (or inline), working the
Critical ones first so a mid-way stop still leaves the tree in the best available state.
Then present the outcome table (required output, not optional):
| # | Severity | Issue | File | Disposition | What was done | Status |
|---|
| 1 | Critical | … | path:line | Fix | … | Fixed |
Rules for the table:
- One row per finding, including every one the user chose to skip. A table listing only the
fixes is a highlight reel.
file:line, so every row is clickable and checkable.
- Disposition is what the user chose; Status is what actually happened. When they differ — a
fix attempted and abandoned, a finding that turned out to be a false positive — say so in the row
rather than quietly aligning the two columns.
- If the review found nothing, say exactly that — do not manufacture rows.
Commit the fixes, then re-run the review and repeat until it comes back clean.
Findings the user declined are not re-litigated on the next pass. Carry them forward as
already-triaged and say so — a re-review that keeps re-asking about a rejected finding trains the
user to stop reading the list.
THE TRIAGE GATE
Used by both Phase 3 (working-tree findings) and Phase 7 (PR-review findings). The user selects;
you recommend.
Step 1 — Present the full list with a recommendation per finding
Sort by severity, Critical first, so the consequential rows are read first. Show every finding:
Code review found 6 findings. My recommendation is pre-selected — [x] = fix.
[x] 1. CRITICAL Session lookup runs outside the transaction — src/repositories/sessions.ts:88
→ FIX. A revoked session can still authenticate in the window; this is the bug the slice exists to close.
[x] 2. IMPORTANT `revokeAll` has no user_id predicate — src/repositories/sessions.ts:120
→ FIX. One missing predicate revokes every user's sessions.
[ ] 3. SUGGESTION Extract the guard ladder into a helper — src/routes/auth.ts:340
→ DEFER to the slice that touches all these routes anyway.
[ ] 4. SUGGESTION Rename `sid` to `sessionId` for clarity — src/session.ts:14
→ WON'T FIX. `sid` is the JWT-conventional claim name and the payload is wire-visible.
Every line carries: the checkbox prefill, severity, a one-line summary, file:line, and one line
of rationale. A recommendation with no reason is not a recommendation.
Step 2 — Let the user set the selection
Use the AskUserQuestion tool — it is the only real selection UI available. A markdown checklist
is display only; nothing in the harness makes it interactive, so do not pretend it is.
Ask one question first, single-select:
- "Accept all recommendations (Recommended)" — proceed with the pre-selection above
- "Adjust the selection" — go to the per-finding pass below
- "Fix everything" — every finding, including the deferred and won't-fix ones
- "Fix Critical + Important only" — drop the suggestions
If they choose Adjust, run a per-finding pass with multiSelect: true, where selected = fix
and unselected falls back to the recommended non-fix disposition (stated in each option's
description, so nothing is ambiguous). Tool limits are 4 options per question and 4 questions per
call, so batch 4 findings per question, up to 16 per call, and issue more calls if the list is
longer. Keep the severity ordering across batches.
The tool always offers a free-text Other, so the user can type an instruction that fits no option
("fix 1 and 2, and open an issue for 3") — honour it literally.
Step 3 — Confirm before executing
Restate the final selection in one line — Fixing 1, 2, 5 · deferring 3 · won't-fix 4, 6 — then
execute. No further prompting until the work is done.
Rules
- Never silently upgrade a "won't fix" to a fix because you disagree. If a declined finding is
genuinely dangerous, say so once, plainly, and let the user decide again.
- Deferred needs a destination — a slice, a milestone, or a filed issue. "Deferred" with nowhere
to go is a dropped finding wearing a label.
- A declined Critical is a STOP-condition candidate, not a silent proceed: if the user declines a
finding that breaks a documented invariant, surface that specific consequence and confirm before
moving on.
- If there are no findings, skip the gate entirely. Do not ask a question with an empty list.
PHASE 4 — Push and open the PR
Pre-push gate. CI commonly runs checks the local gate does not, and finding that out from a red
PR wastes a full CI cycle. Run every prePush entry:
<prePush[0]>
<prePush[1]>
<prePush[2]>
If the repo defines a dbGate (or any gate layer that self-skips without infrastructure), run it
now — skipped ≠ passed:
<dbGate.up>
<dbGate.migrate>
<dbGate.verify>
Then push and open the PR:
git push -u origin "$(git rev-parse --abbrev-ref HEAD)"
Write the PR body with a heredoc. On Windows the Bash tool is Git Bash, and a PowerShell
here-string (@'...'@) injects literal @ characters. A quoted heredoc also eats backslashes — for
a body containing regex backslashes, write the file with the Write tool and use --body-file.
gh pr create --base "<baseBranch>" --title "<type>(<scope>): <slice title>" --body "$(cat <<'EOF'
## What
<one paragraph: what this slice does and why>
## Decisions
<decisions this slice settled, if any>
## Review
- Code review: `<paths.codeReviews>/<slice>.md` — N findings, all resolved
- Execution report: `<paths.executionReports>/<slice>.md`
## Validation
- `<gate.full>` — PASS, 0 skipped
- `<prePush...>` — PASS
🤖 Generated with [Claude Code](https://claude.com/claude-code)
EOF
)"
Then fill in the PR number in projection.file and commit that change, so the projection is
accurate the moment the PR exists rather than after the fact.
Auto-push caveat: an auto-push may already have carried commits to origin. If an amend then
hits a non-fast-forward, resolve with git push --force-with-lease guarded on the expected sha —
only ever on this unmerged feature branch.
PHASE 5 — Wait for CI green (first pass)
gh pr checks --watch --interval 30
Every required check must pass (checks names them, if the profile knows them).
- Red → fix, push, re-watch. Do not proceed to Phase 6 on a red PR; reviewing code that does not
build wastes the review.
- If
--watch exceeds the tool timeout, poll instead: gh pr checks every ~60 s until every check
reports a terminal state. Do not conclude "green" from a partial run.
- Scattered, non-reproducible integration-test failures are often infrastructure flakiness rather
than real regressions. Establish that a failure reproduces before bisecting. (420AI: Docker
Postgres checkpoint stalls — run
CHECKPOINT first.)
PHASE 6 — Deep PR review
Run commands.prReview against the PR number, requesting the broadest available fan-out.
This is the deeper second lens — it reads the PR diff, whereas Phase 2 read the working tree.
PHASE 7 — Triage the PR-review findings, fix, then comment on the PR
Run the TRIAGE GATE again — same rules, same recommendation-then-selection shape.
Do not skip the gate here on the grounds that the user already triaged once in Phase 3: these are
different findings, from a different lens, and a multi-agent fan-out routinely surfaces more
suggestions than the working-tree review did. That volume is exactly why the user gets to choose.
Two adjustments specific to this pass:
- Deduplicate against Phase 3 first. A finding the user already declined must be presented as
[ ] (declined in Phase 3), not re-asked as if new. A finding already fixed is dropped entirely.
- Weight the recommendation toward the specialists' strengths. Error/type/test-focused reviewers
find real defects; simplification and docs reviewers more often produce taste calls. Say which is
which in the rationale line, so the user is choosing with that context.
Commit and push the fixes, then post the results as a PR comment — the durable record a future
reader sees, which is why the table lives outside the transcript as well:
gh pr comment <pr-number> --body "$(cat <<'EOF'
## PR review — resolution
| # | Severity | Finding | File | Disposition | What was done | Status |
|---|---|---|---|---|---|---|
| 1 | Critical | … | `path:line` | Fix | … | Fixed |
**Summary:** N findings — X fixed, Y declined (reasons above), Z deferred to <destination>.
Dispositions were chosen by the maintainer at the triage gate.
EOF
)"
That last line matters: it tells a future reader the skips were a decision, not an oversight.
Also present the same table in the transcript, so the user does not have to open GitHub to see it.
PHASE 8 — CI green (second pass), then merge
gh pr checks --watch --interval 30
gh pr merge <pr-number> --<merge.strategy> [--admin] [--delete-branch]
- Use
merge.strategy. --merge, --squash and --rebase are not interchangeable; a repo that
requires linear history will reject a merge commit, and a repo that records slices as merge commits
loses that structure to a squash.
--admin only when merge.admin is true. It exists for the solo-maintainer case: branch
protection requires an approving review, GitHub forbids self-approval, and enforce_admins is off
precisely so the maintainer can merge. It is not a bypass of the checks — the required status
checks still had to pass. On a team repo, --admin overrides a real human review requirement:
do not pass it unless the profile says so.
--delete-branch when merge.deleteBranch is true.
STACKED-PR CAVEAT: if this PR is the base of another open PR, do NOT delete the branch —
deleting a base auto-closes the child PR instead of retargeting it. Either merge child→parent first,
or retarget the child to the base branch before merging this one. Check with
gh pr list --base "$(git rev-parse --abbrev-ref HEAD)" before merging.
Finally:
git checkout <baseBranch> && git pull --ff-only && git log --oneline -1
and report the merged PR number, the commit sha, and a one-line summary of what shipped.
STOP CONDITIONS
Stop and ask the user — do not improvise past any of these:
- On the base branch at Phase 0a.
- Nothing to ship — clean tree, no unpushed commits.
gh unauthenticated, or the profile's gate command cannot be resolved.
- CI red after two fix attempts. A third blind attempt is guessing; surface the failure output.
- A Critical finding you cannot fix, or one whose fix would change the slice's agreed scope or a
documented decision. Scope changes are the user's call.
- The slice's plan and the implementation disagree on something load-bearing — that is a planning
defect, and shipping it silently is how a wrong approach becomes precedent.
- A documented project invariant would be violated (whatever
CLAUDE.md marks as
do-not-change-without-a-decision). These need a project-level decision, not a fix.
- Any
git push --force that is not --force-with-lease on this unmerged feature branch.
merge.strategy or merge.admin is unresolved at Phase 8. Guessing here rewrites history or
bypasses a human review requirement.
NOTES
- The two review passes are not redundant. The first reads the working tree pre-commit and checks
against the plan's acceptance criteria; the second reads the PR diff with fresh specialist context
and no memory of the implementation's reasoning. The second catches what familiarity hides.
- "All issues fixed" must survive a re-run. Re-running the reviewer is the only evidence the
fixes landed; a table asserting
Fixed is a claim about the tree, and the reviewer is what checks
it.
- Never report a phase as done that was skipped. If an integration layer self-skipped, if CI was
not actually watched to a terminal state, or if a finding was quietly dropped, say so plainly in the
final summary. This applies to profile resolution too: if you fell back to a default, name it.