Repository navigation
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 |
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, orsupabase db resetagainst 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:
- Baseline — capture the current state before touching anything.
- Apply the migration.
- Verify the object actually exists, with the right shape.
-
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_argumentsincludes the parameter name, so two genuinely different functions can compare equal. Useto_regprocedureinstead, or your verification will confirm something untrue. This is how an arity mismatch went unnoticed through a 26-hour production outage — see Lesson Engine.
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.
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-runsAPI omits commit statuses such as Vercel's, so a>= Nguard over check runs alone can never be satisfied. Pollcommits/{sha}/statusfor 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.
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.
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.
Design
Working on it