Symptoms
Upgrading a pre-v0.29 PGLite brain to v0.30.x wedges with column "effective_date" does not exist before any migration runs. Same class as #561, #609, #695, #699 — applyForwardReferenceBootstrap is missing a probe for v41's pages.effective_date, so PGLITE_SCHEMA_SQL's replay aborts on the index that forward-references the column.
=== Applying migration v0.11.0: GBrain Minions — durable background agents ===
Pre-v0.21 brain detected, applying forward-reference bootstrap
column "effective_date" does not exist
Phase A (schema) failed: Command failed: gbrain init --migrate-only. Aborting; re-run after fixing.
Migration v0.11.0 reported status=failed.
Once aborted, runMigrations() never gets to v41 (which adds effective_date via ADD COLUMN IF NOT EXISTS), so every subsequent gbrain invocation hits the same failure. gbrain doctor confirms:
[FAIL] minions_migration: MINIONS HALF-INSTALLED (partial migration: 0.11.0). Run: gbrain apply-migrations --yes
[WARN] schema_version: Version 24, latest is 44.
Affected versions
PGLite brains predating v0.29.1 (schema_version < 41) on a v0.29.x or v0.30.x binary.
The Postgres counterpart of this bug class is #561 (v27 columns); this one bites the PGLite path for v41 columns.
Root cause
src/core/pglite-engine.ts:applyForwardReferenceBootstrap runs before getPGLiteSchema() so old brains can satisfy forward-references in the embedded schema blob. The contract is documented at pglite-engine.ts:222-225 and pinned by test/schema-bootstrap-coverage.test.ts:REQUIRED_BOOTSTRAP_COVERAGE. v0.29.1 (PR #696, schema v41) added pages.effective_date / effective_date_source / import_filename / salience_touched_at and a forward-referencing index in src/core/pglite-schema.ts:96:
CREATE INDEX IF NOT EXISTS ...
ON pages ((COALESCE(effective_date, updated_at)));
…but the bootstrap probe and REQUIRED_BOOTSTRAP_COVERAGE were not extended. v0.29.0 (schema v40) added pages.emotional_weight in pglite-schema.ts:74; this one is part of CREATE TABLE pages (... NOT NULL DEFAULT 0.0), so it's a no-op on existing tables (CREATE TABLE IF NOT EXISTS), but worth fixing in the same wave for completeness.
Repro
# On a pre-v0.29.1 PGLite brain (schema_version < 41):
gbrain init --migrate-only
# → column "effective_date" does not exist
gbrain apply-migrations --yes
# → same failure looped through v0.11.0 orchestrator
In my case: 2026-04-28-vintage brain at ~/.gbrain/brain.pglite, 160 pages, schema_version=24, upgraded from gbrain v0.18.2 → v0.30.1 via git pull && bun install.
Suggested fix
Mirror the pattern from #695. Two edits in src/core/pglite-engine.ts:applyForwardReferenceBootstrap:
-
Probe. Extend the round-trip with two new EXISTS checks for pages.effective_date and pages.emotional_weight.
-
Bootstrap. Add parallel branches to the if (...) chain. The v41 branch needs to add four columns:
ALTER TABLE pages ADD COLUMN IF NOT EXISTS effective_date TIMESTAMPTZ;
ALTER TABLE pages ADD COLUMN IF NOT EXISTS effective_date_source TEXT;
ALTER TABLE pages ADD COLUMN IF NOT EXISTS import_filename TEXT;
ALTER TABLE pages ADD COLUMN IF NOT EXISTS salience_touched_at TIMESTAMPTZ;
Only effective_date is strictly required by the pglite-schema.ts:96 index, but the rest are part of the same v41 wave and the v41 handler uses ADD COLUMN IF NOT EXISTS so the bootstrap-then-migrate sequence stays idempotent. The v40 branch:
ALTER TABLE pages ADD COLUMN IF NOT EXISTS emotional_weight REAL NOT NULL DEFAULT 0.0;
-
Pin in CI. Add to REQUIRED_BOOTSTRAP_COVERAGE in test/schema-bootstrap-coverage.test.ts:
// v0.29.1 — forward-referenced by `CREATE INDEX ... ON pages
// ((COALESCE(effective_date, updated_at)))`.
{ kind: 'column', table: 'pages', column: 'effective_date' },
{ kind: 'column', table: 'pages', column: 'effective_date_source' },
{ kind: 'column', table: 'pages', column: 'import_filename' },
{ kind: 'column', table: 'pages', column: 'salience_touched_at' },
// v0.29.0 — present in PGLITE_SCHEMA_SQL CREATE TABLE definition.
{ kind: 'column', table: 'pages', column: 'emotional_weight' },
And extend the strip-block to drop these columns + the index so the test exercises the new branch.
Workaround for affected operators
Back up the brain first:
cp -r ~/.gbrain/brain.pglite ~/.gbrain/brain.pglite.bak-$(date +%Y%m%d-%H%M%S)
Then a small PGLite script that pre-creates the missing columns. PGLite's query() accepts a single statement, so call it five times:
// /tmp/fix.ts — bun run /tmp/fix.ts (run from inside the gbrain repo)
import { PGlite } from '@electric-sql/pglite';
const db = new PGlite('/home/you/.gbrain/brain.pglite');
await db.waitReady;
await db.query(`ALTER TABLE pages ADD COLUMN IF NOT EXISTS effective_date TIMESTAMPTZ`);
await db.query(`ALTER TABLE pages ADD COLUMN IF NOT EXISTS effective_date_source TEXT`);
await db.query(`ALTER TABLE pages ADD COLUMN IF NOT EXISTS import_filename TEXT`);
await db.query(`ALTER TABLE pages ADD COLUMN IF NOT EXISTS salience_touched_at TIMESTAMPTZ`);
await db.query(`ALTER TABLE pages ADD COLUMN IF NOT EXISTS emotional_weight REAL NOT NULL DEFAULT 0.0`);
await db.close();
bun run /tmp/fix.ts && gbrain apply-migrations --yes walks the schema cleanly to v44. Note: a separate bug then prevents the v0.29.1 host-work phase from completing on PGLite — see the companion issue.
Environment
- gbrain v0.30.1 (master @ dffb607)
- OS: Ubuntu 22.04, Linux 5.15.0-176-generic
- Bun 1.3.11
- Database: PGLite (embedded),
schema_version=24 pre-upgrade
Why CI didn't catch it
Same root cause as #561, #695, #699: REQUIRED_BOOTSTRAP_COVERAGE is hand-maintained, and the v0.29.x author missed the bootstrap update when adding new forward-referenced columns. test/schema-bootstrap-coverage.test.ts is the right shape but only enforces what's in its array.
The note at the end of #695 about a stricter CI check (greppping pglite-schema.ts for CREATE INDEX ... ON <table>(<col>) patterns and asserting each <table>.<col> either exists at table-creation time or has a matching coverage entry) feels increasingly worth doing — this is the fourth confirmed instance (#561 v27, #695 v33, #699 v36, this one v41) of the same maintenance-contract drift. Out of scope for this issue.
Symptoms
Upgrading a pre-v0.29 PGLite brain to v0.30.x wedges with
column "effective_date" does not existbefore any migration runs. Same class as #561, #609, #695, #699 —applyForwardReferenceBootstrapis missing a probe for v41'spages.effective_date, soPGLITE_SCHEMA_SQL's replay aborts on the index that forward-references the column.Once aborted,
runMigrations()never gets to v41 (which addseffective_dateviaADD COLUMN IF NOT EXISTS), so every subsequentgbraininvocation hits the same failure.gbrain doctorconfirms:Affected versions
PGLite brains predating v0.29.1 (
schema_version < 41) on a v0.29.x or v0.30.x binary.The Postgres counterpart of this bug class is #561 (v27 columns); this one bites the PGLite path for v41 columns.
Root cause
src/core/pglite-engine.ts:applyForwardReferenceBootstrapruns beforegetPGLiteSchema()so old brains can satisfy forward-references in the embedded schema blob. The contract is documented atpglite-engine.ts:222-225and pinned bytest/schema-bootstrap-coverage.test.ts:REQUIRED_BOOTSTRAP_COVERAGE. v0.29.1 (PR #696, schema v41) addedpages.effective_date / effective_date_source / import_filename / salience_touched_atand a forward-referencing index insrc/core/pglite-schema.ts:96:…but the bootstrap probe and
REQUIRED_BOOTSTRAP_COVERAGEwere not extended. v0.29.0 (schema v40) addedpages.emotional_weightinpglite-schema.ts:74; this one is part ofCREATE TABLE pages (... NOT NULL DEFAULT 0.0), so it's a no-op on existing tables (CREATE TABLE IF NOT EXISTS), but worth fixing in the same wave for completeness.Repro
In my case: 2026-04-28-vintage brain at
~/.gbrain/brain.pglite, 160 pages,schema_version=24, upgraded from gbrain v0.18.2 → v0.30.1 viagit pull && bun install.Suggested fix
Mirror the pattern from #695. Two edits in
src/core/pglite-engine.ts:applyForwardReferenceBootstrap:Probe. Extend the round-trip with two new EXISTS checks for
pages.effective_dateandpages.emotional_weight.Bootstrap. Add parallel branches to the
if (...)chain. The v41 branch needs to add four columns:Only
effective_dateis strictly required by thepglite-schema.ts:96index, but the rest are part of the same v41 wave and the v41 handler usesADD COLUMN IF NOT EXISTSso the bootstrap-then-migrate sequence stays idempotent. The v40 branch:Pin in CI. Add to
REQUIRED_BOOTSTRAP_COVERAGEintest/schema-bootstrap-coverage.test.ts:And extend the strip-block to drop these columns + the index so the test exercises the new branch.
Workaround for affected operators
Back up the brain first:
Then a small PGLite script that pre-creates the missing columns. PGLite's
query()accepts a single statement, so call it five times:bun run /tmp/fix.ts && gbrain apply-migrations --yeswalks the schema cleanly to v44. Note: a separate bug then prevents the v0.29.1 host-work phase from completing on PGLite — see the companion issue.Environment
schema_version=24pre-upgradeWhy CI didn't catch it
Same root cause as #561, #695, #699:
REQUIRED_BOOTSTRAP_COVERAGEis hand-maintained, and the v0.29.x author missed the bootstrap update when adding new forward-referenced columns.test/schema-bootstrap-coverage.test.tsis the right shape but only enforces what's in its array.The note at the end of #695 about a stricter CI check (greppping
pglite-schema.tsforCREATE INDEX ... ON <table>(<col>)patterns and asserting each<table>.<col>either exists at table-creation time or has a matching coverage entry) feels increasingly worth doing — this is the fourth confirmed instance (#561 v27, #695 v33, #699 v36, this one v41) of the same maintenance-contract drift. Out of scope for this issue.