Run Quality Gates

Five sequential gates that catch the failures that would otherwise blow up in a Vercel build or in production. Fail-fast: the first failing gate stops the run.

Default stance

Run the full suite before any PR. Use --quick only for tight local iteration where you'll re-run before opening the PR. The build gate is the most expensive (30-60s) and the most likely to catch what the other four miss — TypeScript errors, import path issues, Next.js route conflicts. Skipping it locally is fine; skipping it before a PR is how broken builds reach staging.

The pre-commit hook already enforces a slightly different superset (db:check, env:check, lint, knip, test). The build gate isn't in pre-commit because it's slow — it lives here.

Use this skill when

Opening a PR, before promotion, after schema changes to lib/schema.ts, after touching lib/env/*, or after a refactor that crosses multiple files.

Run

./scripts/gates.sh           # full suite
./scripts/gates.sh --quick   # skips next build for fast local iteration

Gates in order

  1. bun run db:check — Drizzle schema ↔ migration file sync. Catches schema drift before it reaches production.
  2. bun run env:check — No raw process.env.X access outside lib/env/*. All env reads must go through typed accessors.
  3. bun run lint — ESLint.
  4. bun run test — Vitest unit tests in tests/unit/.
  5. bun run build — Full Next.js production build. Catches type errors, route conflicts, and import issues that lint misses.

When a gate fails

  • db:check — Schema drift. Run bunx drizzle-kit generate to sync, commit the new migration, retry. See db-health.
  • env:check — A raw process.env.X somewhere. Move it to the appropriate lib/env/*.ts module with a typed accessor. The check exists because raw access bypasses the per-environment validation that catches "we forgot to set this in Vercel."
  • lint — Fix or bun run lint --fix for auto-fixable cases.
  • test — Read the failure. bun run test:watch for interactive mode.
  • build — Almost always a type error. Check the build output for the file and line; the error message is usually accurate.

Hard rules

  • Don't bypass gates to "ship faster." Every gate that's failed in this repo's history was catching something real.
  • Don't disable a gate to make it pass. If a gate is wrong, fix the gate; don't skip it.
  • Don't commit with a failing pre-commit hook via --no-verify unless explicitly told to. Hook failures are signal.

Where things live

FilePurpose
scripts/gates.shEntrypoint — runs all 5 in order
scripts/release-gates.shStricter superset for release readiness
scripts/check-env-fallbacks.mjsImplementation of env:check
scripts/ban-db-push.mjsRefuses to run if db:push is in scripts
lib/env/*.tsTyped env accessors (where all process.env access lives)
tests/unit/Where new fast tests go

Auxiliary content