Ship — test, review, version, changelog, commit, PR. Fully automated.
Scanned 5/27/2026
Install via CLI
openskills install iamvonpasion/hashb---
description: Ship — test, review, version, changelog, commit, PR. Fully automated.
---
# Ship
Fully automated. The user said `/ship` — run straight through and emit the SHIPPED receipt at the end.
> Follows `rules/integrity.md` — verify before asserting (I7) on post-fix tests, escalate STOP triggers (I2), no cosmetic fixes (I3) on review findings.
**Only stop for:**
- On the base branch (abort)
- Merge conflicts that can't be auto-resolved
- Build, lint, or test failures
- Pre-Landing Review findings that need user judgment
- MINOR or MAJOR version bump (ask)
**Never stop for:**
- Uncommitted changes (include them)
- Version bump choice for MICRO/PATCH (auto-decide)
- CHANGELOG content (auto-generate)
- Commit message approval (auto-commit)
## Presentation Rules
Follow the shared formatting rules in `skills/shared/formatting.md`.
1. **Progress indicator** — every output starts with:
```
/ship ═══════════════════════════════════════════════════════════
▸ Step 1 Pre-flight
○ Step 1.5 Change Classification
○ Step 2 Merge Base Branch
○ Step 2.5 Build & Lint
○ Step 3 Run Tests
○ Step 3.5 Pre-Landing Review
○ Step 4 Version Bump
○ Step 5 Changelog
○ Step 5.5 Conditional Post-Changelog Tasks
○ Step 6 Commit
○ Step 7 Push
○ Step 8 Create PR
○ Step 8.5 CI Status + SHIPPED Receipt
═════════════════════════════════════════════════════════════════
```
Update `▸` (current), `✓` (done), `○` (pending). Completed steps show a status note (e.g., `✓ tests pass`, `✓ v1.1.0`).
2. **Final SHIPPED receipt** — Step 8.5 emits an Option A inverted-pyramid header (`★ SHIPPED` / `⚠ POST-LAND RISKS` / `✎ FOLLOWUPS` / `▸ NEXT`) above `══ Receipts — …` separators. See Step 8.5.
---
## Step 1: Pre-flight
```bash
# Branch/base detection — see skills/shared/preflight.md
BRANCH=$(git branch --show-current 2>/dev/null || echo "unknown")
if command -v gh >/dev/null 2>&1; then
BASE=$(gh pr view --json baseRefName -q .baseRefName 2>/dev/null \
|| gh repo view --json defaultBranchRef -q .defaultBranchRef.name 2>/dev/null \
|| echo "main")
else
echo "⚠ gh CLI not found — defaulting BASE to 'main'"
BASE="main"
fi
echo "BRANCH: $BRANCH BASE: $BASE"
```
If on the base branch: abort. "Ship from a feature branch."
```bash
git status
git diff $BASE...HEAD --stat
git log $BASE..HEAD --oneline
```
---
## Step 1.5: Change Classification
Classify the diff to determine which pipeline steps are required. This is the single source of truth for step skipping — downstream steps consult `CHANGE_SCOPE`, not their own logic.
```bash
git diff $BASE...HEAD --name-only
```
Tag each file against these pattern groups (first match wins):
| Group | Patterns |
|-------|----------|
| DOC | `*.md`, `*.txt`, `*.rst`, `*.adoc`, `docs/**`, `doc/**`, `documentation/**`, images in doc dirs (`*.png`, `*.jpg`, `*.svg`), `LICENSE*`, `NOTICE`, `AUTHORS`, `CONTRIBUTORS`, `.github/*.md`, `.github/ISSUE_TEMPLATE/**`, `.github/PULL_REQUEST_TEMPLATE/**` |
| TEST | `**/*.test.*`, `**/*.spec.*`, `**/__tests__/**`, `**/test_*.py`, `**/tests/**`, `**/test/**`, `**/*.stories.*`, `**/fixtures/**`, `**/testdata/**`, `**/__mocks__/**`, `**/__snapshots__/**` |
| META | `VERSION`, `CHANGELOG*`, `TODOS.md`, `specs/*.todos.md`, `.gitignore`, `.gitattributes`, `.editorconfig`, `.github/CODEOWNERS` |
| DEPS | `package-lock.json`, `pnpm-lock.yaml`, `yarn.lock`, `Pipfile.lock`, `poetry.lock`, `go.sum`, `Cargo.lock`, `Gemfile.lock`, `composer.lock` (NOT manifests like `package.json`, `pyproject.toml` — those contain source config) |
Determine `CHANGE_SCOPE`:
| All files tagged as… | CHANGE_SCOPE |
|---|---|
| DOC only | `docs` |
| TEST only | `tests` |
| META only | `meta` |
| DEPS only | `deps` |
| Any mix, or any file unmatched | `source` |
**Conservative default:** if any file doesn't match a non-source pattern, `CHANGE_SCOPE = source`. Empty diffs are `source`. Renamed files use the destination path. Files always win over branch names (I1).
**Branch signal (secondary):** if branch prefix (`docs/`, `test/`, `chore/`) conflicts with file classification, log: "Branch prefix suggests {X} but files include {Y} — using file classification (I1)."
**User override:** if the user says "run full pipeline" or "skip nothing", set `CHANGE_SCOPE = source`.
### Step-Skip Matrix
| Step | `docs` | `tests` | `meta` | `deps` | `source` |
|------|--------|---------|--------|--------|----------|
| 2 Merge Base | RUN | RUN | RUN | RUN | RUN |
| 2.5 Build & Lint | SKIP | SKIP build; run lint | SKIP | RUN | RUN |
| 3 Tests | SKIP | RUN | SKIP | RUN | RUN |
| 3.5 Review | Security scan only | Lightweight | Security scan only | Dep audit only | RUN |
| 4 Version | RUN (MICRO) | RUN | SKIP (circular) | RUN | RUN |
| 5 Changelog | RUN | RUN | SKIP (circular) | RUN | RUN |
| 5.5+ | RUN | RUN | RUN | RUN | RUN |
**Rationale:** docs cannot break builds or tests. Tests need to run (that's the point) but don't produce build artifacts. Meta changes to VERSION/CHANGELOG shouldn't generate new version/changelog entries (circular). Deps can break builds and runtime — must verify. Security scan never skips (credentials can hide in any file type).
**Re-classification gate:** if Step 3.5 auto-fixes introduce source files, re-classify as `source` and run any previously skipped steps before proceeding to Step 4.
Skipped steps show as `— Step N {name} ({scope} — skipped)` in the progress indicator.
Cache `CHANGE_SCOPE` for all downstream steps.
---
## Step 2: Merge Base Branch
Merge base into the feature branch so tests run against merged state:
```bash
git fetch origin $BASE && git merge origin/$BASE --no-edit
```
If merge conflicts: try auto-resolve for simple cases (VERSION, CHANGELOG ordering). If complex, **STOP** and show conflicts.
---
## Step 2.5: Build & Lint
**Scope gate:** Runs for `source`, `deps`. Skipped for `docs`, `meta`. For `tests`: skip build, run lint only.
Read the consumer's Project Profile `Stack` field and detect commands:
| Stack signal | Build | Lint |
|---|---|---|
| .NET (`*.csproj`, `*.sln`) | `dotnet build` | `dotnet format --verify-no-changes` |
| Node (`package.json`) | `npm run build` (if script) | `npm run lint` (if script) |
| Python (`pyproject.toml`) | — | `ruff check` or `flake8` (per config) |
| Go (`go.mod`) | `go build ./...` | `golangci-lint run` (if installed) |
| Rust (`Cargo.toml`) | `cargo build` | `cargo clippy` |
**Build:** Run the detected build. **STOP on failure** — code that doesn't compile does not ship.
**Lint:** Run the detected lint.
- Auto-fixable violations → fix silently, stage changes
- Non-fixable → **STOP** and show
If no build/lint detected: warn and skip ("No build/lint commands detected — consider adding stack info to Project Profile").
---
## Step 3: Run Tests
**Scope gate:** Runs for `source`, `tests`, `deps`. Skipped for `docs`, `meta`.
Run the project's test suite (parallel if multiple). **STOP on any failure.** If all pass: note counts briefly, continue.
---
## Step 3.5: Pre-Landing Review
**Scope gate:** Full review for `source`. Lightweight for `tests`. Security scan only for `docs`, `meta`. Dep audit only for `deps`. Secret scan always runs regardless of scope.
If `/review` was already run on this branch (check conversation context or commit messages for review verdicts), trust the verdict and run only a lightweight final check (secrets, data safety, mechanical issues).
If `/review` was NOT run, perform a full pre-landing diff:
```bash
git diff origin/$BASE
```
Classify each finding and act:
| Category | Examples | Action |
|---|---|---|
| Security | Hardcoded secrets, SQL injection, missing auth, unescaped input | ASK |
| Data safety | Destructive migrations without rollback, missing backfills | ASK |
| Code quality | Dead code, N+1, stale comments, missing error handling | ASK if judgment-laden; auto-fix if mechanical |
| Frontend (if frontend files changed) | a11y gaps, missing responsive behavior, AI-slop patterns | ASK |
| Mechanical | Trailing whitespace, import ordering | Auto-fix silently |
If any auto-fixes applied: commit them, then re-run tests (Step 3) before continuing.
Output: `Pre-Landing Review: N issues — M auto-fixed, K asked`
### Secret + Dependency Scan
Run available scanners; warn if none found.
| Tool | Trigger | On critical finding |
|---|---|---|
| `gitleaks` or `trufflehog` (secrets) | Always | **STOP.** List findings. Do not proceed. |
| `npm/pnpm/yarn audit`, `pip audit`, `cargo audit`, `govulncheck` (deps) | Lockfile detected | **STOP** on critical. Warn-and-proceed on high/medium. |
```bash
command -v gitleaks >/dev/null && gitleaks detect --source . --log-opts="$BASE..HEAD" --no-banner
command -v trufflehog >/dev/null && trufflehog git file://. --since-commit="$(git merge-base HEAD $BASE)" --only-verified
[ -f package-lock.json ] || [ -f pnpm-lock.yaml ] || [ -f yarn.lock ] && npm audit --audit-level=critical
[ -f Pipfile.lock ] || [ -f requirements.txt ] && pip audit
[ -f Cargo.lock ] && cargo audit
[ -f go.sum ] && govulncheck ./...
```
Either STOP with findings, or note `Secret scan: clean · Dep audit: clean` and continue.
---
## Step 4: Version Bump
**Scope gate:** Runs for `source`, `tests`, `deps`. Skipped for `meta` (circular — the change IS to version/meta files). For `docs`: auto-decide MICRO.
Read `VERSION`. Auto-decide bump from diff size (heuristic — teams may adjust):
| Diff size | Bump |
|---|---|
| < 50 lines, trivial | MICRO (4th digit) |
| 50+ lines, bug fixes, features | PATCH (3rd digit) |
| Major feature / architecture | MINOR — **ask user** |
| Milestone / breaking change | MAJOR — **ask user** |
Bumping a digit resets digits to its right to 0. If no `VERSION` file: skip.
---
## Step 5: CHANGELOG
**Scope gate:** Runs for `source`, `tests`, `deps`, `docs`. Skipped for `meta` (circular — the change IS to changelog/meta files).
Auto-generate from commits on the branch:
```bash
git log $BASE..HEAD --oneline
```
Categorize into Added / Changed / Fixed / Removed. Insert after file header, dated today. Format: `## [X.Y.Z.W] - YYYY-MM-DD`.
---
## Step 5.5: Conditional Post-Changelog Tasks
These run only when their trigger fires. Skip silently otherwise.
**Tracker preflight for Step 5.5:** Run the Tracker Detection block from
`skills/shared/tracker.md` §Tracker Detection if `TRACKER_TYPE` is not yet
cached. The §Issue Resolution Block runs inside the `TRACKER_TYPE=github-issues`
trigger row below.
| Trigger | Action |
|---|---|
| `TODOS.md` exists | Cross-ref diff to open items in root **General** section AND every `specs/*.todos.md` referenced by the `## By spec` index. Conservatively mark completed items with `**Completed:** vX.Y.Z (YYYY-MM-DD)`; move to Completed section in their owning file. After marking, refresh the root index `done/total` counts for any per-spec file that changed. Output what was marked, per file. |
| `TRACKER_TYPE=github-issues` | Run the §Issue Resolution Block from `skills/shared/tracker.md` if `TASK_ISSUE` is not already set — resolve from branch name, user message, or conversation context. **Additional `/ship` fallback:** if `TASK_ISSUE` is still empty after the standard resolution chain, query the PR body for `Closes #N` or `Fixes #N` patterns (`gh pr view --json body -q .body 2>/dev/null`) and extract the issue number. Then for each task referenced in this shipment: (1) close the GitHub Issue via `gh issue close {N}`, (2) remove all `status:*` labels via `gh issue edit {N} --remove-label "status:backlog,status:planning,status:dev,status:review,status:testing"`, (3) move project card to Done column (best-effort — see `skills/shared/tracker.md` §Project Board Sync). Verify no orphan open issues remain for the milestone: if all milestone issues are closed, close the milestone and archive the project board (see `skills/shared/tracker.md` §Issue Close Block). |
| `specs/deltas/*.md` exists | For each delta: read ADDED / MODIFIED / REMOVED, apply to the target spec in `specs/`, update "Last updated", move delta to `specs/deltas/archive/`. If a spec's `Source:` is `engineering-inferred` and a delta from `/spec` is being merged, upgrade the source to `product-authored`. Warn-and-skip if target spec missing. Stage changes. |
| Capability fully shipped (all tasks in `specs/{slug}.todos.md` are `[x]` AND user signals capability completion) | Archive `specs/{slug}.md` and `specs/{slug}.todos.md` together to `specs/archive/`. Remove the matching line from the root `TODOS.md` `## By spec` index. Conservative — only act when the user explicitly indicates the capability is done. |
| Lifecycle artifacts present (spec/design/eng/review/qa/retro produced this session, or `.qa-reports/` / `.retro/` files exist) | Archive into `.history/{branch}-{YYYY-MM-DD}/`. Only archive what exists; never create empty placeholders. Stage `.history/`. |
Be conservative: only act when the trigger is unambiguous. The artifact archive preserves the full decision chain (why → how → reviewed → tested → learned).
---
## Step 6: Commit (bisectable chunks)
Group changes into logical commits. Each commit = one coherent change.
**Ordering:**
1. Infrastructure (migrations, config, routes)
2. Models & services (with their tests)
3. Controllers & views (with their tests)
4. VERSION + CHANGELOG + TODOS.md (final commit)
**Rules:** A file and its test go in the same commit · each commit must be independently valid (no broken imports) · dependencies first · small diffs (< 50 lines, < 4 files) → single commit is fine · only the final commit gets the co-author trailer.
```
chore: bump version and changelog (vX.Y.Z.W)
Co-Authored-By: Claude <noreply@anthropic.com>
```
**Verification gate:** if any code changed after Step 3's test run (review fixes, etc.), re-run the full test suite. "Should work now" is not evidence — run it. If tests fail: **STOP**. Do not push.
---
## Step 7: Push
```bash
git push -u origin $BRANCH
```
Never force push.
---
## Step 8: Create PR
```bash
gh pr create --base $BASE --title "<type>: <summary>" --body "..."
```
PR body: **Summary** (CHANGELOG bullets) · **Pre-Landing Review** (findings or "No issues") · **Test plan** (suite results with counts).
---
## Step 8.5: CI Status + SHIPPED Receipt
After PR creation, check CI:
```bash
gh pr checks $BRANCH --watch --fail-fast 2>/dev/null || true
```
| CI status | Action |
|---|---|
| Passing | Note "CI: passing" — ship complete |
| Running | Note "CI: running — monitor before merging" with checks URL |
| Failed | **STOP.** Show failures. "CI failed — investigate before merging." |
| No checks | Warn "No CI checks detected — verify manually" |
> Local test results are necessary but not sufficient. CI may catch environment differences, dependency resolution issues, or integration failures local runs miss. Never declare ship complete without noting CI status.
Emit the final receipt as an Option A inverted pyramid:
```
▎ ★ SHIPPED
▎
▎ PR #{n} — {commit subject}
▎ Branch landed on {base} · CI {green|yellow|red} · {N} commits · {N} files
▎ Scope: {docs|tests|meta|deps|source}{" — N steps skipped" if non-source, omit for source}
▎ ⚠ POST-LAND RISKS
▎
▎ • {risk drawn from change scope — material only; "None — clean cut" if so}
▎ • {risk}
▎ ✎ FOLLOWUPS
▎
▎ F1 {action, ≤40 chars} → {when/who, ≤8 words}
▎ F2 {action} → {when/who}
▎ ▸ NEXT — /hashb:retro (recommended — non-trivial change){· /hashb:fix (if CI failed) — only when flagged}
```
Receipts (full CI detail when not green, file list, commit hashes, version-bump rationale) appear below `══ Receipts — {section} ══` separators on demand.
**Verbose mode:**
```
══ Receipts — CI Status ════════════════════════════════════════════════════════
{detailed CI output if not green; otherwise omit this block}
══ Receipts — Files & Commits ══════════════════════════════════════════════════
{file count · commit hashes · branch → base · version-bump rationale}
```
Suppress `══ Receipts — Files & Commits ══` block by default — the session
summary covers key facts. CI Status still emits if not green (failure is
always newsworthy). User can surface via `receipts ship`. In verbose mode,
emit the full Files & Commits receipt.
---
## Session Summary
When `/ship` completes a chain involving 2+ skills, emit a session summary
after the SHIPPED receipt. This is the user's single view of everything
that happened since the entry-point gate. Omit when `/ship` is invoked
standalone (no chain context).
```
══ SESSION SUMMARY ══════════════════════════════════════════════════════════════
What: {2-line feature summary from /spec's THE OVERVIEW}
Principles: P1={val} · P2={val} · P3={val} · P4={val}
/spec ✓ {N} ACs · specs/{slug}.md
/design ✓ {N} screens · {key decision}
/eng ✓ Approach {letter} · {N} tests planned
/tdd ✓ {N}/{N} GREEN · {N} files
/review ✓ {verdict}
/ship ✓ PR #{num} · v{version}
Decisions:
1. {skill}: {decision} — {P-principle cited}
2. {skill}: {decision} — {P-principle cited}
Escalations: {none — or numbered list}
═════════════════════════════════════════════════════════════════════════════════
```
**Rules:**
- Chain Log: `✓` completed, `—` skipped, `✗` failed.
- Decisions: ≤5 non-obvious autonomous choices. Cite the principle that drove each.
- Escalations: any points where the chain paused for user input.
- If a skill was skipped (e.g. no UI → no `/design`), use `—` with reason.
- Omit `Principles:` line if the chain didn't start from `/spec`.
---
## Next Step
| Condition | Next | Why |
|---|---|---|
| Ship successful | `/hashb:retro` | Capture learnings, persist if non-trivial |
| Ship blocked by tests or CI | `/hashb:fix` | Investigate before retry |
| Ship blocked by review findings | Address findings → re-run `/ship` | Fix issues first |
**Default chain behavior:** PR created → emit session summary → auto-proceed to `/hashb:retro`. Blocked → stop the chain, present blocker to user.
---
## Rules
- **Never skip build / lint / tests for `source` scope.** If they run and fail, stop. For non-source scopes (`docs`, `tests`, `meta`, `deps`), steps are conditionally skipped per the Step 1.5 matrix. When classification is ambiguous, default to `source` and run everything (I2). Rule Challenge (I11): the original "never skip" rule assumed all changes carry build/test risk. File-path classification proves docs and meta changes cannot break builds — running them adds latency without safety value.
- **Never force push.**
- **Never push without fresh verification** if code changed after tests (I7).
- **Split commits for bisectability.**
- **TODOS completion detection must be conservative** across root `TODOS.md` and every `specs/*.todos.md` it indexes. Refresh index `done/total` counts after marking. Capability archival (move `{slug}.md` + `{slug}.todos.md` to `specs/archive/`) only on explicit user signal.
- **Report CI status.** Never declare ship complete without it.
- **Auto-fix only mechanical** (whitespace, imports). Anything judgment-laden goes to ASK (I3, I9).
- **The goal:** user says `/ship`, next thing they see is the PR URL + CI status + SHIPPED receipt.
No comments yet. Be the first to comment!