---
name: ship-it
description: Carries one work item from idea to a review-ready PR: grill the plan → implement → verify → code review → security review → see it run → draft PR → green CI → PR comments → hand off. Use when the user wants the whole thing carried through or says "ship it".
---
Carry one work item from idea to a review-ready pull request, autonomously, in this codebase. After the human approves the plan, you run the whole pipeline hands-off, stopping only at a small set of human gates. The goal is that the human grills the plan once at the start and reviews the PR once at the end — and you handle everything in between, including making the build green, surviving your own reviews, and clearing CI and PR review comments.
This skill chains tools that already exist (/grill-me, /code-review,
/security-review) plus the repo's own verify gate, its app launcher, and gh. Lean on
them; don't reinvent what they do. The PR body format and PR-comment handling are built
into this skill directly (Phases 5 and 7).
[human] grill the issue + approve the plan ← gate: plan approval
│
▼
implement ──► verify gate (loop until green) ← gate: can't get green (rare)
│
▼
/code-review (effort scaled to the diff) ──► fix ← gate: uncertain findings (rare)
│
▼
/security-review ──► fix the confident findings ← gate: uncertain findings (rare)
│
▼
open the app on the page you changed and look at it
│
▼
open draft PR ──► watch CI (loop until green) ──► mark ready for review
│
▼
address PR comments ──► fix confident comments ← gate: uncertain comments
│
▼
notify: PR is green and ready ← gate: human approves / requests changes
│
└──── on "request changes" or new comments ──► back to implement/fix and re-run
The gates differ in posture, not mechanics. Code-review and security-review should almost always clear without bothering the human, so hold a high bar before pausing there — pausing is the rare exception. PR review comments are coin-flippier, so don't agonize over borderline calls; when genuinely torn, ask.
Run hands-off after plan approval. Once the human approves the plan, don't ask permission for each step — just execute the pipeline. The only legitimate reasons to stop are the gates below. If you find yourself wanting to check in "just to be safe," resist it; the human opted into auto mode precisely so they don't have to babysit.
Maintain a live TodoWrite checklist. Build it at the start — one item per phase
(Grill/plan, Implement, Verify, Code review, Security review, See it run, Open PR, Watch
CI, Address comments, Hand off), plus sub-items for the meaningful chunks of a long phase
like Implement. Exactly one item is in_progress at any time, marked in the turn you
start that work and completed in the turn you finish it — a list that lags behind
what you're actually doing is a bug, not cosmetic. The checklist is how the human watches
progress, which makes it a status update, not the check-in the hands-off rule resists.
Post a short phase summary as you cross each boundary. After implement, after the reviews, after opening the PR, after CI goes green, etc., print one or two lines: what just happened and what's next. These let the human watch progress and interrupt if something looks wrong — they are not questions and never block. Keep them terse.
The confidence bar (what "fix it vs. ask" means). "A lot of confidence" means: you understand the root cause, the fix is clearly correct, and it doesn't change product behavior in a way the human might not want. Apply those silently. Pause only when a finding is genuinely ambiguous, when fixing it would change intended behavior or scope, or when you can't tell whether it's a real problem. When you do pause, present the specific finding, your read on it, and a recommendation — not an open-ended "what do you want to do?"
Notify with the terminal bell + a status line at the two moments the human is
waiting on: when you hit a gate and need them, and when the PR is finally green and
ready. Ring the bell with printf '\a' and print a clear, skimmable status. Don't ring
it for routine phase summaries — reserve it for "I need you" moments so it stays
meaningful.
Keep the working tree honest. Re-run the verify gate after every batch of fixes (post-review, post-security, post-CI-fix, post-comment-fix). A fix that breaks the gate isn't a fix. Never push red.
This phase runs in plan mode (steps 2–4): the plan is always presented to the human
through the ExitPlanMode approval dialog, never as loose message text, and no code is
touched until it's approved.
-
Preflight. Fail fast before doing any work: confirm
ghis authenticated and pointed at the right repo (gh auth statusandgh repo view). If either is wrong, stop and surface it now — don't discover it at Phase 5 after the work is done. -
Resolve the work item. It may arrive as a GitHub issue, a Jira issue, or just a verbal description. Detect which by shape:
- GitHub issue (number, or a
github.com/.../issues/<n>URL) →gh issue view <n>(add--repo owner/nameif the URL points elsewhere) to pull the title, body, and discussion. - Jira issue (a key like
PROJ-1234matching[A-Z][A-Z0-9]+-\d+, or an Atlassian URL likehttps://<org>.atlassian.net/browse/PROJ-1234) → fetch it through the Atlassian MCP. Find the Jira "get issue" tool via ToolSearch (query something likejira get issue) and look it up by key to pull the summary, description, and acceptance criteria. - Verbal description → work from what the human said; no fetch needed.
Either way, restate the work back so you're sure you've got the intent before grilling.
- GitHub issue (number, or a
-
Enter plan mode. Call
EnterPlanModebefore grilling (skip the call if the session is already in plan mode). The whole grill-and-plan step runs inside plan mode, so nothing — code, files, worktrees — gets touched before the plan is approved.AskUserQuestionstill works in plan mode, which is all/grill-meneeds. -
Grill it. Run
/grill-meto stress-test the design against the codebase and resolve every branch of the decision tree. This is where ambiguity gets killed — it's far cheaper to settle a question now than after the PR is open. -
Write the plan, then pop the approval gate with
ExitPlanMode. Write the full plan — what you'll build, how, and how it will be verified — to the plan file, then callExitPlanMode. That is the plan-approval gate: the harness renders the plan file in an approval dialog and blocks until the human approves or requests changes. Approval puts you in auto mode for the rest of the pipeline; on rejection or feedback, fold it in, update the plan file, and callExitPlanModeagain.Hard rule: never substitute
AskUserQuestionor plain message text for this gate — text written between tool calls may never reach the human, so the plan must be presented viaExitPlanModeto guarantee they actually see it before approving. -
Create a worktree (after approval). Plan mode forbids writes, so this happens only once the plan is approved. Isolate the work in a fresh git worktree on a new branch so the main checkout stays clean and you (or another
/ship-it) can run in parallel without stepping on it. Put the worktree in a sibling directory named for the repo plus a slug (my-app→my-app-<slug>):git worktree add ../<repo>-<slug> -b <branch> # branch: feat/<slug> or fix/<issue-n>-<slug>
Then run every subsequent phase inside that worktree directory. The verify gate, CI, commits, and PR all operate on this branch — never commit straight to
main. -
(Optional) Reset context before implementing. Default is to continue straight into Phase 1 — the harness auto-summarizes as context grows. Only when the human explicitly wants the plan applied on a clean context: you can't
/clearyourself, so copy the approved plan toPLAN.mdin the worktree and either delegate Phases 1–2 to a fresh subagent seeded only with that file, or hand back for them to/clearand re-invoke/ship-itagainst the worktree. Don't offer this unprompted.
Execute the approved plan. Read the repo's CLAUDE.md / AGENTS.md first if you
haven't, and match its documented conventions and the surrounding code — some repos run
strict builds that fail on warnings, so don't introduce any. Commit in logical chunks
with clear messages.
Phase summary when done: what you built, which files, anything you deferred.
Find and run the repo's verify gate, then loop — fix, re-run — until it passes clean. Don't move on with a red gate.
How to validate is defined by the repo, not hardcoded here. Check the instructions
files (CLAUDE.md / AGENTS.md) for how this project wants changes validated, and use
the first that applies:
- A verify script exists (e.g.
./scripts/verify.sh) → run it. - A repo verify skill exists (e.g. a
/verify-gateor/verifyskill) → invoke it. - The instructions files document a gate command → run exactly that.
- Otherwise fall back to the project's own build, format/lint check, and tests.
Whatever the gate checks counts as a real failure — fix it (including writing any tests the gate requires to pass), never route around it.
If you genuinely can't get it green · GATE (rare). The default is persistence: as long as failures trace to your change, keep fixing — don't escalate just because it's taking a few tries. But don't loop forever either. Stop and surface it to the human when either is true:
- You've root-caused the failure to something outside your diff. The fast way to tell:
run the gate on a clean
main— if it fails the same way there, it's pre-existing and not yours to fix silently. Same bucket if it needs an environment you don't have (az login, a credential, network) or demands something this change legitimately can't satisfy (e.g. a coverage line in code you never touched). - You've made a handful of genuinely distinct fix attempts (≈3) and it's still red for a reason you can't pin down. Once you're guessing rather than diagnosing, that's the signal.
Escalating means stop and ask — never weaken the gate to end the loop. Do not disable a
check, add a suppression, lower a coverage threshold, or [Skip]/comment out a test to get
past it. When you stop, present the exact failing output, your root-cause read, what you
tried, and a recommendation.
Run /simplify on the diff first, then re-run the verify gate — reviewing code you're
about to restructure wastes both passes. Phases 3 and 4 are independent, so run them
concurrently when the repo calls for that; just triage both sets of findings before moving
on.
-
Pick the effort level from the change you just made.
/code-reviewtakes a level —low,medium,high,xhigh,max— and it's a real trade-off, not a dial to turn up:low/mediumreturn fewer, high-confidence findings;highthroughmaxbuy broader coverage at the cost of more uncertain findings and more wall-clock. Start from the shape of the diff:low— mechanical, no behavior change: docs, comments, formatting, a pure rename, a version bump.medium— the default, and where most PRs land: a routine feature or fix inside one subsystem, following patterns already in the codebase.high— broad or contract-changing: several subsystems touched, a shared or public signature changed, non-trivial data/query/state logic, or a large diff (≳400 changed lines).xhigh/max— large and risky at once: a data migration, an auth or authorization change, a security boundary, anything that can lose data, or a refactor rippling through many call sites. Reservemaxfor when a miss is expensive and hard to undo.
Then escalate one band for every risk factor present, whatever the diff size says: auth, authorization, or session handling; secrets, PII, or money; concurrency, async ordering, or shared mutable state; a migration or a destructive operation; code with no existing test coverage; or a spot where you had to guess while implementing. A three-line auth change is not a
low. Symmetrically, a thousand-line docs change is not ahigh— size alone escalates only when there's behavior under it.State the level and a one-line reason in the phase summary, so the human can see the call and override it if they disagree.
-
Run
/code-review <level>on the working diff to get findings with confidence/severity. Don't pass--fix— triage stays yours, under the confidence bar below. -
Fix the confident ones directly (the bar from Operating Principles).
-
Style / refactor findings need a metric. Don't apply a "this reads nicer" change on taste alone — that's how bikeshedding sneaks in. Before applying a style or structural finding, confirm it measurably improves something: lower cyclomatic complexity, shallower nesting, less duplication, fewer parameters. If you can show the metric moved, apply it and note the before/after. If you can't, drop it or route it to the gate.
-
Gate: for the rare finding you genuinely can't call (intended-behavior question, real-but-maybe-not-worth-it, ambiguous), present it with your recommendation and wait. This should be rare. Note that a
high+ review is expected to surface uncertain findings — that's what the extra coverage buys — so don't let a higher level turn into a stream of gates. Judge each finding on its merits; the level changes what you're shown, not the bar for interrupting the human. -
Re-run the verify gate after fixes.
- Run
/security-reviewon the pending changes. - Fix the confident findings directly.
- When unsure, prove it with a test. If a finding might be a real vulnerability but you're not certain, write a test that exercises the suspected hole. A passing exploit- style test turns "maybe" into "yes, fix it"; if you can't make it demonstrate a problem, that's strong evidence it's a false positive. This keeps you from either ignoring real issues or churning on phantom ones.
- Gate: present anything still genuinely uncertain after the test, with the test and your read. This should be very rare.
- Re-run the verify gate after fixes.
First, open the app on the page you changed. A green gate and two clean reviews prove the code is sound, not that the feature works. Before opening the PR, start the app and load the exact route your change affects — not just the home page.
- Use the repo's launcher if it ships one (e.g. a
/runskill, adocker compose up, a documenteddotnet run/npm run devline in the instructions files). It already knows the env vars, ports, and auth bypass the app needs. Run it in the background. - Look at the result — a screenshot of that route if the repo's tooling supports it. Confirm the thing you built is on screen with real data, not a blank frame, a login redirect, an error state, or the old behavior.
- What you see is a gate. A wrong-looking page is a failure even with a green verify and clean reviews — back to Phase 1, then re-run the gate and the reviews on the fix.
- Skip only when there's nothing to see: a pure refactor, a CI/config change, a docs edit. Anything that changes what a user sees or does gets opened.
- Stop the app when done, or leave it up and give the human the URL so they can click around while they review.
Then confirm you actually built what was asked. Re-read the issue's acceptance criteria (from Phase 0 — or, if the work came in verbally with none stated, the intent you restated and the plan that got approved) and check the implementation against each item. If anything's unmet, go back and finish it (Phase 1) before opening — a PR that doesn't satisfy the issue isn't review-ready. If an acceptance criterion turns out to be infeasible or contradicts the approved plan, that's a gate: stop and surface it rather than quietly shipping something different from what was asked.
Then:
- Push the branch.
- Open the PR as a draft with
gh pr create --draft. Link the issue in the body (Closes #<n>on its own line at the end of Why) when there is one. Opening as a draft means reviewers and review bots aren't pinged yet — you mark it ready in Phase 6 once CI is green, so nobody is summoned to look at a build that's still going red. - Write the body in the concise format below. If the repo ships a PR-format skill
(e.g.
/concise-pr), invoke it instead so this stays in sync with the repo's own convention; the format below is the fallback when there's no such skill.
Phase summary: what the page looked like, and the PR URL.
Every PR description is exactly two sections:
## Why
The problem or motivation. Max 3 sentences.
Closes #N <- own line, end of Why, nowhere else. Omit when there's no issue.
## What
- One verb phrase per decision. Max 5 bullets.Don't hard-wrap the body. GitHub wraps for you, and wrapped bullets diff badly when edited.
Rules:
- Why before what. Reviewers can read the diff for the "what," but they can't infer intent. If only one section survives, it's Why.
- Both caps are hard. Needing a 6th bullet means the PR is too big or the bullets are per-edit — fix that, don't raise the cap.
- One bullet per decision, not per edit. "Gate on PR author instead of
github.actor" is a bullet; "updated yaml" is noise. - A bullet carries no rationale. One verb phrase, ~12 words, no "because", "so that", or " — ". Justification belongs in Why.
- Title does the heavy lifting: imperative mood, specific, under ~70 characters — it's
what survives in
git logafter squash. If it needs "and", check whether it's two PRs. - At most one section beyond Why/What, and only for something a reviewer can't derive from the diff (a measured before/after, a deliberate divergence from the issue). Test counts, follow-ups, and cross-PR conflicts don't qualify — follow-ups become issues, conflicts become PR comments.
Before creating, reread the body and delete every clause a reviewer could infer from the diff. Do this as its own pass, not while drafting.
Example:
Title: Have Dependabot perform its own merges so the ruleset bypass applies
## Why
Green Dependabot PRs never automerged: native auto-merge waits on the required review and ignores bypass actors, and the `github.actor` gate skipped the job whenever a human updated the branch.
## What
- Merge by commenting `@dependabot squash and merge`
- Gate on PR author instead of `github.actor`
- Drop unneeded `contents: write` permission- Watch the checks:
gh pr checks --watch(blocks until they finish) or pollgh pr checks. - On failure, fix the cause — don't just retry. Pull the failing job's logs
(
gh run view <run-id> --log-failed), diagnose, fix, push, and watch again. - Flaky test → root-cause it, don't paper over it. If a test fails non- deterministically, find out why (ordering, shared state, timing, a real race) and fix the root cause. Re-running until it's green by luck just ships the flake to the next person. Only when you've truly established it's infrastructure (not the code) do you note that explicitly.
- Keep the branch current with
main. Ifmainhas advanced and the branch falls behind — CI requires an up-to-date branch, or conflicts appear — update it: rebase onto (or merge)main, resolve conflicts honestly (no-X ourspast them), re-run the verify gate (Phase 2), and push (--force-with-leaseif you rebased). Then re-watch. - Loop until CI is green.
- Once green, mark the PR ready for review:
gh pr ready <n>. This is the moment reviewers and review bots get pinged — which is what sets up Phase 7. (No-op if it's already non-draft, e.g. on a later loop.)
This skill handles review comments on the PR itself rather than delegating them — no
matter who leaves them (an automated reviewer, a bot, or a human). Fetch the unresolved
threads, triage each by confidence, fix the confident ones with focused commits, then
reply and resolve every thread on GitHub. The fetch/triage/reply/resolve mechanics below
mirror the repo's /review-pr-comments skill where it exists — reuse its approach so the
two don't drift. The one deliberate difference: unlike /review-pr-comments, you don't
pause to get a triage table approved — you're already in auto mode, so the confidence bar
from Operating Principles decides what you fix silently versus what you surface at the gate.
You marked the PR ready at the end of Phase 6, so reviewers and bots are now running. Their
comments usually land a couple of minutes later: wait ~1 minute, then poll once a minute
for up to 5 minutes, and start triaging the moment anything appears. If nothing shows up
in that window and the repo has no review automation, say so and move to Phase 8 rather
than hanging. This harness blocks foreground sleep, so drive the poll with a scheduled
wakeup, Monitor, or a backgrounded command — yield until there's something to do.
You have the PR number from Phase 5; also grab the repo owner and name (the GraphQL query needs them):
gh repo view --json owner,name --jq '"\(.owner.login) \(.name)"'Pull every review thread with its comments via GraphQL, then keep only the unresolved ones:
gh api graphql -f query='
query($owner: String!, $repo: String!, $pr: Int!) {
repository(owner: $owner, name: $repo) {
pullRequest(number: $pr) {
reviewThreads(first: 100) {
nodes {
id
isResolved
isOutdated
path
line
startLine
comments(first: 20) {
nodes { id body author { login } createdAt }
}
}
}
}
}
}' -f owner='OWNER' -f repo='REPO' -F pr=PR_NUMBERFilter to isResolved == false. Ignore pure bot overview comments with no actionable
feedback (e.g. a "reviewed N files" summary). For each remaining thread, note the thread
id (needed to reply and resolve), path, line/startLine, the first comment's body
(later comments are replies — read the whole conversation, the concern may already have
been discussed away), and the author. If there are no unresolved threads, say so and move
to Phase 8.
For each thread, read the file at the commented path and the PR diff for that file
(gh pr diff <n> -- <path>) to understand the context, then sort it into one of three
buckets — same bar as the code review:
- Confident it's valid → names a real bug, missing edge case, security/perf issue, legitimate convention violation, or a clearly-missing addition. Fix it.
- Confident it's not valid → false positive, based on outdated code, already addressed in a later commit, pure subjective preference, or contradicts the project's conventions. Reply with the reason and resolve it (exact wording in "Reply and resolve").
- Genuinely unsure → gate. Surface it for the human with your read and a recommendation. This is the gate that fires most often, so don't agonize over borderline calls — but when truly torn, ask rather than guess.
For each valid thread, in the order they appear:
-
Make the change. If the comment carries a
```suggestionblock, apply it as closely as possible. For a multi-line thread (startLine→line), read the full range first. -
Make a focused commit that references the comment — one commit per thread, so each fix stays independently reviewable rather than batched:
fix: add null check for user input Addresses PR review comment by @reviewer about missing validation on the user input field. -
Capture that commit's SHA right after committing (
git rev-parse --short HEAD) and keep a thread-id → SHA map. You'll cite the exact SHA when you resolve the thread — don't reconstruct it later by guessing which commit went with which thread.
Re-run the verify gate (Phase 2) after the fixes — a fix that breaks the gate isn't a fix, and you never push red. Then push once at the end; a single push avoids kicking off a CI/review run per commit:
git pushAfter pushing, close out each thread on GitHub. For each valid thread, reply with the SHA you captured for it (not a guessed one), then resolve it:
gh api graphql -f query='
mutation($threadId: ID!, $body: String!) {
addPullRequestReviewThreadReply(input: { pullRequestReviewThreadId: $threadId, body: $body }) {
comment { id }
}
}' -f threadId='THREAD_ID' -f body="Resolved in <commit-sha>"
gh api graphql -f query='
mutation($threadId: ID!) {
resolveReviewThread(input: { threadId: $threadId }) { thread { id isResolved } }
}' -f threadId='THREAD_ID'For each not-valid thread, reply with a concise Not going to fix due to <reason>
(e.g. "already addressed in ", "outdated — code has changed", "intentional, see ")
via the same addPullRequestReviewThreadReply mutation, then resolveReviewThread it.
Leave the threads you gated on alone — don't resolve those; the human owns them.
Return to Phase 6 to re-watch CI. New commits may trigger a fresh review pass, so re-fetch threads and repeat until every thread is either resolved or gated and CI is green. Gated threads are a stop, not a spin: once the only things left unresolved are ones you've gated for the human, hand off (Phase 8) — don't loop waiting for them to clear themselves.
When CI is green and every review thread is either resolved or gated for the human, hand it back:
- Ring the bell and print a clear status: PR URL, one-line summary of what shipped, CI green, comments resolved (and any you've gated, called out explicitly), "ready for your review."
- Then stop and wait. The human will approve or request changes.
printf '\a'When the human requests changes (or new review comments arrive), you're not done — loop back:
- Understand exactly what's being asked; grill it briefly if it's ambiguous.
- Make the changes (Phase 1).
- Re-run the verify gate (Phase 2).
- Re-run
/code-reviewand/security-reviewif the changes are non-trivial (Phases 3–4); skip for tiny, obviously-safe edits — use judgment. Re-score the effort level against this round's delta, not the original diff — a one-line fix on top of a big feature is alow/mediumreview, not a repeat of whatever you ran the first time. - Re-open the app if the change is visible, then push and re-watch CI (Phases 5–6); clear any new PR comments (Phase 7).
- Notify ready again (Phase 8).
Repeat until the PR is approved and merged. That's the whole loop — one issue, carried to the finish.
- Unattended runs. This skill proceeds without asking between steps, but the harness may still prompt for individual tool permissions depending on the human's mode. For a truly hands-off run, the human should start in a mode that doesn't prompt per action. Mention this once if permission prompts are interrupting the flow.
ghmust be authenticated and pointed at the right repo. You run inside whichever git repo holds the work, each with its own CI.- Scoped skills auto-resolve. When you're working inside a repo that ships its own
scoped variant of a skill (e.g. a repo-scoped
grill-me), invoking the base name resolves to the most specific one for the files you're changing. Just call/grill-meand/code-review. - Stay in the worktree. Everything after Phase 0 happens inside the worktree created
there. Don't merge or close the PR yourself — the final approve/merge is the human's
call. Once they've merged, the worktree can be cleaned up with
git worktree remove ../<repo>-<slug>(offer this; don't do it unprompted, since unmerged work would be lost).