| name | ship |
| description | Use when code is ready to ship — creates PRs, merges, deploys, and verifies. Handles the full PR-to-production pipeline. Triggers on /ship. |
| concurrency | exclusive |
| depends_on | ["review","qa","security"] |
| summary | Release pipeline. PR creation, CI monitoring, post-deploy verification, rollback plan. |
| estimated_tokens | 350 |
| hooks | {"PreToolUse":[{"matcher":"Bash","command":"./ship/bin/pre-ship-check.sh"}]} |
/ship — Ship to Production
You get code from "done" to "verified in production" in one pass. You own the full pipeline: pre-flight, PR, CI, deploy, verification. If something breaks after merge, you rollback first and debug second.
Telemetry preamble
Defensive telemetry init. No-op if telemetry is disabled via NANOSTACK_NO_TELEMETRY=1, ~/.nanostack/.telemetry-disabled, or if the helpers are removed.
_P="$HOME/.claude/skills/nanostack/bin/lib/skill-preamble.sh"
[ -f "$_P" ] && . "$_P" ship
unset _P
Session state
Read profile, run_mode, autopilot, and plan_approval per reference/session-state-contract.md BEFORE doing any pre-flight or pipeline work. This is required for /ship because every subsequent step in this skill mutates state (commit, push, PR, deploy, rollback) and the contract forbids that when run_mode == report_only.
SESSION=$NANOSTACK_STORE/session.json
[ -f "$SESSION" ] || SESSION="$HOME/.nanostack/session.json"
PROFILE=$(jq -r '.profile // (if (.capabilities // null) == null then "guided" else "professional" end)' "$SESSION" 2>/dev/null || echo "professional")
RUN_MODE=$(jq -r '.run_mode // "normal"' "$SESSION" 2>/dev/null || echo "normal")
AUTOPILOT=$(jq -r '.autopilot // false' "$SESSION" 2>/dev/null || echo "false")
PLAN_APPROVAL=$(jq -r '.plan_approval // (if .autopilot then "auto" else "manual" end)' "$SESSION" 2>/dev/null || echo "manual")
Report-only mode
When RUN_MODE == "report_only", /ship enters Shipping report mode and MUST NOT mutate anything. The session-state contract is explicit: report-only sprints exist so the user can see what /ship would do without committing the agent to action.
Allowed in report-only:
- Read session state, artifacts, git status, branch info.
- Inspect what would change: list files staged, the branch name, the commit message that would be used, the PR title/body that would be filed, the deploy target.
- List blockers: missing review/security/qa artifacts, uncommitted changes that need attention, broken README links from
quality-check.sh.
- Print a final "Shipping report" summary that names every action the user can take to ship for real.
Forbidden in report-only:
git commit, git push, git tag, any other mutating git command.
gh pr create, gh pr merge, any other GitHub mutation.
- Deploy commands (
fly deploy, vercel deploy, wrangler deploy, etc.).
- Rollback commands.
bin/init-project.sh --fix, bin/nano-doctor.sh --fix, any other repair flag.
- File writes outside
.nanostack/ (read-only inspection only). save-artifact.sh ship is skipped by default; treat artifact persistence as a future opt-in (--save-report).
If RUN_MODE == "report_only", jump straight from this section to the Report output section at the end of this skill, then stop. Do not run pre-flight, do not run quality-check (it reads only and is fine), do not enter the PR / CI / deploy flow below.
Local Mode
Run source bin/lib/git-context.sh && detect_git_mode.
If local (no git repo): Skip the entire PR/CI/deploy flow below. Instead:
- Run
ship/bin/quality-check.sh (already works without git).
- Verify files from the plan exist and are non-empty.
- Detect project type and show the result immediately:
- HTML → run
open index.html (or the main HTML file) so the user sees it instantly. Then say "Se abrió en tu navegador."
- Python → "Corré: python3 main.py"
- Node → "Corré: npm start y abrí localhost:3000"
- Other → "Tu proyecto está en [ruta completa]"
- If the user wants to publish: suggest drag-and-drop hosting (Netlify, Vercel). Walk through it step by step.
- Save artifact and run compound as normal.
Never mention PR, CI, branch, merge, deploy, rollback, or slash commands. Output: "Listo. Para verlo: [comando]."
If local-git (git, no remote): Run pre-ship check and quality check. Skip PR/CI/deploy. Suggest git tag for versioning. Output: "Listo. Commit: [hash]."
If full: Continue with the normal process below.
Process
1. Pre-flight Check
Run both checks before proceeding:
ship/bin/pre-ship-check.sh
ship/bin/quality-check.sh
If either reports errors, fix them before proceeding. Warnings are informational but should be reviewed.
Resolve context and verify review findings were resolved:
~/.claude/skills/nanostack/bin/resolve.sh ship
The output is JSON with upstream_artifacts (review, security, qa paths). If a review artifact exists, read it and check that all blocking findings have been addressed. For each blocking finding, verify the code at the reported file and line no longer has the issue. If a blocking finding is still present, do NOT proceed. Flag it.
Then verify:
git status
git fetch origin && git log --oneline HEAD..origin/main | head -5
If tests fail, fix them first. Do not ship broken code with a "will fix later" comment.
If the branch is behind, rebase or merge:
git rebase origin/main
git merge origin/main
2. PR Preview (mandatory stop)
Before creating the PR, show the user a full preview. This is a mandatory stop because after creation it's public.
## PR Preview
**Title:** {{title}}
**Branch:** {{branch}} → {{base}}
**Files changed:** {{count}}
### Summary
{{1-3 bullets of what changed and why}}
### Changes
{{file list with one-line description each}}
### Test plan
{{how to verify}}
Wait for user approval. Only proceed after explicit confirmation. If the user adjusts something, update the preview and ask again.
3. Create PR
After approval, use the template at ship/templates/pr-template.md for the PR body.
gh pr create \
--title "{{concise title, under 70 chars}}" \
--body "$(cat <<'EOF'
{{filled PR template}}
EOF
)"
PR title rules:
- Under 70 characters
- Start with a verb: Add, Fix, Update, Remove, Refactor
- Describe the what, not the how
- No ticket numbers in the title (put them in the body)
PR body rules:
- Summary: 1-3 bullet points of what changed and why
- Test plan: how to verify this works
- Link to related issues/tickets
4. Monitor CI
After creating the PR, check CI status:
gh pr checks <number> --watch
If CI fails:
- Read the failure log:
gh pr checks <number> --fail-only
- Fix the issue and push
- Do not retry without understanding the failure
- If a test is genuinely flaky (not caused by your change), note it in the PR
5. Post-Merge Verification
After the PR is merged:
gh pr view <number> --json state,mergedAt
gh run list --limit 3
If the project has a staging/production URL, run a post-deploy checklist:
- Smoke test: Does the changed feature work? (manual or
/qa --quick against prod URL)
- Error check:
gh run view --log-failed — any new errors in the deploy?
- Side effects: Did anything else break? Check the pages/endpoints adjacent to your change.
- Metrics: If monitoring exists (Grafana, Datadog, CloudWatch), check error rate and latency for 5 minutes post-deploy. Any spike > 2x baseline → investigate before moving on.
If any check fails: stop and rollback before debugging. A broken prod is worse than a reverted feature.
6. Rollback Plan
If something goes wrong after deploy:
git revert <merge-commit-sha> --mainline 1
gh pr create --title "Revert: {{original PR title}}" --body "Reverting due to {{reason}}"
Document what went wrong for the team.
7. Repo Quality Standards
Before creating the PR, verify the standards in ship/references/repo-quality-standards.md (README links, PR/commit quality, repo hygiene). The public repo is the face of the project. ship/bin/quality-check.sh automates the checks it can; use judgment for the rest.
After shipping, do these steps in order:
Step 1: Save the artifact. Run this command now — do not skip it. The save is validated against the per-phase schema (see reference/artifact-schema.md); a normal-mode ship artifact requires summary (object) and context_checkpoint. Report-only ship runs may save a looser shape with run_mode: "report_only".
The snippet below sets safe JSON defaults first so jq --argjson does not fail when a shell variable is unset (PR_NUMBER may legitimately be unset in local-mode ships, on a no-PR push, or when CI has not reported yet).
: "${PR_NUMBER:=null}"
: "${PR_URL:=}"
: "${PR_TITLE:=}"
: "${PR_STATUS:=open}"
: "${CI_PASSED:=false}"
SHIP_JSON=$(jq -n \
--argjson pr_number "$PR_NUMBER" \
--arg pr_url "$PR_URL" \
--arg title "$PR_TITLE" \
--arg status "$PR_STATUS" \
--argjson ci_passed "$CI_PASSED" \
--arg checkpoint_summary "PR #N <title> shipped, CI status, deploy result." \
'{
phase: "ship",
summary: {
pr_number: $pr_number,
pr_url: $pr_url,
title: $title,
status: $status,
ci_passed: $ci_passed
},
context_checkpoint: {
summary: $checkpoint_summary,
key_files: [],
decisions_made: [],
open_questions: []
}
}')
~/.claude/skills/nanostack/bin/save-artifact.sh ship "$SHIP_JSON"
~/.claude/skills/nanostack/bin/sprint-journal.sh
For a report-only ship (no PR cut, no commit), save the artifact with run_mode: "report_only" instead so the validator accepts the looser shape:
~/.claude/skills/nanostack/bin/save-artifact.sh ship \
'{"phase":"ship","run_mode":"report_only","summary":"Pre-flight check only; nothing was pushed."}'
Step 2: Proof block. Before "How to see the result", emit a proof block summarizing what was verified during the sprint. Read it from session phase_log:
SESSION=$NANOSTACK_STORE/session.json
[ -f "$SESSION" ] || SESSION="$HOME/.nanostack/session.json"
jq -r '
([.phase_log[]? | select(.phase == "review" and .status == "completed")] | length) as $rev |
([.phase_log[]? | select(.phase == "security" and .status == "completed")] | length) as $sec |
([.phase_log[]? | select(.phase == "qa" and .status == "completed")] | length) as $qa |
"Reviewed: \(if $rev > 0 then "yes" else "no" end)\n" +
"Security checked: \(if $sec > 0 then "yes" else "no" end)\n" +
"QA checked: \(if $qa > 0 then "yes" else "no" end)"
' "$SESSION" 2>/dev/null
If a phase says no, list it under "Not verified" so the user sees what was skipped instead of inferring it from absence.
Step 3: How to see the result.
Read profile from session per reference/session-state-contract.md. The branch differs by profile:
If profile == "guided" (or no git remote, even when professional): Skip the deployment menu and focus on how to try the result locally. Tell the user where the entry point is and the exact command to run, then list anything that is not yet verified (e.g. "I did not deploy this to the internet"). One next action only. Follow reference/plain-language-contract.md for wording. Example:
Resultado: Listo para probar.
Como verlo:
1. Abri index.html en el navegador.
Que revise:
- La pantalla carga.
- El boton principal responde.
- No encontre secretos en archivos visibles.
Pendiente:
- No esta publicado en internet todavia.
- No revise la seguridad de las dependencias.
If profile == "professional" and autopilot == true: Skip this question. Go directly to Next Step (compound + sprint summary). The user will decide how to run it after the sprint closes.
Otherwise (professional, manual), ask:
How do you want to see it?
- Local — I'll start the server and show you how to open it
- Production — I'll guide you through deploying to the internet
- I'm done — just the commit
If Local (option 1):
- HTML files: "Open
index.html in your browser"
- Web apps: start the server (
npm start, node src/server.js, etc.) and tell the user the URL
- CLI tools: show the command to run it
- Never auto-open URLs or execute
open commands. Show the path and let the user decide.
If Production (option 2):
Detect project type, recommend ONE provider (Next.js→Vercel, Node→Railway, Static→Cloudflare Pages, Python→Railway, Go→Fly.io). Walk through: account, connect repo, env vars, push. Mention domain (~$10/yr), SSL (automatic), monitoring (Sentry free + UptimeRobot free). Show monthly cost.
If Done (option 3): Skip to next features.
Report output (report-only mode only)
When RUN_MODE == "report_only", this is the only section that runs. It produces a summary the user can read and act on; the agent itself does not commit to any action.
The report has four blocks:
- What would ship. Branch name (or "no git remote"), staged files, commit message that would be used, PR title and body that would be filed.
- What is verified. Read fresh artifacts from
.nanostack/. List which of review / security / qa are present and pass. Use bin/find-artifact.sh <phase> 1 to confirm freshness. If any are missing or stale, name them.
- What is not verified. Anything the report could not check: deploy target, post-deploy canary, dependencies, integration with downstream services. State this honestly; do not hide gaps.
- What the user can do next. Exactly the commands needed to ship for real, in order. Example:
To ship for real:
/review # missing
/security # missing
/qa # missing
session.sh init development --run-mode normal
/ship
Use the wording in reference/plain-language-contract.md when PROFILE == "guided" (Resultado / Como verlo / Que revise / Pendiente). Professional output may keep technical terms (PR, CI, branch).
Stop after printing the report. Do not call telemetry-finalize with success; pass report_only so the event reflects what actually happened.
Output Format
Close with a summary:
Ship: PR #N created. CI passed.
Tests: X → Y (+N new). No regressions.
Include before/after test counts when tests were added. Quantify the improvement.
Gotchas
- Run tests before creating PR. CI is slower than catching it locally.
- One PR = one concern. Split unrelated changes.
- Check existing PRs before creating yours. Search first.
- Read CONTRIBUTING.md. Every project has different rules.
Telemetry finalize
Before handing off to compound or the user:
_F="$HOME/.claude/skills/nanostack/bin/lib/skill-finalize.sh"
[ -f "$_F" ] && . "$_F" ship success
unset _F
Pass abort or error instead of success if ship did not complete normally.
Next Step
After shipping, two things happen in order:
First: capture learnings. Run compound immediately:
Use Skill tool: skill="compound"
Do not ask. Do not skip. Compound reads the sprint artifacts and saves solutions for future sprints.
Then: close the sprint. This is the last thing the user sees. Make it count.
1. What was built. Summarize what the user now has in plain language. Not phase names or artifact counts. What does the thing DO, where is it, and how to use it.
2. How to use it. Show the exact command or URL to try it right now.
3. What could come next. Suggest 2-3 concrete extensions as /feature commands the user can run immediately.
Example:
Sprint complete. You have a JSON validator CLI.
Try it: node src/index.js test.json
Ideas for the next feature:
/feature Add --format flag to pretty-print valid JSON
/feature Add directory mode: jsonlint schemas/*.json
/feature Add --fix mode that auto-corrects trailing commas