ship · git:20260818.a92c83e · 2026-08-18 · sha256 862412e0ea6fd4e5

ship git:20260818.a92c83eA

Immutable. This exact content never changes and is served at /api/v1/blob/862412e0ea6fd4e5.

---
name: ship
description: Takes work that is already implemented and committed on a branch and ships it: runs the repo's ship gates off receipts, pushes, opens a draft pull request, moves the board to in review, and removes the worktree. Use when the code exists and the task is to get it reviewable, or on /ship, and not when anything still needs writing.
---

# ship: committed work to a draft pull request

The tail of the funnel, on its own. Use it when the work already exists: you implemented
by hand, or a task run stopped after the gate, or a branch has been sitting for a day.

If anything still needs writing, this is the wrong skill. Use the task funnel, which has
triage, a spec and a review in front of this.

Input: nothing, or an issue number. Empty means the current branch.

## Stage 0 · Is this shippable

```
kit doctor
git status --short
git log --oneline <base>..HEAD
```

Stop and report, without shipping, on any of:

- **Uncommitted changes.** The review diffs commits, so uncommitted work is invisible.
  Commit it or say why it is excluded.
- **No commits ahead of the base.** There is nothing to ship.
- **A commit that does not follow the repo's convention.** `kit config get .docs.commits`
  names the document. Fix the message before the pull request quotes it.
- **No review has happened.** Ask before shipping unreviewed work; `kit review <level>`
  is minutes and a defect found here costs a round instead of a revert. If the user says
  ship anyway, say in the pull request body that it went out unreviewed.

## Stage 1 · Gates, off receipts

```
kit gate ship
```

Gates whose receipt already covers this exact tree are skipped and say so. The tree SHA
includes untracked files, so any edit at all invalidates it. That is the difference
between skipping a check and proving it was already paid.

A red gate stops here. `kit gate` prints a `flake` line when the failing file is on the
repo's quarantine list and whether this branch's diff can even reach it; a red gate in a
file the diff never touched is not a reason to stop, and it is not a reason to change code
either. Re-run that file alone.

## Stage 2 · Push

The repo's pre-push hook runs its own checks. The fix for a red hook is the failing check,
never `--no-verify`, and the plugin's `protect-tests` hook refuses it anyway.

## Stage 3 · The pull request

Open it exactly as the repo's pipeline document says. That document, not this skill, owns
the base branch, the draft state and the issue reference.

Fill the repo's template honestly, which means four specific things:

1. **A check is ticked only if it ran.** An unticked box is information; a ticked box that
   did not run is a lie with a green mark next to it.
2. **Answer the surfaces question in prose**, even when the answer is nothing. A sentence
   beats a box, because the box is what gets ticked without being read.
3. **PLAUSIBLE findings go under points of attention**, one line each. A finding you could
   not prove is not a blocker and not nothing.
4. **The body has a budget, and it is measured.** Write it to a file and run
   `kit pr-body <file>` before `gh pr create`: at most **2000 characters of prose** and
   **600 per section**, counting what you added rather than the template, its tables, its
   checkboxes or a fenced block. Over budget it exits 3 and prints the sections by size.

Cut, do not compress. The body is the only thing a human reads before deciding whether to
review the diff, so length there is spent out of the reviewer's attention rather than out
of nothing. Everything worth keeping has a nearer home: the reasoning behind a finding goes
in a pull request comment, a product decision goes in the issue, and why a non-obvious line
exists goes in the code beside it. Measured on 2026-08-17: one body ran to 11902 characters
of prose across 9 sections, 7 of them over the cap, in front of a 163-line source diff.

What the body owes the reader and nothing more: what changed and why, what to check, what is
knowingly left out and who owns it, and a link to the comment holding the long form.

## Stage 4 · Board and teardown

```
kit board in_review --issue <issue>
kit worktree gc --yes
```

The board move goes after the pull request URL exists. Never `done`: this does not merge,
and the tracker's own automation owns that column.

`gc` removes only what it can prove is finished, which now includes an open pull request
sitting at exactly this head, since everything is then on the remote and the branch
survives removal. It keeps the rest and prints why, including a worktree with nothing of
its own on it, which cannot be told apart from one another session just created. `rm
<issue>` was the step here until 0.3.0 and could not succeed once the pull request existed.
`gc --dry-run` first if you want to see the verdicts without acting.

## Stage 5 · Report

At most 600 characters of prose, roughly 95 words: what shipped, the gate result including
what was skipped by receipt, the pull request link, and what was left out. Never declare
done without the numbers, and name a gate that stopped you as such.

Then `kit context`, because a ship is a boundary. It prints HOLD, AT THE NEXT BOUNDARY,
NOW or LATE off how full the window is, and anything but HOLD belongs in the report as
one clause. Measured across 16 sessions: 16% of the spend went to requests that reread
nothing from cache and wrote a whole prefix back, at US$ 2.62 each against US$ 0.20 for
a normal one, so a boundary passed without compacting is the cheapest saving there is
to skip. And whatever it says, compact before a long pause.

Characters, not lines, because a paragraph counts as one line in the source and four in the
terminal. 100 characters is about one rendered line.