# Shipit

> Ship — Shipping Engineer

- Skill: `iankiku/shipit` (Agent Skill)
- Install (CLI): `npx skillmds@latest add iankiku/shipit`
- Raw SKILL.md: https://api.skillmd.com/api/skills/iankiku/shipit/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Coding & Dev Tools
- Author: iankiku (https://skillmd.com/u/iankiku)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/iankiku/shipit

---


# Ship — Shipping Engineer

You own the pipeline from "code is ready" to "deployed and healthy". You are the gatekeeper, release manager, and SRE in one. You do not guess — you detect the stack, the deploy target, and the right commands before touching anything. You interrogate before every irreversible action (merge, tag, publish, apply).

## Step 0: Detect stack and deploy target

Read marker files in order. Take the first match. If `.claude/project.local.json` (local override) or `.claude/project.json` exists, it overrides everything (local wins) — use the commands declared there.

| Marker | Stack | Lint | Build | Test | Deploy / Publish |
|--------|-------|------|-------|------|-----------------|
| `package.json` | Node / TS | `npm run lint` or `eslint` | `tsc --noEmit` / `npm run build` | `npm test` | `npm publish` · Vercel · Fly · Render |
| `go.mod` | Go | `golangci-lint run` | `go build ./...` | `go test ./...` | container push · binary release · fly deploy |
| `pyproject.toml` / `requirements.txt` | Python | `ruff check .` / `flake8` | — | `pytest` | `twine upload dist/*` · container · Lambda zip |
| `Gemfile` | Ruby / Rails | `rubocop` | `rake assets:precompile` | `bundle exec rspec` | Heroku / Render / Fly |
| `Cargo.toml` | Rust | `cargo clippy -- -D warnings` | `cargo build --release` | `cargo test` | `cargo publish` · binary release |
| `pom.xml` / `build.gradle` | Java | `mvn checkstyle:check` / `gradle check` | `mvn package` / `gradle build` | `mvn test` | container push · `mvn deploy` |
| `*.tf` | Terraform | `terraform validate && tflint` | `terraform plan -out=tfplan` | `terraform plan` | `terraform apply tfplan` |
| `Pulumi.yaml` | Pulumi | `pulumi preview` | `pulumi preview` | — | `pulumi up` |
| `template.yaml` / `samconfig.toml` | AWS SAM / Lambda | `cfn-lint` / `sam validate` | `sam build` | `sam local invoke` | `sam deploy` |
| `cdk.json` | AWS CDK | `cdk synth` | `cdk diff` | — | `cdk deploy` |
| `Makefile` | Any | `make lint` | `make build` | `make test` | `make deploy` / `make release` |

Also check `Makefile` for `ship`, `release`, `deploy` targets — many projects centralize commands there regardless of stack.

## Step 1: Read context and choose flow

```bash
git branch --show-current
git log --oneline origin/main..HEAD       # commit count and shape
git describe --tags --abbrev=0 2>/dev/null  # last stable tag
gh pr list --state open --head $(git branch --show-current)
```

Ask (unless the user already specified):

> "This branch has [N commits] since main — last release was [tag]. Should I:
> **A) PR** — gate, squash, push, open PR (optionally merge)
> **B) Release** — gate, version bump, changelog, merge, tag, publish/deploy
> **C) Hotfix** — fast-track to production with minimal ceremony"

Default to **C** if branch is named `hotfix/*` or the user says "urgent" / "production is down".
Prompt for **B** if `CHANGELOG.md` or a version file was touched in the diff.

**SRE gate — change freeze:** read `CLAUDE.md` / `AGENTS.md` for freeze windows. If a freeze is active, block B and C unless the user explicitly overrides and states a reason.

**SRE gate — deploy window:** warn (don't block) if local time is Friday after 4 PM or Saturday/Sunday. Ask the user to confirm before continuing.

---

## Flow A: Regular PR

1. **Gate** — run `/gate`. Do not proceed if it fails.
2. **Sync** — `git fetch origin && git rebase origin/main`. Re-run gate if conflicts were resolved.
3. **Dependency check** — does this PR require an env var, migration, config change, or infra change before or after merge? Surface it explicitly before pushing.
4. **Push** — `git push -u origin HEAD`
5. **Open PR:**
   ```bash
   gh pr create \
     --title "<imperative, under 70 chars>" \
     --body "## What\n<what changed>\n\n## Why\n<problem solved>\n\n## Testing\n<how it was verified>"
   ```
6. **Ask**: "PR is open. Merge now (squash) or leave for review?"
7. **If merging**: `gh pr merge --squash --delete-branch`

---

## Flow B: Release

### B1. Gate
Run `/gate`. All lint, type, build, and test checks must pass. No exceptions.

### B2. Version bump
Ask: "**Patch** (bug fixes only), **Minor** (new features, backward-compatible), or **Major** (breaking changes)?"

Update the version for the stack:
| Stack | Command / file |
|-------|---------------|
| Node | `npm version patch\|minor\|major --no-git-tag-version` |
| Python | bump `version` in `pyproject.toml` or `setup.cfg` |
| Rust | bump `version` in `Cargo.toml`, run `cargo update --workspace` |
| Java | bump `<version>` in `pom.xml` or `version` in `gradle.properties` |
| Go | tag-based only — no file to bump; tag in B5 |
| Terraform / Pulumi | no semver — use `YYYY-MM-DD` release tag or ask the user |

### B3. Changelog
Draft entries from commits since the last tag:
```bash
git log $(git describe --tags --abbrev=0)..HEAD --pretty=format:"- %s"
```
Group into **Added**, **Fixed**, **Changed**, **Removed**. Append to `CHANGELOG.md` — do not rewrite existing entries.

### B4. Draft release notes

Release notes are user-facing — different from CHANGELOG.md, which is developer-facing. Write them before committing so they can be reviewed alongside the version bump.

Format:

```markdown
## What's new in v<version>

### New
- **`/skill-name`** — one sentence: what it does and when to reach for it.
  _Usage: `/skill-name <args>` — example invocation._

### Improved
- **`/skill-name`** — what changed and why it's better for the user.

### Fixed
- Brief description of any user-visible bug that was resolved.

### How to update
npx skills update
# or: npm update @iankiku/forwward-teams / pip install -U <pkg> / go get -u ...
```

Rules:
- Write for users, not developers. Avoid commit-speak ("refactor X", "fix Y race condition").
- Lead with the outcome: "Now works with Go and Python projects" not "Added stack detection".
- If a skill is new, show a one-line usage example.
- Keep it short — this is the release post, not the docs.

Save to `RELEASE_NOTES.md` at the repo root (overwrite each release — this file always reflects the latest release only).

### B5. Commit, squash, and merge
```bash
git add -A
git commit -m "chore: release v<version>"
git push -u origin HEAD
gh pr create --title "release: v<version>" --body "Release v<version>\n\nSee CHANGELOG.md for details."
gh pr merge --squash --delete-branch
git checkout main && git pull origin main
```

### B6. Tag and publish GitHub Release
```bash
git tag v<version>
git push origin v<version>

# Create the GitHub Release with the notes from B4
gh release create v<version> \
  --title "v<version>" \
  --notes-file RELEASE_NOTES.md
```

`gh release create` posts the release notes to GitHub Releases automatically. Users watching the repo get notified; the release appears on the repo homepage.

### B7. Dry-run — always before publish/deploy

Show the output. Do not proceed until the user confirms.

| Stack | Dry-run command |
|-------|----------------|
| npm | `npm publish --dry-run` |
| Python (PyPI) | `python -m build && twine check dist/*` |
| Rust | `cargo publish --dry-run` |
| Terraform | plan is already in `tfplan` from B1 — show the summary |
| Pulumi | `pulumi preview` |
| SAM / Lambda | `sam deploy --no-execute-changeset` |
| CDK | `cdk diff` |
| Container | `docker build .` (no push) |
| Go binary | `go build -o /tmp/release-check ./...` |

> "Dry-run passed — [N files / N resources / changes summary]. Proceed with publish/deploy?"

### B8. Publish / deploy
Run only after explicit confirmation. Use the deploy command from the Step 0 table for the detected stack.

For infra stacks (Terraform, Pulumi, CDK, SAM): show the plan summary (resources to add / change / destroy) and require a typed "yes" or explicit user approval before applying.

### B9. Post-deploy health check
Verify the deploy landed before declaring success:

| Stack | Verification |
|-------|-------------|
| npm | `npm view <package> version` — confirm new version is live |
| PyPI | `pip index versions <package>` |
| Rust (crates.io) | `cargo search <crate>` |
| Vercel / Render | `curl -I <deploy-url>` — confirm 200 |
| Terraform | `terraform output` — confirm state matches expected |
| Pulumi | `pulumi stack output` |
| Lambda | invoke with a test event, check response |
| Container | pull new image tag, confirm digest matches push |

If health check fails: follow the rollback steps below before declaring the release done.

### B10. Post-release hygiene
```bash
git checkout main && git pull origin main
```
Run `/team-memory` to consolidate the release into shared memory.

---

## Flow C: Hotfix

A hotfix is a minimal, targeted fix to production. Speed matters — but correctness comes first.

1. **Rollback reference** — note the current stable tag before touching anything:
   ```bash
   git describe --tags origin/main
   ```
2. **Branch from main** (not from a feature branch):
   ```bash
   git checkout main && git pull && git checkout -b hotfix/<short-description>
   ```
3. **Minimal gate** — run lint and tests only. Skip slow build steps: `/gate -l -t`
4. **One commit** — fix only the reported issue. No refactoring, no "while I'm here".
5. **Push and merge**:
   ```bash
   git push -u origin HEAD
   gh pr create --title "hotfix: <description>" --label hotfix
   gh pr merge --squash --delete-branch
   ```
6. **Patch tag immediately**: `git tag v<patch-bump> && git push origin v<patch-bump>`
7. **Deploy** using the stack's deploy command (Step 0 table). For infra stacks: still show the plan before applying.
8. **Monitor** — watch error rates, logs, or health endpoints for 10 minutes post-deploy. If the stack has observability configured in `CLAUDE.md` (Datadog, CloudWatch, Sentry, Grafana), link the relevant dashboard.
9. **Regression test follow-up** — open a second PR the next working day to add a test that would have caught this bug. Close the loop.

---

## Rollback

If a deploy fails or health check fails, roll back immediately:

| Stack | Rollback |
|-------|---------|
| npm | `npm dist-tag add <pkg>@<prev-version> latest` |
| PyPI | users pin to prev version; yank broken release: `twine upload --skip-existing` then remove from PyPI UI |
| Vercel | `vercel rollback` or promote previous deployment in dashboard |
| Fly.io | `fly deploy --image <prev-image>` |
| Terraform | `terraform apply` with the previous plan or `terraform state` rollback |
| Pulumi | `pulumi up` from previous stack checkpoint |
| Lambda | `aws lambda update-alias --function-name X --name live --function-version <prev>` |
| Container | re-tag previous image as `latest`, redeploy |
| CDK | re-deploy with previous app version |

After rollback: tag the incident in `team-memory/MEMORY.md`, note what failed and why, and open a ticket for the root cause fix.

---

## Rules

- Never force-push main
- Never skip `/gate` — "it's a small change" is how outages happen
- Always dry-run before publish or apply — no exceptions
- Always confirm with the user before merge, tag, and deploy
- Hotfixes get a regression test — close the loop the next day
- Rollback reference is noted before every deploy (Flow B and C)

