Install
openclaw skills install @dennisrongo/ship-itPre-launch operational-readiness checklist for a feature, release, or branch. Walks a fixed 10-category gate (logging, error handling, telemetry, feature flags, migrations, rollback, secrets, local-first storage, auth, update strategy), produces a structured report with PASS / GAP / N/A per item, every PASS backed by file:line evidence and every GAP cited as no evidence found at <path>. Final verdict groups findings as **Blocking** / **Should-fix** / **N/A with reason** / **Passing**. Use this skill whenever the user says "is this ready to ship?", "ship-it check", "/ship-it", "production checklist", "pre-launch checklist", "production readiness", "release readiness", "launch checklist", or asks whether a release is operationally safe — even if they don't explicitly say "ship-it skill". Use [code-review](../code-review/SKILL.md) for diff-level code quality (DRY, dead code, tests). Use ship-it for cross-cutting operational readiness. Never edits code unprompted — recommendation first, ask, then fix.
openclaw skills install @dennisrongo/ship-itThe operational gate I run before calling something done. Code review tells me the diff is clean; ship-it tells me the thing won't page me at 3am.
Do not auto-trigger when the user is asking about diff-level code quality, DRY, dead code, or test coverage — that's code-review's job. If the trigger is ambiguous (e.g. bare "ready to ship?" with a working tree full of uncommitted edits), ask once whether they want the diff review or the operational checklist.
file:line. "I checked, it looks fine" is not a PASS — that's a GAP labelled couldn't verify.not checked — never PASS. A result you didn't observe doesn't exist.migrations/ in the diff (git diff --stat) → migrations N/A") — never a guess from the project type. Don't fabricate gaps for categories that don't apply.code-review's work. If the user asks about DRY / dead code / test coverage, defer.Don't proceed until scope is named. Ask the user with AskUserQuestion if it isn't obvious:
git diff against the base; scope the audit to changed files + their direct call graph.git diff <prev-tag>..HEAD.Echo the scope back in one line before starting Phase 2 ("Auditing branch feat/billing-v2 against base main — 14 files changed.").
For each category: state the criterion in one line, search the codebase for evidence, mark PASS / GAP / N/A. Use Grep and Read aggressively; spawn an Explore sub-agent for any category where the search would take more than 3 queries.
Record each probe as you run it — the report cites them. A probe is a specific Grep pattern + path, a Read of a named file, or a command with its observed output. A sub-agent's result counts as a probe only if the sub-agent names its own probes and citations; "the agent said it's fine" is not evidence. Where a check depends on the user (dashboards, runbooks, rollout plans), record their answer as the probe ("user confirmed: alert added to Grafana billing board") — an unanswered question stays GAP.
What good looks like: Structured logs (JSON or key=value), correlation / request IDs propagated, levels used correctly (error ≠ info), no secrets / tokens / PII in log output, errors logged with stack + context, not just the message.
How to check: Grep for the logger import and inspect call sites in changed files. Confirm a request-ID middleware exists upstream and that the new code paths inherit it. Grep for known secret-shaped variables being passed to log calls.
What good looks like: Each failure mode named (network, validation, auth, downstream 5xx), no catch {} / except: pass / _ = ... silent swallows, user-facing errors mapped to safe messages, retry / backoff on external-boundary calls (HTTP, DB, queues).
How to check: Grep for empty catches, bare except, error-swallowing patterns. Trace each new external call to see how failures propagate. Confirm idempotency where retries exist.
What good looks like: New code paths emit metrics (counts, latencies, error rates), traces propagate (OpenTelemetry / equivalent context passed through), dashboards / alerts updated to include the new signal.
How to check: Grep for the metrics client in changed files. Confirm at least one counter and one latency histogram per significant code path. Ask the user whether the dashboards / alerts were updated — that's usually out-of-tree.
What good looks like: New behavior gated behind a flag with a clear owner, documented default state for prod (almost always off), and a written ramp / cleanup plan (when does the flag get removed?).
How to check: Grep for the flag client; confirm new branches are gated. Read the flag definition file / dashboard config for owner + default. Ask the user for the cleanup plan if it isn't in the PR description.
What good looks like: Forward-compatible — the deployed code tolerates BOTH old and new schema for at least one release. Backfill plan named if columns are added. Runtime estimated against prod-size data (not just dev).
How to check: Read the migration files. Look for ALTER TABLE against large tables — flag any that take exclusive locks. Confirm the matching code reads column ?? fallback rather than assuming the new shape exists. Ask about backfill strategy.
What good looks like: The change can be reverted by redeploying the previous artifact. Flag-on / flag-off is the rollback path where possible. Migrations are split from code deploys so revert never requires a DB rollback. There's a one-liner in the runbook.
How to check: Look for the runbook entry. Confirm migrations were merged in a separate commit / PR from the feature code (the "expand / migrate / contract" pattern). If a migration is destructive (DROP, NOT NULL added) call it out as blocking unless a rollback plan exists.
What good looks like: No secrets in code, config files, or logs. Rotation path documented. New env vars added to the secret store (Vault / AWS SM / Doppler / etc.), not just .env.example.
How to check: Grep changed files for high-entropy strings, common key shapes (AKIA…, sk_live_, xoxb-), and .env* diffs. Confirm any new env var also exists in the secret-store config (Terraform / Helm values / etc.).
What good looks like: Offline behavior defined (queue + replay, or fail-fast), conflict resolution rule named for sync (last-writer-wins, CRDT, merge UI), schema version in the local DB, migration handles users upgrading from an older client.
How to check: Find the local store (IndexedDB / SQLite / Realm / AsyncStorage). Read its schema-version handling and migration code. If the PR changes the local schema, confirm the upgrade path exists.
What good looks like: New endpoints / screens have authorization checks (not just authentication), tenant / org scoping enforced server-side (never trust client-sent IDs), no IDOR — GET /orders/:id verifies the order belongs to the caller — session / token handling unchanged or explicitly reviewed.
How to check: For each new route, read the handler top-to-bottom. Confirm: caller identity comes from the session, the resource is loaded scoped to that identity, no where: { id: req.params.id } without a tenant predicate. Missing authz on a new route is blocking.
What good looks like: How users get the new version is named and tested.
How to check: Identify the artifact type and ask the user how the rollout happens. For desktop, read the updater config. For mobile + breaking changes, confirm there's a min-version-supported check on the backend.
Group findings into four buckets. Order matters — blocking first.
# Ship-It Report — <scope>
## Blocking
- **<category>:** <one-line problem> — `<file:line>` or `no evidence found at <path>`
## Should-fix (non-blocking)
- **<category>:** <one-line problem> — `<file:line>`
## N/A
- **<category>:** <one-line reason grounded in the scope> (probe: `<what established it>`)
## Passing
- **<category>:** <one-line evidence> — `<file:line>` (probe: `<grep pattern / file read / command>`)
Same PASS, written badly and well:
**Logging:** structured logging in place — src/billing/ — no line, no probe; unverifiable, could have been written without looking.**Logging:** new invoice paths log via structured logger with request ID — src/billing/invoice.ts:31 (probe: grep -n "logger\." src/billing/ → 6 call sites, all pass ctx.reqId) — anyone can rerun the probe and land on the same line.Blocking = any one of:
Everything else is should-fix.
After the report, ask: "Want me to draft fixes for the blocking items, or stop at the report?" Wait for explicit go-ahead before editing.
User: "is this ready to ship?" (currently on branch feat/billing-v2 with an open PR)
Claude:
feat/billing-v2 against main — 14 files changed."file:line per finding and the probe that produced it.POST /api/v2/invoices — no tenant predicate in routes/invoices.ts:24-51, read top-to-bottom; no rollback for the currency_code NOT NULL migration), 4 should-fix, 1 N/A (local-first storage — "no client-side store in scope: grep -rl 'localStorage\|indexedDB' src/billing/ → 0 hits, server-only module"), 3 passing with probes named.User: "ship-it check on the billing module"
Claude:
src/billing/** (confirms the path exists first).User: "review my diff"
Claude: "That's code-review's territory — diff-level quality, DRY, tests. ship-it is for the operational gate (logging, flags, migrations, rollback, auth, etc.). Want me to run code-review instead, or did you mean the operational checklist?"
file:line citation. "Looks fine" is a GAP labelled couldn't verify.not checked.git diff --stat) → migrations N/A".code-review.code-review (run code-review first for diff quality, then ship-it for operational readiness) and handoff (capture the report as the next-session pointer if shipping is deferred).