Garry flagged: migration v24 fires `ALTER TABLE budget_ledger ENABLE
ROW LEVEL SECURITY` unconditionally. budget_ledger and
budget_reservations are migration-only (v12) — not in schema.sql,
not re-created on every initSchema. In the normal flow v12 runs
before v24 so they exist, but two edge cases break that assumption:
1. An operator manually dropped them (budget data is regenerable
from resolver call logs, so `DROP TABLE` is a reasonable
cleanup move).
2. A brain was somehow running an old gbrain that lacked v12, and
is only catching up now.
Bare ALTER hits 42P01 (relation does not exist), aborts the
transaction, and leaves schema_version at 23. On next initSchema,
v24 retries and hits the same error — stuck in a loop.
Fix: wrap each of the two budget ALTERs in
IF EXISTS (SELECT 1 FROM information_schema.tables
WHERE table_schema = 'public'
AND table_name = '<tbl>') THEN ... END IF;
The other 8 tables are not guarded. schema.sql creates them
idempotently on every initSchema run before migrations fire, so
they are guaranteed to exist by the time v24 runs. Adding guards
there would be unnecessary and make the SQL noisier.
Also simplified the DECLARE/BEGIN structure: moved the
non-BYPASSRLS early-exit to the top so the happy path reads
cleanly without the outer IF.
Tests:
- test/migrate.test.ts: new assertion that both budget_* ALTERs
are wrapped in information_schema.tables IF EXISTS blocks;
BYPASSRLS gate assertion relaxed to match either phrasing.
- Manual e2e: fresh Postgres init (v0→v24), then DROP TABLE
budget_ledger + budget_reservations, reset version=23, re-run
init. v24 applied cleanly, version advanced to 24, budget_*
stayed dropped. Without the guard this would have errored out.