mirror of
https://github.com/garrytan/gbrain.git
synced 2026-07-28 06:23:01 +00:00
* fix(initSchema): narrow pre-schema bootstrap + v24 PGLite no-op Closes a 2-year-old wedge cycle that hit users 10+ times across 6 schema versions (#239, #243, #266, #357, #366, #374, #375, #378, #395, #396). Bug class: gbrain ships an embedded schema blob (PGLITE_SCHEMA_SQL + SCHEMA_SQL) that runs before numbered migrations on every initSchema(). The blob references columns that newer migrations introduce. On any brain older than the migration that adds those columns, the blob crashes before the migration can run. Fix: PGLiteEngine.initSchema() and PostgresEngine.initSchema() now call a new private applyForwardReferenceBootstrap() before the schema blob. The bootstrap probes for missing forward-referenced state and adds only what's needed (sources table + pages.source_id, links.link_source + links.origin_page_id, content_chunks.symbol_name + content_chunks.language). Fresh installs and modern brains both no-op. A CI guard test/schema-bootstrap-coverage.test.ts enforces that the bootstrap covers every forward reference in PGLITE_SCHEMA_SQL. Future migrations that add column-with-index in the schema blob must extend the bootstrap; the test fails loudly otherwise. Migration v24 (rls_backfill_missing_tables) now no-ops on PGLite via sqlFor.pglite: '' since PGLite has no RLS engine and is single-tenant. Closes #395. The plan went through CEO + Eng + Codex review. Codex caught a critical bug in the original "run all migrations early" approach: it would crash on v24 trying to ALTER subagent tables that the schema blob hadn't created yet. The narrow bootstrap shape resolves that. Wave incorporates community PRs #398 (@vinsew), #399 (@jdcastro2), #402 (@schnubb-web). Co-Authored-By: vinsew <yiyangchaishu@gmail.com> Co-Authored-By: Julián David Castro <juliancastro@Mac-mini-de-Julian.local> Co-Authored-By: schnubb-web <info@mia-mai.de> Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * chore(test): bump beforeAll timeout on minions-shell-pglite for parallel-load flake Default 5s beforeAll timeout occasionally trips under the parallel test runner when many test files initialize PGLite concurrently. The same pattern is documented as a P0 TODO for v0.21 Code Cathedral tests; this is the one instance the upgrade-hardening wave directly exposed (CPU pressure from new bootstrap test files). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * chore: bump version and changelog (v0.21.1) Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * docs: update project documentation for v0.21.1 - CLAUDE.md: PGLite + Postgres engine entries note new applyForwardReferenceBootstrap() in initSchema(), v24 sqlFor.pglite no-op, and the new bootstrap test files (test/bootstrap.test.ts, test/schema-bootstrap-coverage.test.ts, test/e2e/postgres-bootstrap.test.ts). - CHANGELOG.md: voice polish on the v0.21.1 headline (drop stray ## prefixes so the bold two-line headline renders as bold prose, not h2 sub-headers that break the version-entry hierarchy). Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * chore: correct version slot from v0.22.5 to v0.21.6 Slot allocation correction. v0.21.6 is the actual landing slot for this wave on the v0.21.x patch line. VERSION, package.json, CHANGELOG.md (header + table + take-advantage section), CLAUDE.md (engine entries, migrate.ts entry, test descriptions) all updated together. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * chore: correct version slot to v0.22.7 VERSION, package.json, CHANGELOG.md (header + table + take-advantage section), CLAUDE.md (engine entries, migrate.ts entry, test descriptions) all updated together. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * chore: regenerate llms.txt + llms-full.txt for v0.22.7 CLAUDE.md changed (engine entries describe the bootstrap, migrate.ts entry describes the v24 PGLite no-op). The build:llms regen-drift guard caught the staleness in CI. Running `bun run build:llms` propagates the same content into the AI-consumable bundles. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * chore: change version slot from v0.22.7 to v0.22.6-hotfix.1 PR #483 (fix/mcp-registration-auth) claimed v0.22.7. Moved this wave to v0.22.6-hotfix.1 to avoid the collision. Note: semver-orders BEFORE 0.22.6 (pre-release suffix), so the hotfix tag is informational, not ordering-correct. Acceptable here because the wave's content predates master's 0.22.6 and is being landed as a parallel hotfix slot. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * chore: change version slot to v0.22.6.1 4-digit hotfix slot under master's v0.22.6. bun + bun:test accept the format; the build-llms regen-drift guard and bootstrap tests pass. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> --------- Co-authored-by: vinsew <yiyangchaishu@gmail.com> Co-authored-by: Julián David Castro <juliancastro@Mac-mini-de-Julian.local> Co-authored-by: schnubb-web <info@mia-mai.de> Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
94 lines
4.0 KiB
TypeScript
94 lines
4.0 KiB
TypeScript
/**
|
|
* E2E test for PostgresEngine forward-reference bootstrap.
|
|
*
|
|
* Codex caught that `test/e2e/helpers.ts:74` uses the standalone
|
|
* `db.initSchema()` from `src/core/db.ts`, which only runs SCHEMA_SQL and
|
|
* never calls runMigrations(). A test using that helper would NOT exercise
|
|
* `PostgresEngine.initSchema()`'s reordered path, producing false-positive
|
|
* coverage. This test deliberately bypasses the standard helper and
|
|
* instantiates `PostgresEngine` directly, calling `engine.initSchema()` so
|
|
* the bootstrap → SCHEMA_SQL → runMigrations sequence runs end-to-end.
|
|
*
|
|
* Covers issues #366, #375, #378 — Postgres-side wedges where pre-v0.18
|
|
* brains crashed on `column "source_id" does not exist`.
|
|
*
|
|
* NOTE: snapshot-based historical state simulation is out of scope for this
|
|
* wave (would require maintaining historical schema dumps). The test
|
|
* mutates a fresh-LATEST brain to a pre-v0.18 shape; codex flagged this as
|
|
* approximate. Acceptable here because the bootstrap's contract is narrow:
|
|
* "given a brain that lacks the specific forward-references, initSchema
|
|
* produces a brain at LATEST." The test exercises exactly that contract.
|
|
*
|
|
* Run: DATABASE_URL=postgresql://... bun run test:e2e test/e2e/postgres-bootstrap.test.ts
|
|
*/
|
|
|
|
import { describe, test, expect, beforeAll, afterAll } from 'bun:test';
|
|
import { PostgresEngine } from '../../src/core/postgres-engine.ts';
|
|
import { LATEST_VERSION } from '../../src/core/migrate.ts';
|
|
|
|
const DATABASE_URL = process.env.DATABASE_URL;
|
|
const skip = !DATABASE_URL;
|
|
|
|
describe.skipIf(skip)('PostgresEngine forward-reference bootstrap (E2E)', () => {
|
|
let engine: PostgresEngine;
|
|
|
|
beforeAll(async () => {
|
|
engine = new PostgresEngine();
|
|
await engine.connect({ database_url: DATABASE_URL! });
|
|
});
|
|
|
|
afterAll(async () => {
|
|
await engine.disconnect();
|
|
});
|
|
|
|
test('PostgresEngine.initSchema applies bootstrap → SCHEMA_SQL → migrations on pre-v0.18 brain', async () => {
|
|
// First call: bring the test DB to LATEST shape so we have something to mutate.
|
|
await engine.initSchema();
|
|
|
|
// Clear data from prior tests in the suite. Adding a UNIQUE(slug)
|
|
// constraint below would fail if multi-source fixtures left rows with
|
|
// duplicate slugs across sources (which is valid under the composite
|
|
// UNIQUE this test is undoing).
|
|
const conn = (engine as any).sql;
|
|
await conn.unsafe(`TRUNCATE pages, content_chunks, links, tags, raw_data, timeline_entries, page_versions, ingest_log RESTART IDENTITY CASCADE`);
|
|
|
|
// Mutate to pre-v0.18 shape: drop source_id and the sources table.
|
|
// The advisory lock is released between initSchema calls, so this
|
|
// direct DDL won't deadlock.
|
|
await conn.unsafe(`
|
|
ALTER TABLE pages DROP CONSTRAINT IF EXISTS pages_source_slug_key;
|
|
ALTER TABLE pages ADD CONSTRAINT pages_slug_key UNIQUE (slug);
|
|
DROP INDEX IF EXISTS idx_pages_source_id;
|
|
ALTER TABLE pages DROP COLUMN IF EXISTS source_id CASCADE;
|
|
DROP TABLE IF EXISTS sources CASCADE;
|
|
`);
|
|
await engine.setConfig('version', '20');
|
|
|
|
// The path under test: full PostgresEngine.initSchema() including the
|
|
// bootstrap call, SCHEMA_SQL replay, and runMigrations chain.
|
|
await engine.initSchema();
|
|
|
|
expect(await engine.getConfig('version')).toBe(String(LATEST_VERSION));
|
|
|
|
// Verify the forward-referenced column exists after upgrade.
|
|
const colCheck = await conn`
|
|
SELECT column_name FROM information_schema.columns
|
|
WHERE table_schema = current_schema()
|
|
AND table_name = 'pages'
|
|
AND column_name = 'source_id'
|
|
`;
|
|
expect(colCheck).toHaveLength(1);
|
|
|
|
// Verify the default source row was seeded.
|
|
const srcCheck = await conn`SELECT id FROM sources WHERE id = 'default'`;
|
|
expect(srcCheck).toHaveLength(1);
|
|
});
|
|
|
|
test('PostgresEngine.initSchema is idempotent on a brain already at LATEST', async () => {
|
|
// Fresh-LATEST brain. Calling initSchema again must not error and must
|
|
// not regress the version.
|
|
await engine.initSchema();
|
|
expect(await engine.getConfig('version')).toBe(String(LATEST_VERSION));
|
|
});
|
|
});
|