# Ship Pr

> Ship a single change through a worktree -> branch -> PR -> squash-merge -> cleanup workflow - pick the right branch type (feat/fix/chore/docs/hotfix), implement, verify with this repo's real build/typecheck/test commands, open the PR, check CI, merge, and clean up. Use for any single PR-sized change in any git repo with a GitHub remote - a bug fix, a small feature, a doc edit, a dependency bump. For 3+ independently shippable PRs from one analysis pass, see parallel-ship. For sequential phases where each PR builds on the last, see epic-ship.

- Skill: `davidtheproduct/ship-pr` (Agent Skill, multi-file: 4 files)
- Install (CLI): `npx skillmds@latest add davidtheproduct/ship-pr`
- Raw SKILL.md: https://api.skillmd.com/api/skills/davidtheproduct/ship-pr/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Productivity
- Author: davidtheproduct (https://skillmd.com/u/davidtheproduct)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/davidtheproduct/ship-pr

---


# Ship PR

A repo-agnostic worktree -> branch -> PR -> squash-merge -> cleanup workflow. Works in any git repo with an `origin` remote and `gh` configured; the scripts detect the package manager and default branch rather than assuming pnpm/Turborepo/`main`.

This is the atomic unit of the ship stack: `parallel-ship` and `epic-ship` both call this skill internally (per fanned-out agent and per phase respectively). Invoke it directly for anything that is one PR-sized change.

> **Naming note:** if a repo you work in defines its own project-level `ship-pr` skill (tuned to that repo's quirks), rename this personal-level copy (e.g. `ship-pr-global`) - in Claude Code's skill resolution, a personal-level skill overrides a project-level one of the same name, so an identical name would silently shadow the tuned version.

## Step 1: Pick the branch type

| Change scope                             | Prefix     |
| ---------------------------------------- | ---------- |
| Docs only (markdown, `docs/**`)          | `docs/`    |
| New behavior                             | `feat/`    |
| Bug correction (behavior existed, broke) | `fix/`     |
| Tooling/refactor, no behavior change     | `chore/`   |
| Production is broken right now           | `hotfix/`  |

If unsure between `feat/` and `fix/`: did this behavior exist and work correctly before? Correcting it is `fix/`. Adding something new is `feat/`.

## Step 2: Worktree -> implement -> verify -> ship -> merge -> cleanup

1. **Worktree**: `bash ${CLAUDE_SKILL_DIR}/scripts/worktree-new.sh <prefix>/<slug>`. Run from anywhere inside the target repo. It fetches the remote default branch (detected locally via `refs/remotes/origin/HEAD`, falling back to a `git remote show origin` network call only if that's unset, then to `main`), creates `.worktrees/<slug>/` at the repo root (added to `.gitignore` automatically if missing), installs dependencies with whatever package manager the repo's lockfile indicates, and copies untracked `.env*` files from the repo root if present.
2. **Implement**: do the actual work. Implement directly rather than just writing a plan, unless the user asked for a plan. For large files (200+ lines), make incremental targeted edits rather than rewriting the whole file in one shot.
3. **Verify - do not skip, do not just document it**: check `package.json` (or the repo's equivalent - `Makefile`, `Cargo.toml`, etc.) for what's actually available and run it:
   - A build script (`build`, `compile`) if present.
   - A typecheck script (`typecheck`, `tsc`, or `tsc --noEmit` directly) if the repo is TypeScript.
   - The test suite (`test`).
   - Lint (`lint`) if configured.
   - UI/visual change: run the app, take a screenshot, and actually look at it (use a screenshot-verification skill if you have one).
   - Deps added/updated: install with `--frozen-lockfile`/`ci` (not a plain install) to catch a drifted lockfile the way CI would.
   - Markdown touched: the skill-scoped hook on this skill greps **added lines** in every touched `.md` file for em-dashes before `git push`/`gh pr create` and blocks if it finds any. Note this checks the diff, not the whole file - if this repo's CI whole-file-scans touched markdown (verify by checking the CI config), pre-existing em-dashes you didn't write can still fail CI even though this hook passed.
   - If the repo has branch protection requiring CI to pass, wait for it. If it doesn't (check repo settings or just try merging), CI is a second-opinion signal - don't sit and wait on a slow/queued run when the manual checks above already cover it.

   Recommended CI job naming, if this repo is setting up checks from scratch: `docs-check` for doc-only PRs (link/em-dash checks), `code-quality` for lint, `unit-tests` for the test suite - scoped by changed-path so doc-only PRs skip the code checks.
4. **Ship**: commit, push, `gh pr create --fill`.
5. **Merge**: note that on private repos under GitHub's free plan there is no branch protection at all - nothing platform-side stands between a green PR and the default branch, so this skill's rules are the actual merge gate. The policy:

   | Change | Merge behavior | Why |
   | --- | --- | --- |
   | Docs-only | Self-merge immediately | Trivial verification, nothing platform-side to wait for |
   | feat / fix / chore | Self-merge after verification | Same default; step 3 just has more to clear first |
   | Payments, auth, data-integrity | Leave PR open for human review | The one exception - never self-merge, even on green CI |

   Glance at `gh pr checks <PR#>`. Still running/queued and not required to merge -> proceed, don't block on it. Red for something step 3 already predicted and fixed -> proceed. Red for something unpredicted -> stop and investigate before merging, don't merge blind. Otherwise: `gh pr merge --squash --delete-branch`, then `bash ${CLAUDE_SKILL_DIR}/scripts/worktree-close.sh <slug>` **immediately**, not "later" - don't batch cleanup.

## Model economics

This skill runs as a fork (`context: fork`), and **a forked skill always inherits the invoking session's model - the Skill tool has no `model` parameter**. Invoked from a frontier-tier session, every run costs frontier rates (~180k tokens per full implement-verify-merge cycle observed). For a mechanical, fully-specified change - and especially when fanning out several at once via `parallel-ship` - spawn `Agent(model: sonnet)` (or the cheapest tier that can do the job) with this skill's workflow steps written into the prompt instead of forking this skill. Reserve same-tier forks for changes needing real judgment. Five parallel frontier-tier forks once burned ~1M tokens and hit a session usage cap mid-epic; the Sonnet-agent rerun did equal-quality work at roughly 40% of the cost.

## Hard rules

- Never push directly to the default branch. Every change is its own PR.
- Fork fresh from the remote default branch (`worktree-new.sh` does this automatically) - never build on top of another unmerged branch.
- Close the worktree immediately after merge.
- One PR-sized change per invocation. If what you're doing is several independently shippable PRs from one analysis pass, use `parallel-ship` instead. If it's sequential dependent phases (PR N+1 builds on PR N), use `epic-ship` - it calls this skill once per phase in order.
- High-risk changes (payments, auth, data-integrity, anything a human should eyeball before it goes live) should not self-merge on green CI - leave the PR open for human review instead of completing step 5 automatically.

## Enforcement

The skill-scoped hook (`scripts/check-em-dashes.sh`) fires on every `git push`/`gh pr create` while this skill is active and blocks on em-dashes added to touched markdown. It cannot catch everything CI might check (lockfile drift, lint, types, whole-file em-dash scans) - the manual verify step above still matters.

