Ship
Success
I := commits on feature branch ∧ PR ∃ ∧ agent review APPROVED (or clean after ≤K fix↔review iters) ∧ label reviewed ∧ CI green ∧ (auto-merge attempted ∨ merge path reported)
V := gh pr view state MERGED ∨ (CI green ∧ reviewed ∈ labels ∧ autoMergeRequest ≠ null) ∨ explicit Abort/Stop
Let:
Β := current branch (must ∉ {main, master, staging})
β := staging (∃ origin/staging) ∨ main
P := open PR for Β (number)
K := max fix↔review iterations (default 2, override --max-fix-iters N)
Σ_s := session step map (in-memory only)
ψ_r := PR comments ∃ body starting with ## Code Review
ψ_f := PR comments ∃ body starting with ## Review Fixes Applied
dirty := git status --porcelain non-empty
ahead := git rev-list --count origin/${β}..HEAD > 0 (or unpushed commits on Β)
bar := same quality bar as /R-dev — hand-authored feel; QG = floor
Not a substitute for /R-dev. Use when implementation is already done (or nearly) and you want the gate path only:
commit → /R-pr → /R-dev-review → [/R-fix ↺ /R-dev-review]×≤K → label reviewed → /R-ci-watch → [/R-cleanup]
Compared to /R-dev Verify (pr → ci-watch → validate → review → fix):
- Review before merge-gate — agent review + fix loop first;
reviewed+ CI watch last - No frame/spec/plan/implement/validate (optional: jump via
/R-dev --from …if you need those) - Commit step included (dirty tree → conventional commit + push)
¬rewrite child skill logic. ¬auto-merge mid-CI. Label reviewed is the merge gate (merge-on-green / gh pr merge --auto --merge).
Entry
/R-ship → full pipeline from current branch
/R-ship #42 → bind to PR #42 (must match current branch head)
/R-ship --draft → pass --draft to /R-pr
/R-ship --base staging → pass --base to /R-pr
/R-ship --from review → skip commit+pr if PR already exists
/R-ship --max-fix-iters 1 → cap fix↔review loops
/R-ship --no-commit → refuse to create commits; dirty tree → halt
/R-ship --skip-cleanup → after merge, do not run /R-cleanup
Steps
commit → pr → review → fix ↺ review → label-reviewed → ci-watch → cleanup
| Step | Class | Skill / action | On success → |
|---|---|---|---|
| commit | adv (inline) | conventional commit + push if dirty | pr |
| pr | adv | skill: "R-pr" (+ --draft / --base if set) |
review |
| review | verdict | skill: "R-dev-review" |
APPROVED → label-reviewed · CHANGES_REQUESTED → fix · Stop → halt |
| fix | loop | skill: "R-fix", args: "#{P}" |
strip premature reviewed if present → review (iter++) |
| label-reviewed | adv (inline) | ensure auto-merge + reviewed label |
ci-watch |
| ci-watch | adv | skill: "R-ci-watch", args: "--pr {P}" |
cleanup (if merged ∨ green path) |
| cleanup | adv | skill: "R-cleanup" (scoped if issue# known) |
done |
Step 0 — Parse + Guard Rails
DRAFT=false; BASE=""; FROM=""; K=2; NO_COMMIT=false; SKIP_CLEANUP=false; PR_ARG=""
for arg in $ARGUMENTS; do
case "$arg" in
--draft) DRAFT=true ;;
--base) next is BASE ;;
--base=*) BASE="${arg#--base=}" ;;
--from) next is FROM ;;
--from=*) FROM="${arg#--from=}" ;;
--max-fix-iters=*) K="${arg#--max-fix-iters=}" ;;
--no-commit) NO_COMMIT=true ;;
--skip-cleanup) SKIP_CLEANUP=true ;;
\#[0-9]*|[0-9]*) PR_ARG="${arg#\#}" ;;
esac
done
git fetch origin ${β} --quiet(best-effort)- Β ∈ {main, master, staging} → REFUSE: "Create/switch to a feature branch first."
- Resolve P:
PR_ARGset →gh pr view $PR_ARG --json number,headRefName,state,url- else →
gh pr list --head "$Β" --state open --json number,url -q '.[0]'
- If
FROMset ∈ {review, fix, label-reviewed, ci-watch, cleanup}: mark prior steps done in Σ_s; require P ∃ for any step after pr.
Step 1 — Scan State
Σ_s defaults false. Detect:
commit: ¬dirty (nothing to commit) — still may have ahead commits
pr: P ∃ ∧ open
review: P ∃ ∧ (ψ_r ∨ reviewDecision ∈ {APPROVED, CHANGES_REQUESTED})
fix: P ∃ ∧ ψ_f (last cycle; re-review may still be needed)
label: P ∃ ∧ label reviewed ∈ PR
ci: null (session)
cleanup: null (session)
iter: 0 # fix↔review iterations completed
Present short banner (no full /R-dev phase bars):
## Ship — {Β} [PR #{P}|no PR]
commit {✓|→|skip}
pr {✓|→|pending}
code-review {✓|→|pending} (iter {iter}/{K})
fix {cond}
reviewed+CI {✓|→|pending}
cleanup {✓|skip|pending}
→ Next: {S*}
Step 2 — commit (inline)
Skip if: --no-commit ∧ ¬dirty · or ¬dirty (nothing to stage).
If dirty ∧ --no-commit: halt — "Working tree dirty; commit manually or drop --no-commit."
If dirty:
git status -sb+git diff/git diff --staged+git log -5 --oneline- Stage intentional paths only — ¬
git add -Aif secrets risk (same rule as/R-fix). Prefer explicit paths from status. - Message: Conventional Commits, focus on why. HEREDOC commit.
git push -u origin HEAD(orgit pushif upstream set).- Fail → Retry | Abort.
If ¬dirty ∧ ¬ahead ∧ ¬P: REFUSE — "Nothing to ship (clean tree, no commits ahead of base, no PR)."
If ¬dirty ∧ ahead: mark commit done; continue (already committed).
Step 3 — pr
Skip if: P ∃ open for Β (unless user forced update — then invoke /R-pr which offers Update).
Invoke: skill: "R-pr" with args:
--draftif DRAFT--base {BASE}if BASE set
On success: re-resolve P from gh pr list --head "$Β". ¬P → halt.
Silent via /R-ship: no "Next: /R-ci-watch" from child — ship owns chaining.
Step 4 — code-review
Invoke: skill: "R-dev-review" (PR auto-detected from branch).
Interpret Phase 8 outcome (user decision inside code-review when standalone; when driven by ship, prefer):
| Outcome | Ship action |
|---|---|
| Clean / APPROVED / user picks Merge as-is | → do not let code-review label+merge alone if ship will own gate — if code-review already labeled, continue to ci-watch; else → label-reviewed |
| User picks Fix now / CHANGES_REQUESTED | → fix (if iter < K) |
| Stop / Abort | halt ship |
| F = ∅ clean approve | → label-reviewed |
Important: /R-dev-review Phase 8 may offer Merge as-is (label + auto-merge). Under /R-ship, that path is allowed and equivalent to label-reviewed + handoff; then ship still runs ci-watch to observe green + merge. If user already labeled inside code-review, skip duplicate label step.
Step 5 — fix (loop)
Enter only if: review requested changes ∧ iter < K.
Invoke: skill: "R-fix", args: "#{P}".
On success:
iter := iter + 1- Strip premature merge gate (fix Phase 7 may add
reviewedbefore re-review):gh pr edit "$P" --remove-label reviewed 2>/dev/null || true - Goto Step 4 (code-review) for re-verify.
iter ≥ K on entry to fix: refuse another fix cycle — present Merge as-is (→ label-reviewed) | Stop.
Step 6 — label-reviewed (inline)
Skip if: reviewed already on PR.
- Rebase check (best-effort, same spirit as code-review Phase 8):
git fetch origin ${β} # if behind: rebase + force-with-lease on feature branch only; conflict → halt - Enable auto-merge if supported:
(¬plain merge while checks in progress)gh pr merge "$P" --auto --merge 2>/dev/null || true - Add label:
gh api "repos/{owner}/{repo}/issues/${P}/labels" -f "labels[]=reviewed" - Confirm:
gh pr view $P --json labels,autoMergeRequest
Step 7 — ci-watch
Invoke: skill: "R-ci-watch", args: "--pr ${P}"
Bash timeout ≥ 20 min when auto-merge expected (see ci-watch skill).
| Exit | Ship action |
|---|---|
| 0 merged / green+AM | → cleanup |
| 1 CI failed | present Retry CI (gh run rerun) | fix locally | Abort |
| 2 cancelled | report; Retry | Abort |
| 4 green unmerged | report blocker (conflicts / AM off / timeout); rebase | re-label | Abort — ¬blind Retry-CI |
Step 8 — cleanup
Skip if: --skip-cleanup ∨ PR not merged yet (still open) ∨ user declines.
If PR merged:
skill: "R-cleanup"
If issue number known from branch (feat/42-… → 42) or PR closing issues: prefer skill: "R-cleanup", args: "--scope #N".
Continuous-flow rules (same as /R-dev)
- ¬ask "Ready to proceed to /X?"
- ¬summarize "Just finished X, moving to Y"
- Child skills return → re-scan Σ_s → invoke next step same turn
- Exception: verdict/loop gates inside code-review and fix (human decisions stay)
- Exception: CI failure / merge blocker → present choice
Task list (optional)
If TaskCreate available, seed kind: "ship-pipeline" tasks for active steps only (same pattern as /R-dev 2b, lighter). Metadata: { kind: "ship-pipeline", pr: P, step, iteration }. ¬required for correctness.
Options
| Flag | Description |
|---|---|
| (none) | Full pipeline on current branch |
#N / N |
Bind to PR number N |
--draft |
Create draft PR |
--base <branch> |
PR base override |
--from <step> |
commit|pr|review|fix|label-reviewed|ci-watch|cleanup |
--max-fix-iters N |
Default 2 |
--no-commit |
Never create commits |
--skip-cleanup |
Skip post-merge cleanup |
Safety Rules
- NEVER ship from
main/master/staging - NEVER
git push --force— only--force-with-leaseon feature branch rebase - NEVER plain
gh pr mergewhile checks IN_PROGRESS/QUEUED - NEVER leave
reviewedon a PR that still needs re-review after/R-fix(strip before re-review) - ALWAYS cap fix↔review at K (default 2)
- ALWAYS stage intentional files only on commit step
- NEVER rewrite
/R-pr,/R-dev-review,/R-fix,/R-ci-watch,/R-cleanuplogic — delegate
Edge Cases
| Scenario | Behavior |
|---|---|
| Dirty tree + secrets patterns | Halt; ask user to unstage |
| PR exists, local behind origin/Β | git pull --ff-only or halt on divergence |
/R-pr REFUSE (no commits) |
Halt with message |
| Review clean first try | Skip fix; label + ci-watch |
| Fix applies label early | Strip before re-review |
| AM not available on repo | Label still applied; ci-watch reports green; note manual merge path |
| Multiple open PRs for branch | Impossible normally; if API returns many, pick latest open |
--from review without PR |
REFUSE |
Chain Position
- Phase: Ship (feature → base)
- Predecessor: ad-hoc implementation, or
/R-dev-implement, or end of/R-devBuild without Verify - Successor:
/R-promote(staging→main) when base was staging - Class: meta-orchestrator (like
/R-dev, narrower)
Relation to /R-dev
/R-dev |
/R-ship |
|
|---|---|---|
| Scope | issue lifecycle Frame→Ship | branch already has code |
| Order | pr → ci-watch → validate → review → fix | commit → pr → review → fix → reviewed+ci-watch |
| validate | yes | no |
| commit | via implement | explicit first step |
| Entry | /R-dev #N |
/R-ship on feature branch |
Prefer /R-ship for hotfix / already-pushed work / agent sessions that coded first. Prefer /R-dev when artifacts (frame/spec/plan) matter.
Exit
- Merged: print PR URL + base branch; if staging, hint
/R-promotewhen ready. Stop. - CI green, AM pending: print status; Stop (or keep watching if user asks).
- Failure: error + Retry | Abort. Stop.
- User Stop: "Stopped at {S*}. Resume:
/R-ship --from {S*}."
$ARGUMENTS