Skip to content

Deployment / Release Runbook

1. Branching Model

Two long-lived branches map directly to environments: development and production. There is no separate staging branch — development serves as the shared pre-production environment. All feature work merges to development first via PR (gated by pr-checks.yml — see §4), then development is merged/pushed to production to release.

Per notion-pr-sync.yml: every non-draft PR must reference a linked Notion task (either a notion.so/notion.site URL or an explicit Notion-Page-ID: <uuid> line in the PR body) — a PR without one fails this check. Link your Notion task before marking a PR ready for review.

2. What a Push to production or development Actually Does

Both branches trigger the same ci-cd.yml workflow, mapped to the matching Cloudflare environment. In order:

  1. Install dependencies (pnpm install --frozen-lockfile).
  2. Build: pnpm run pages:build, with NEXT_PUBLIC_APP_URL, GRAPHQL_ENDPOINT, GRAPHQL_TOKEN, QUEUE_API injected from the matching GitHub Environment’s variables.
  3. Safety check: if the branch is production, the build fails outright if NEXT_PUBLIC_APP_URL or GRAPHQL_ENDPOINT contain “development”/“dev” — this catches a misconfigured GitHub Environment before it ships.
  4. D1 migrations applied: wrangler d1 migrations apply <db> --remote --env <env>this runs automatically on every push, before the app deploys. Any migration merged into the branch will be applied to that environment’s live database immediately on push. There is no separate manual migration-approval gate.
  5. Pages deploy: wrangler pages deploy .vercel/output/static --project-name=yareta-platform --branch=<branch>.
  6. Queue worker deploy: scripts/queue-deploy.sh, a separate step that can succeed or fail independently of step 5.

Consequence to plan releases around: because migrations apply automatically on push, a schema migration and the application code that depends on it land in the same push — you cannot “merge the migration now, deploy the code later” as two controlled steps through this pipeline. If a migration needs to run ahead of its dependent code for safety (e.g. backfilling a column before code that reads it goes live), split it into two separate PRs/pushes deliberately, with the migration-only push going out first.

3. Manual Release Steps (Pre-Push Checklist)

Before pushing to production:

  1. Confirm the corresponding development deploy has been running cleanly for a reasonable soak period — there’s no automated staging validation beyond pr-checks.yml’s lint/format/typecheck gate, so development running well in practice is the closest thing to a pre-prod signal.
  2. If the release includes a prompt template change in code, remember this alone does not update the live system_prompts DB row (see the Admin Guide) — plan a follow-up forceUpdatePrompts call or direct SQL update as part of the same release, not a separate afterthought.
  3. If the release adds a new large server-only dependency, confirm it’s been added to serverComponentsExternalPackages in next.config.mjs (see the NFR doc) — otherwise the build may silently balloon past Cloudflare’s 25MiB Pages Function limit and fail deploy.
  4. If the release adds a new migration, check the current highest migration number in src/db/migrations/ immediately before generating it, to avoid adding to the existing numbering-collision list.
  5. Confirm any new environment variable the release depends on has been set as a Cloudflare dashboard Variable/Secret on both the Pages project and, if the queue worker needs it too, the Worker — CI only injects the four vars listed in step 2 of §2 above; everything else must already exist on the dashboard.

4. PR Checks (What Blocks a Merge)

pr-checks.yml runs on every PR into production or development: pnpm format:check, pnpm lint, pnpm typecheck. Note: this does not run the test suite (pnpm test) — passing PR checks does not mean the 26 existing Vitest tests passed. Run pnpm test:run locally before merging anything touching a tested area (integrations, OAuth, the investor-team-summary pipeline), since CI won’t catch a regression there.

notion-pr-sync.yml additionally requires a linked Notion task on any non-draft PR (§1).

5. Rollback

There is no one-command rollback in this pipeline. Recovery paths, by layer:

  • App code: revert the merge commit on the affected branch and push — this re-runs the full pipeline (including migrations, which is safe for a pure code revert but see below for a migration revert).
  • A bad migration: D1 migrations are forward-only through wrangler d1 migrations apply — there’s no migrations rollback. Recovery means either a hand-written corrective migration, or a D1 time-travel restore to just before the bad migration ran (see the Disaster Recovery doc). Do not attempt to delete/renumber the bad migration file after it’s been applied to a live database — the migrations directory must stay consistent with what’s actually been run.
  • Queue worker only: if only the queue worker deploy needs to roll back (app deploy was fine), re-run scripts/queue-deploy.sh against a prior commit’s src/queue/ state, or redeploy from the Cloudflare dashboard’s Worker version history if available.
  • A bad prompt change: use the Admin → Prompts version history to restore a previous promptVersion — this is faster than a code revert + redeploy for prompt-only issues.

6. Post-Deploy Verification

Since there’s no automated smoke test or deploy notification (see the Observability doc), verify manually:

  1. GET /api returns the expected liveness text.
  2. Spot-check one representative GraphQL query and one representative mutation against the deployed environment.
  3. Check the GitHub Actions run for both the Pages deploy step and the queue worker deploy step — confirm both succeeded, not just one.
  4. If the release touched a queue/job, trigger one representative job and confirm it completes (check /admin/jobs).

Related documents: Infrastructure & Deployment · Operations Runbook · Database Documentation