---
name: ship-check
description: Pre-deploy checklist tuned for Next.js + Prisma/Drizzle + Railway. Runs typecheck, build, security scan, migration safety review, and env-var diff. Outputs a GO/WAIT/NO verdict with the specific blockers. Use before pushing main or hitting deploy.
trigger: /ship-check
---

# /ship-check

Most deploy disasters are not exotic — they're the same five mistakes: typecheck didn't run, env var missing in prod, migration locks a big table, secret committed by accident, debug `console.log` left in. Ship-check catches all five before they reach Railway.

The skill is the pilot's pre-flight checklist for your stack. It does not deploy. It does not push. It tells you, with specific evidence, whether you're safe to ship.

## Usage

`/ship-check` (runs against current working directory)

Or:

`/ship-check /Users/claude/Documents/path/to/project`

## What it's for

You ship to Railway often. Most of your projects (EAA, Fathom Fury, eventually GFM) are Next.js + Postgres on Railway. Each has live users (or will) and each has a moment where you push to main and pray. Ship-check replaces the prayer with a structured pass.

It catches:
- Type errors (TS)
- Build failures
- Committed secrets (.env in git, hardcoded API keys/tokens)
- Risky migrations (NOT NULL on big tables without defaults, dropping columns with data, missing indexes on FK)
- New `process.env.X` references missing from `.env.example`
- Stranded debug code (`console.log`, `console.debug`, `// TODO: remove`)
- Uncommitted changes that probably should ship together

It does NOT replace:
- Code review
- Manual smoke testing in staging
- Reading the diff yourself

## What You Must Do When Invoked

### Step 1 — Identify the project + stack

`pwd` to find project root. Inspect:
- `package.json` — confirm Next.js, identify ORM (Prisma vs Drizzle vs raw SQL)
- `railway.json` or `nixpacks.toml` — Railway config presence
- `.env.example` — declared env vars
- Git remote — confirm we're checking the right branch

If this is not a Railway-Next.js project, run a more generic version (typecheck + build + secrets scan only) and note the calibration.

### Step 2 — Run checks in parallel

Where possible, run reads/checks concurrently. Block-by-block:

#### Block A — Typecheck + build
```
npx tsc --noEmit
```
Capture exit code and any errors. If the project has a custom typecheck script in `package.json`, use that instead.

For build: optional but recommended. `npm run build` (or `pnpm build`). Skip if it'd take longer than ~3 minutes — note as "build skipped, run manually".

#### Block B — Secrets + .env safety
- `git ls-files | grep -E '\.env$|\.env\.local$|\.env\.production$'` — should return nothing committed.
- Grep changed files in current branch for hardcoded secrets:
  - `(api[_-]?key|secret|token|password|bearer)\s*[:=]\s*['"][^'"]{16,}['"]` (case-insensitive)
  - Specifically watch for keys starting with `sk-`, `pk-`, `re_`, `whsec_`, `xoxb-`, etc.
- Compare `process.env.X` references in changed files against `.env.example`. Anything new not in example → flag.

#### Block C — Migration review (Prisma or Drizzle)
Identify any new migration since main:
- Prisma: `prisma/migrations/` — files newer than main
- Drizzle: `drizzle/` — same

For each new migration, audit for:
- `NOT NULL` columns added without defaults to non-empty tables
- `DROP COLUMN` on columns with production data
- New foreign keys without indexes
- Long-running operations on large tables (re-indexes, type changes)

If you can't tell whether a table is large/has data, flag it as needs-human-review rather than declaring it safe.

#### Block D — Stranded debug code
Grep changed-since-main files for:
- `console\.(log|debug|info|warn)` — note the file:line for each
- `// TODO:?\s*remove`, `// FIXME:?\s*before\s*deploy`, `XXX`, `// debug`
- `debugger;`
- `.only(` and `.skip(` in test files

Light flag for `console.warn`/`console.error` (often legitimate). Hard flag for `console.log` and `debugger`.

#### Block E — Git hygiene
- `git status --short` — uncommitted changes
- `git log --oneline main..HEAD` — what's about to ship
- Confirm branch is up to date with remote (`git fetch` then `git status -uno`)

### Step 3 — Output the verdict

Total output target: under 400 words. Front-load the verdict.

```markdown
# Ship Check — <project>

**Verdict:** 🟢 GO | 🟡 WAIT | 🔴 NO  
**Branch:** <branch> · **Commits ahead of main:** <count>

## 🔴 Blocking
<Things that will break prod or expose secrets. Empty if none.
Each item: file:line + 1-line description + suggested fix.>

## 🟡 Yellow flags
<Things that work but are risky. Empty if none.
Migration ambiguity, console.warn that might be intentional, etc.>

## 🟢 Clean
- Typecheck: ✓
- Build: ✓ / skipped (ran in <Xs>)
- Secrets scan: ✓
- Migrations: ✓ / N/A
- Env vars: ✓
- Debug code: ✓
- Git: ✓

## Next action
<Single concrete instruction:
"Fix <file:line>, then re-run /ship-check"
or
"Safe to push. Run: git push origin <branch>"
or
"Migration in <file> needs Jake's eyes — review locking behavior on InstagramAccount before deploying.">
```

### Step 4 — Verdict rules

- **🔴 NO**: any committed secret, any failing typecheck, any migration that will lock a known-large table, any hardcoded API key in changed files. ZERO tolerance.
- **🟡 WAIT**: stranded `console.log`, ambiguous migration, env var missing from example, build skipped on a deploy you're nervous about. User can override.
- **🟢 GO**: all checks pass, no blockers, no yellow flags. The push button is yours.

When uncertain, lean WAIT, not GO. False positives are cheap; false negatives ship bugs.

### Step 5 — Stop

Do not push. Do not deploy. Do not modify any files. Ship-check is read-only by design.

If the user asks "fix it for me" after the report, that's a separate task — switch out of ship-check mode and into normal build mode.

## Calibration for Jake's stack

- **Elite AI Advantage:** Next.js 16 + Prisma + Postgres on Railway. Watch InstagramAccount, Post, QueuedPost migrations carefully — these tables have admin-owned data that must not be reset by deploy.
- **Fathom Fury New System:** Next.js + Drizzle + Postgres on Railway. CIGARLINK-related migrations are sensitive (compliance data).
- **Garage Floor Marketing:** small site, fewer moving parts, looser checks acceptable.

For projects you don't know, default to the strict ruleset and ask Jake before relaxing anything.

## What to avoid

- **No false confidence.** "Probably fine" is not a verdict. Pick GO/WAIT/NO and commit.
- **No skipping the migration check** even if the diff is small. Lock-the-table mistakes are usually one line.
- **No fixing things mid-check.** Ship-check reports. Fixes are a separate session, with its own audit trail.
- **No "everything looks good"** without listing what was checked. The Clean section is the receipt.
- **No going past the verdict.** End with "Next action" and stop.

## Example output (abbreviated)

```markdown
# Ship Check — Elite AI Advantage

**Verdict:** 🟡 WAIT  
**Branch:** main · **Commits ahead of main:** 4

## 🟡 Yellow flags
- `src/app/admin/instagram-pipeline/InstagramPipelineClient.tsx:347` — `console.log("force drain result", body)` left in production code path. Not a security issue but pollutes browser console for admins.
- New env var `BLOG_RESEARCH_ENDPOINT` referenced in `src/lib/skills/research-helper.ts:12` but not declared in `.env.example`. Will work in prod if Railway has it set, but undocumented.

## 🟢 Clean
- Typecheck: ✓ (0 errors)
- Build: skipped (run manually before push)
- Secrets scan: ✓ (no .env files tracked, no hardcoded keys in changed files)
- Migrations: ✓ (no new migrations)
- Debug code: 1 console.log flagged above
- Git: ✓ (clean, up to date with origin/main)

## Next action
Remove the console.log on line 347, add `BLOG_RESEARCH_ENDPOINT=` to `.env.example`, then re-run /ship-check.
```

That's the shape. Verdict-first, evidence-grounded, concrete next action.
