Skill: Ship
<purpose> Manage the complete workflow for shipping changes: bump the version, verify docs are current, commit, push, create a PR, merge it, and clean up the local branch. Every step includes safety checks to prevent data loss. </purpose> <scope> - Bump development version in `VERSION` and `package.json` - Run `/documentarian` to confirm docs are up to date - Build and typecheck verification (`npm run typecheck`, `npm run build`) - Commit with conventional commit message - Push branch and create PR via `gh` - Squash-merge the PR (repo disallows merge commits) - Switch to main, pull latest, delete local branch </scope>Commands
| Command | Description |
|---|---|
/ship | Full workflow (all phases) |
/ship version | Bump version only |
/ship pr | Push + create PR only (skip version bump) |
/ship merge | Merge existing PR + clean up only |
Workflow
<phase name="preflight"> ### Phase 1: Preflight ChecksBefore anything else, verify the working state is safe to proceed.
<action> ```bash # Current branch (must NOT be main) git branch --show-currentCheck for uncommitted changes
git status
Verify we have changes to ship
git diff --stat git diff --cached --stat
Check typecheck passes
npm run typecheck
</action>
<abort-if>
- On `main` branch (must be on a feature/fix branch)
- Typecheck fails
- No changes to commit (nothing to ship)
</abort-if>
<ask-user>
If the branch name doesn't match a clear issue or feature, ask:
- What is this change about? (for commit message)
</ask-user>
</phase>
<phase name="version-bump">
### Phase 2: Version Bump
Bump the development version using the format: `v{MAJOR}.{MINOR}.{PATCH}-dev-{YYYYMMDD}-{letter}`
<version-format>
| Component | Description |
|-----------|-------------|
| `MAJOR.MINOR.PATCH` | Semantic version (stays same during dev) |
| `YYYYMMDD` | Today's date |
| `letter` | Sequential letter for same-day releases: a, b, c... |
**Auto-increment logic**:
1. Read current version from `VERSION` file
2. If date matches today, increment the letter (a -> b -> c...)
3. If date is older, reset to today's date with letter `a`
</version-format>
<action>
1. Read `VERSION` file
2. Calculate next version
3. Update `VERSION` file with new version string
4. Sync to `package.json`:
```bash
# Strip leading 'v' for npm (package.json uses bare semver)
npm version "<version-without-v>" --no-git-tag-version --allow-same-version
- •Verify both files match </action>
Verify documentation is current with the changes being shipped.
<action> 1. Run `/documentarian` skill to review changes and update docs 2. Verify typecheck still passes: ```bash npm run typecheck ``` </action> <ask-user> After the documentarian review, ask: - Are these doc updates acceptable, or do you want to adjust anything? </ask-user> </phase> <phase name="commit"> ### Phase 4: CommitCreate a well-formed conventional commit.
<commit-conventions> This repo uses conventional commits. Infer the type from the branch name and changes:| Prefix | When |
|---|---|
fix: | Bug fixes, renames, corrections |
feat: | New endpoints, new functionality |
refactor: | Code restructuring without behavior change |
docs: | Documentation-only changes |
chore: | Dependencies, CI, tooling |
If the branch name contains an issue number (e.g., fix/batch-status-endpoint-path), reference it.
</commit-conventions>
Commit with conventional message
git commit -m "$(cat <<'EOF' <type>: <description> (#<issue>)
<body explaining why, not what>Co-Authored-By: Claude Opus 4.5 noreply@anthropic.com EOF )"
</action> <important> - Stage files explicitly by name — never `git add -A` or `git add .` - Never commit `.env`, credentials, or secrets - Always include `Co-Authored-By` trailer - Use HEREDOC for commit message formatting - **Include all local changes**: If `/ship` discovers additional uncommitted changes on the branch (e.g., new skills, doc updates, config changes), include them in the commit and PR. Don't cherry-pick only the "primary" fix — ship everything on the branch together. </important> </phase> <phase name="push-and-pr"> ### Phase 5: Push and Create PR Push the branch and open a pull request. <action> ```bash # Push with upstream tracking git push -u origin <branch-name> # Create PR with squash-friendly format gh pr create --title "<type>: <short description>" --body "$(cat <<'EOF' ## Summary <1-3 bullet points of what changed and why> ## Files changed <grouped list of changed files> ## Test plan - [ ] `npm run typecheck` passes - [ ] `npm run build` succeeds - [ ] <domain-specific checks> 🤖 Generated with [Claude Code](https://claude.com/claude-code) EOF )"
Safely merge the PR using squash merge (required by this repo).
<action> ```bash # Check for CI failures (if any checks exist) gh pr checks <pr-number>Squash merge and delete remote branch
gh pr merge <pr-number> --squash --delete-branch
</action> <important> - This repo does NOT allow merge commits — always use `--squash` - `gh pr merge --delete-branch` automatically deletes the remote branch - If merge fails due to conflicts, abort and notify the user </important> </phase> <phase name="cleanup"> ### Phase 7: Switch to Main and Clean Up Return to main branch with latest changes. Handle the case where `gh pr merge` already switched branches. <action> ```bash # Switch to main (may already be on main after gh pr merge) git checkout main # Pull latest (includes the squash-merged commit) git pull # Delete local branch (may already be deleted by gh pr merge) git branch -d <branch-name> 2>/dev/null || true # Verify clean state git status git log --oneline -3
Display a final summary of what was shipped.
<display> ``` Ship Complete ═══════════════════════════════════════════ Version: <new-version> Branch: <branch-name> → main (merged + deleted) PR: <pr-url> Commit: <commit-message-first-line> ``` </display> </phase>Safety Guarantees
<safety> | Risk | Mitigation | |------|------------| | Committing secrets | Explicit file staging, never `git add -A` | | Force-pushing | Never used — only regular `git push` | | Losing unmerged work | `git branch -d` (not `-D`) refuses if unmerged | | Merging to wrong branch | PR targets `main` by default | | Merge conflicts | `gh pr merge` fails cleanly, no data loss | | Wrong merge strategy | Always `--squash` (merge commits disallowed) | | Stale local main | `git pull` after merge | </safety>Quick Reference
<commands> ```bash # Check current version cat VERSIONStamp version from git tags (CI-style)
bash scripts/stamp-version.sh
Dry-run version stamp
bash scripts/stamp-version.sh --dry
Typecheck
npm run typecheck
Full build
npm run build
Lint
npm run lint
View PR status
gh pr status
View specific PR
gh pr view <number>
</commands> --- ## Troubleshooting <troubleshooting> | Issue | Solution | |-------|----------| | `gh pr merge` fails with merge commit error | Use `--squash` flag (this repo requires it) | | Branch already deleted | `git branch -d` will say "not found" — safe to ignore | | PR has conflicts | Pull main into branch, resolve conflicts, push, then merge | | Typecheck fails | Fix type errors before committing — never ship broken code | | Build fails | Fix the build before committing — never ship broken code | | VERSION and package.json out of sync | Update VERSION, then run `npm version` with `--no-git-tag-version` | | `gh` not authenticated | Run `gh auth login` | </troubleshooting>