Skip to content

Operations Runbooks

Shimon Esterkin edited this page Oct 6, 2026 · 2 revisions

Operations Runbooks

Operational procedures — most of them owner-only dashboard actions that nothing in the repository can perform or verify.

The detailed step-by-step runbooks stay in the repo, because they change alongside config that ships with the code. This page is the index and the context around them.

Runbook What it covers
AUTH_GOOGLE_OAUTH_RUNBOOK.md Google Cloud OAuth client → Supabase provider → build flag
AUTH_GITHUB_OAUTH_RUNBOOK.md GitHub OAuth App → Supabase provider → build flag
AUTH_APPLE_OAUTH_RUNBOOK.md Apple identifiers and keys, Supabase provider, token revocation
MOBILE_AUTH_SETUP.md Capacitor deep links and device verification for all providers
AUTH_EMAIL_TEMPLATE_RUNBOOK.md Publishing the magic-link template to hosted Supabase
MAINTENANCE_CHECKLIST.md ~20-minute post-deploy verification pass
NATIVE_BUILD.md Reproducible iOS/Android build flags and sync steps
MOBILE_PUSH_SETUP.md APNs/FCM setup and real-device delivery checks
PRE_BETA_OWNER_CHECKLIST.md Owner-only gates before external beta
STORE_SUBMISSION_CHECKLIST.md App Store and Play Console submission sequence
BACKLOG.md Work deliberately not started, and owner-only actions
dev-toolkit.md Navigation and anti-cruft scripts

Applying a production migration

The MCP configuration points at production. All three agent MCP files carry the production project_ref, so opening this repo in any AI agent connects that agent to the live database with write-capable tools. There is no staging project — Supabase branching needs a Pro plan.

Therefore, for every agent and every human:

  • Never run supabase migration repair, supabase db push, supabase db pull, or supabase db reset against the linked project.
  • Never apply a migration or mutate production data without the owner's explicit, in-the-moment approval. Reads and advisors are fine.
  • Schema changes land as a file in supabase/migrations/, reviewed in a PR, and are applied by the owner.

The drill, in order:

  1. Baseline — capture the current state before touching anything.
  2. Apply the migration.
  3. Verify the object actually exists, with the right shape.
  4. Reload PostgREST's schema cache:
    notify pgrst, 'reload schema';

Step 4 is not optional. A stale PostgREST cache rejects a brand-new column with PGRST204, and the error looks exactly like the migration having failed.

Verifying a function signature: pg_get_function_identity_arguments includes the parameter name, so two genuinely different functions can compare equal. Use to_regprocedure instead, or your verification will confirm something untrue. This is how an arity mismatch went unnoticed through a 26-hour production outage — see Lesson Engine.

Which migration check to believe

Two checks ask opposite questions, and only one of them is authoritative.

Migration Drift (.github/workflows/migration-drift.yml) is the source of truth. It asks: does production have every migration this repo has committed? It matches on migration name, and it exists because a migration once merged without being applied. It is deliberately not a required check — it reports on production, which no pull request can fix.

Supabase Preview (posted by the Supabase GitHub app) is not required and is currently red on main. It asks the inverse: does the repo have every production version stamp? Seven stamps legitimately differ because those migrations were applied through the MCP, which stamps application time rather than the filename's authoring time. scripts/check-migrations/compare.js names this exact divergence in its docstring.

Ignore Supabase Preview. Do not "fix" it by applying or repairing migrations.

Branch protection

main has branch protection with four required checks and enforce_admins: true. That means a direct push is rejected with GH006 — for the owner too. Everything lands via pull request.

When a merge shows BLOCKED, that usually just means the checks are still running. Poll for pending == 0 and total >= expected rather than reading BLOCKED as a failure.

Two false signals worth knowing when watching checks:

  • An empty check set reads as "all passed", because nothing failed.
  • The check-runs API omits commit statuses such as Vercel's, so a >= N guard over check runs alone can never be satisfied. Poll commits/{sha}/status for the rolled-up state.

And if CI jobs die in about 2 seconds with zero steps, log not found, and an empty failing-steps list while GitHub's own status stays green — that is billing, not code. The reason appears only in check-run annotations.

Monitoring

Sentry collects client errors on Preview and Production (EU host, de.sentry.io). Errors only, no performance tracing.

Use Sentry as a live runtime signal, and reconcile every release against the current owner checklist instead of relying on a fixed issue count; deliberate delivery-path tests and real regressions otherwise become indistinguishable.

Source maps are uploaded only when SENTRY_AUTH_TOKEN is set in Vercel. While it is unset, production stack traces are minified. Note that VITE_SENTRY_DSN takes a DSN URL and nothing else: a Sentry API token pasted there would report no events and would be inlined into the public bundle.

The Sentry release needs no configuration — vite.config.js stamps it from VERCEL_GIT_COMMIT_SHA, falling back to local git. Do not set VITE_SENTRY_RELEASE in the dashboard; being static, it would pin every future deploy to whatever commit was current when it was typed.

Design a probe so its failure says which layer broke. A monitor that reports a 404 as total = 0 is indistinguishable from a real outage — "moved" and "broken" must not produce the same signal.

SonarCloud

Analysis runs in CI, and SonarCloud Code Analysis is a required check.

  • Enable order matters: set the token first, then disable Automatic Analysis. The reverse leaves a window with no analysis at all.
  • Rule text lives in annotations on the PR-head check run. The merge-commit run has none, so looking there finds nothing.
  • Duplication is a ratio. Removing duplicated lines once made it worse (3.1% → 3.2%) because the denominator shrinks too. Sonar folds literals and compares against the whole project, so measure locally with a token-window detector — its threshold sits near 70 tokens — instead of guessing across nine-minute CI rounds.
  • Pin third-party actions to a SHA. An unpinned one is a Major security finding; actions/* are exempt.

Clone this wiki locally