Branching and Releases
This page explains how code moves from your feature branch to production: the promotion flow is feature branch → staging → main, with staging auto-deploying and prod requiring a manual human dispatch. It applies to every change in the monorepo — there is no direct path to main.
The reason for the two-hop flow is validation on a real environment: staging runs the same ordered deploy pipeline as prod, so a change is exercised against a live stack before anyone can promote it. And because the org’s GitHub plan lacks environment required-reviewers, the prod gate isn’t a branch protection rule — it’s the deliberate act of a human dispatching the prod workflow.
The rules
- Open all PRs against
staging, notmain. A feature/fix branch never PRs straight intomain(enforce-branch-flow.ymlbacks this up). - Merging to
stagingauto-deploys staging.staging-deploy.ymlis push-triggered and runs migrate →cdk deploy→ Cloudflare portal, the same ordered pipeline as prod. - Promote
staging→mainvia PR once the change looks good on staging. - Prod deploys are manual.
prod-deploy.ymlisworkflow_dispatchfrommain. Merging tomaindoes not deploy anything. - CI gates PRs into both branches identically —
build / typecheck / test,gates, andpostgres integration(see Verify and CI).
A typical ship
git checkout staging && git pull
git checkout -b evan/ops-123-my-fix
# ...make changes...
bun run verify # always, before pushing
git push -u origin evan/ops-123-my-fix
gh pr create --base staging --title "OPS-123: my fix"
# after merge: staging auto-deploys; validate there, then:
gh pr create --base main --head staging --title "Promote staging to main"
# after merge: a human dispatches prod-deploy.yml from mainTwo things to respect in this flow. Migrations run before code deploys in both pipelines — and they are forward-only: never edit a migration’s SQL after it has been applied anywhere (prod has suffered checksum-drift incidents from exactly this; CI’s migration-immutability gate now enforces it). And deploying code ahead of migrations is an outage pattern — a past prod 500 came from shipped code referencing a table whose migration hadn’t run. Trust the pipeline ordering; don’t hand-deploy around it.
Docs-only changes (README.md, CLAUDE.md, docs/**, etc.) are in CI’s paths-ignore, so a docs PR shows no CI checks — that’s expected, not broken.
Related
- Verify and CI — the gates every PR must pass
- Development workflow — local iteration before you ever push
- /architecture — what actually gets deployed (Lambdas, Cloudflare portal, RDS)