Trigger: shipping any change to bizos.jonnyai.co.uk. Owner: Jonny Allum. Policy anchors: POL-06, POL-08.
Deployment is currently SSH + fast-forward pull/build/restart on the GCP VM (PM2 + Caddy). The manual GitHub Deploy workflow stays inert until VM secrets are configured — when that lands, this SOP is updated in the same PR.
Pre-deploy (local)
- All four gates green:
pnpm turbo run typecheck test buildandcd apps/shell && pnpm exec tsx audit.mts→ PASS. (Sandboxed sessions:NODE_EXTRA_CA_CERTS=/root/.ccr/ca-bundle.crtfor the build.) - Sensitive-class change (auth, RLS, billing, public writes, AI actions, cron)? Cooling-off self-review of the diff first (POL-06 §2).
- Migrations, if any, are applied to Supabase before the code that needs them (SOP-04) — the app must tolerate the window between.
- Everything committed and pushed; the VM only ever pulls, never diverges.
Deploy (VM)
- SSH in;
git pull --ff-only(if it won't fast-forward, stop — the VM has drifted; fix the branch state first, never merge on the box). pnpm install(if lockfile changed) → build →pm2 restartthe app (and JAIOS only if it changed).- Note the deployed commit hash in the release note (the README "Current state" table records the pattern, e.g.
c1d10f5).
Post-deploy smoke test (every deploy, ~5 minutes)
- ☐Public probes:
/,/pricing, a/shop/[slug], a/book/[slug]return 200 - ☐Login works; dashboard renders for a demo tenant
- ☐The area you changed, exercised authenticated in the browser
- ☐One cron endpoint test-fired with
BIZOS_CRON_SECRET(if cron touched) - ☐Stripe webhook delivery green in the Stripe dashboard (if billing touched)
- ☐
pm2 logsclean of new errors for a few minutes
Rollback
Fix forward with the smallest diff where possible (POL-06 emergency class). If the deploy is unusable: git checkout <last-good-hash> on the VM, build, restart — then repair on a branch locally. Migrations are not rolled back; they are idempotent and forward-only, so the rolled-back app must tolerate the newer schema (this is why migrations are additive — SOP-04).
Release hygiene
- Deploy when you can watch it for 30 minutes — not last thing at night.
- One deploy = one intention. Batches of unrelated changes make smoke failures undiagnosable.
- Update
BUILD_PLAN.mdRESUME HERE block after any session that changes production state.