fix(db): our tables are not in orangecat's schema — tell every client - #138
Merged
Conversation
/api/health has returned 503 because `public.consultations` does not exist. It
never did. Botsmann shares one self-hosted Supabase database with orangecat,
which owns `public` and its 128 tables; ours were nowhere, because all eleven
migrations were unapplied.
The schema half is fixed in the deploy pipeline (fleetcrown#398): botsmann now
declares `supabase:botsmann` and its migrations apply into a schema of its own,
exactly as printcraft's do. That has run — all 11 tags are recorded and the 12
tables exist.
This is the other half. A Supabase client that is not told the schema queries
`public`, so it would still get PGRST205. Measured against production just now:
GET /rest/v1/consultations -> 404 PGRST205
GET /rest/v1/consultations Accept-Profile: botsmann -> 200 []
`db: { schema }` is what sends that header, and it was missing from all four
client factories — anon, service-role, browser and route-handler.
The schema name is one constant in lib/constants.ts, not four literals. The
second copy is how the first goes stale, and this repo has just spent a day
proving what duplicated configuration costs.
Two things this also removes:
* `scripts/run-migration.ts` and `scripts/migrate-via-api.ts` applied SQL
through the service-role client with no schema set. They are from the era
when migrations were run by hand; now they would create our tables inside
orangecat's `public`. Schema is the pipeline's job, and a second way to do
it is a second way to do it wrong. Nothing references them.
* The bare `SupabaseClient` type hardcodes `'public'`, so annotating with it
silently asserts we query orangecat's tables — and rejects the client we
actually build. The type is now derived from the factory, which keeps the
two in step and survives supabase-js reordering its generics.
New test pins it per file, by count: every `createClient` call in a client file
must set the schema, from the shared constant, with comments stripped first so
the guard cannot be fooled by prose. Adding a fifth unscoped factory fails the
test instead of production. Mutation-checked: unscoping the browser client
fails it.
verify green: 237 tests, lint, typecheck, build.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HVwg8DKHQktxJuHeLM3xpG
github-actions Bot
pushed a commit
that referenced
this pull request
Aug 28, 2026
…143) The schema fix landed in #138, but the repo still told you to operate a Supabase we do not have. docs/SUPABASE_SETUP.md opened with "Completed Setup" for a hosted project jkjmhtirxwhljpkcfxqe.supabase.co and said to apply migrations by pasting SQL into a supabase.com dashboard. Two unreferenced scripts printed the same instruction, one handing out a psql string against pooler.supabase.com, and the CLI's .temp cache with the old project ref was committed — twice, once nested under supabase/migrations/. That is not stale clutter, it is the outage. A wrong pointer to a real-looking place is worse than no pointer: no pointer makes someone ask, a plausible one makes them assume it was handled. Nobody pasted anything, the eleven migrations were never applied, and /api/health served PGRST205 for months while every deploy went green. So the doc now describes what is actually true — self-hosted on bitbaum at supabase.orangecat.ch, our tables in the `botsmann` schema because one database backs several apps and `public` is orangecat's, and migrations applied automatically on deploy by fleetcrown's apply-schema.sh against the botsmann._deploy_schema_history ledger. Nothing is pasted anywhere. 280 lines became 100. Fixing today's copies only clears today's copies, so check:selfhost joins verify: it fails on supabase.com/dashboard, app.supabase.com, pooler URLs and real project refs, allows supabase.com/docs because the product documentation is still correct for us, and exempts docs/archive/ so history can stay honest. Proven to fail before shipping — it catches an injected dashboard link and a project ref, and leaves the your-project.supabase.co placeholder alone. Claude-Session: https://claude.ai/code/session_018waGt1ieA9TjpscqrbrnGb Co-authored-by: Mao Nakamoto <41178744+maonakamoto@users.noreply.github.com> Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The 503
/api/healthhas returned 503 becausepublic.consultationsdoes not exist. It never did.Botsmann shares one self-hosted Supabase database with orangecat, which owns
publicand its 128 tables. Ours were nowhere — all eleven migrations were unapplied, and no deploy ever noticed.Half of the fix already shipped
fleetcrown#398 taught the deploy pipeline that a second app on the shared database needs its own schema. Botsmann now declares
supabase:botsmann, and its migrations apply there — exactly as printcraft's do. That has already run: all 11 tags are recorded inbotsmann._deploy_schema_historyand the 12 tables exist.This is the other half
A Supabase client that is not told the schema queries
public, so it would still getPGRST205. Measured against production:db: { schema }is what sends that header — and it was missing from all four client factories: anon, service-role, browser, and route-handler.The schema name is one constant in
lib/constants.ts, not four literals. The second copy is how the first goes stale, and this repo has just spent a day proving what duplicated configuration costs.Also removed
scripts/run-migration.tsandscripts/migrate-via-api.ts. They applied SQL through the service-role client with no schema set — from the era when migrations were run by hand. Now they would create our tables inside orangecat'spublic. Schema is the pipeline's job; a second way to do it is a second way to do it wrong. Nothing references them.The bare
SupabaseClienttype. It hardcodes'public', so annotating with it silently asserts we query orangecat's tables — and rejects the client we actually build. The type is now derived from the factory, which keeps the two in step and survives supabase-js reordering its generics.Test
Pins it per file, by count: every
createClientcall in a client file must set the schema, from the shared constant, with comments stripped first so the guard cannot be fooled by prose. Adding a fifth unscoped factory fails the test instead of production.Mutation-checked — unscoping the browser client fails it.
verifygreen: 237 tests, lint, typecheck, build.🤖 Generated with Claude Code
https://claude.ai/code/session_01HVwg8DKHQktxJuHeLM3xpG