Skip to content

Forward-reference bootstrap missing v0.29 columns (pages.effective_date, emotional_weight) — pre-v0.29 PGLite brains wedge on upgrade #755

@axe08

Description

@axe08

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, #699applyForwardReferenceBootstrap 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:

  1. Probe. Extend the round-trip with two new EXISTS checks for pages.effective_date and pages.emotional_weight.

  2. 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;
  3. 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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions