Four fixes from `/codex` review of the merged diff:
1. HIGH — wire migration v24 into the `gbrain apply-migrations`
upgrade path. Without an orchestrator entry, `gbrain upgrade`'s
post-upgrade step runs `apply-migrations --yes`, which walks the
registry in `src/commands/migrations/index.ts`. The registry
stopped at v0_18_0, so v24 never fired on upgrade (connectEngine
and doctor do not call initSchema). New `v0_18_1.ts` orchestrator
mirrors v0.18.0's Phase A: shells out to `gbrain init
--migrate-only`, which triggers initSchema → runMigrations → v24
applies. Registered in the migrations array.
2. HIGH — fail loudly when v24 runs under a non-BYPASSRLS role
instead of RAISE WARNING-then-silently-bumping-version. The
runner at migrate.ts:773 unconditionally calls
`setConfig('version', String(m.version))` when a migration
completes without throwing, so a WARNING-and-continue path would
permanently lock the backfill out: schema_version=24 on the next
run means `m.version > current` is false and v24 is skipped
forever, even after the role gets BYPASSRLS. Changed `RAISE
WARNING` → `RAISE EXCEPTION` so the transaction aborts,
schema_version stays at 23, and a subsequent initSchema retries
cleanly after the role is fixed. Test asserts the SQL uses
EXCEPTION and does not use WARNING.
3. MEDIUM — escape double-quote characters in the remediation SQL
output. doctor.ts was building `ALTER TABLE "public"."${n}"`
with `n` un-escaped, so a pathological table name containing a
literal `"` would break out of the quoted identifier and produce
invalid copy-paste SQL. Double the `"` before interpolating,
matching Postgres quoted-identifier escaping rules. Extremely
rare in practice, cheap to get right.
4. LOW — CHANGELOG cleanup: corrected the upgrade-behavior claim
(v24 runs via `apply-migrations --yes` through the new
orchestrator, not during `gbrain doctor`) and split the "tables
with RLS" row into two metrics (21 base-schema tables + 2
migration-only budget_* tables = 23 managed total, all covered).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>