Skip to content

v0.26.6 feat(schema): PGLite ↔ Postgres parity gate (closes #588)#590

Merged
garrytan merged 6 commits into
masterfrom
garrytan/issue-588-v0.26.3
May 4, 2026
Merged

v0.26.6 feat(schema): PGLite ↔ Postgres parity gate (closes #588)#590
garrytan merged 6 commits into
masterfrom
garrytan/issue-588-v0.26.3

Conversation

@garrytan

@garrytan garrytan commented May 3, 2026

Copy link
Copy Markdown
Owner

Summary

  • New CI gate at test/e2e/schema-drift.test.ts spins up fresh PGLite + Postgres, runs each engine's canonical initSchema() (bootstrap → schema replay → migrations), snapshots information_schema.columns, and diffs the four-tuple (data_type, udt_name, is_nullable, column_default) per column. 27 of 29 tables in the parity contract; 2-table allowlist (files, file_migration_ledger) is narrow by design.
  • 17 unit cases for the pure diff helper (test/helpers/schema-diff.ts + schema-diff.test.ts) including a D3 negative test that reproduces the v0.26.1 oauth_clients.token_ttl regression — what would have caught that incident at PR time.
  • D6 reconciliation: gate caught access_tokens.id was UUID on Postgres (schema.sql:328, migration v4) but TEXT on PGLite. Aligned to UUID DEFAULT gen_random_uuid() on both sides.
  • CI wiring in scripts/e2e-test-map.ts triggers the gate on changes to src/schema.sql, src/core/pglite-schema.ts, or src/core/migrate.ts.
  • Plus two pre-existing master regressions cleaned up in passing: privacy violation in src/core/mounts-cache.ts (banned name) and a new legacy db.getConnection() caller in src/commands/integrity.ts (added to the script's grandfather allowlist). The CHANGELOG merge of v0.26.1's credit line had a third instance fixed during merge resolution.

Codex review

Original static-comparison plan got demolished by codex review — raw schema.sql vs raw pglite-schema.ts are already intentionally divergent (PGLite reaches its end-state via PGLITE_SCHEMA_SQL + migrations, not the raw blob alone). Comparing raw files would generate permanent false positives on the first run. Pivoted to end-state comparison, which catches real drift without false positives. Six of seven codex findings folded into the plan as binding constraints (D2 pivot, D4 four-tuple snapshot, D5 narrow allowlist, D6 fix exposed drift, plus 2 scope deferrals); one (versioning hardening guard) deferred to v0.26.7.

What's NOT in this release

Out of scope for v0.26.6 (filed for v0.26.7):

  • Manual ALTER TABLE on production Postgres that never made it into source files (the actual v0.26.1 trigger; needs a gbrain doctor --schema-audit mechanism, separate from the CI parity gate)
  • Index parity (column drift was the v0.26.1 trigger; the diffSnapshots shape is extensible to indexes)
  • Versioning hardening (a scripts/check-version-sync.sh pre-push guard)

Test plan

  • bun test test/helpers/schema-diff.test.ts — 17/17 pass (pure diff function, no DB)
  • DATABASE_URL=… bun test test/e2e/schema-drift.test.ts — 6/6 pass against fresh pgvector container
  • bun run typecheck — clean
  • bun build --compile --outfile bin/gbrain src/cli.ts — clean
  • Full unit suite: 3745 pass / 318 skip / 3 fail. The 3 fails are pre-existing on master HEAD (brain-registry > empty/null/undefined id routes to host, claw-test --scenario fresh-install, plus build-llms which I fixed by regenerating bundles after CLAUDE.md edits).
  • Manual: deliberately removed oauth_clients.token_ttl from the synthetic snapshot to confirm the gate fires with the column named in the failure message.
  • Post-merge with master (which added v0.26.2): typecheck clean, schema-diff unit tests 17/17 pass. Master's diff didn't touch any schema files.

Versioning note

HEAD's VERSION + package.json were 0.26.0 when this branch started; master has since shipped v0.26.1 (#577) and v0.26.2 (#593). This PR ships as v0.26.6 per user instruction. Hardening guard for VERSION/package.json/commit-message drift is filed as v0.26.7 follow-up.

(Branch name is garrytan/issue-588-v0.26.3 from an earlier rename — GitHub doesn't support changing a PR's head branch ref. Branch URL is cosmetic; VERSION/package.json/CHANGELOG/PR title all read v0.26.6.)

🤖 Generated with Claude Code

garrytan and others added 2 commits May 3, 2026 10:39
…d type fix (#588)

Drift gate (test/e2e/schema-drift.test.ts) spins up fresh PGLite + Postgres,
runs each engine's initSchema(), snapshots information_schema.columns, and
diffs the four-tuple (data_type, udt_name, is_nullable, column_default) per
column. 17 unit cases for the pure diff function (test/helpers/schema-diff.ts
+ schema-diff.test.ts) including a D3 negative test that reproduces the v0.26.1
oauth_clients.token_ttl regression. 6 E2E cases including 4 sentinels for
oauth_clients, mcp_request_log, access_tokens, eval_candidates.

The gate caught one real drift on its first run: access_tokens.id was UUID on
Postgres (schema.sql:328, migration v4) and TEXT on PGLite (pglite-schema.ts).
Reconciled to UUID DEFAULT gen_random_uuid() on both sides.

CI wiring in scripts/e2e-test-map.ts triggers schema-drift on changes to
schema.sql, pglite-schema.ts, or migrate.ts. The 2-table allowlist (files,
file_migration_ledger) is narrow by design — every other Postgres table must
reach PGLite via PGLITE_SCHEMA_SQL or a migration's sqlFor.pglite branch.

Bookkeeping: master HEAD's VERSION was 0.26.0 even though the prior commit
shipped as v0.26.1 (the bump never landed). Moving to 0.26.3 per the same
bookkeeping discontinuity. Codex flagged a versioning hardening follow-up
(scripts/check-version-sync.sh pre-push guard) for v0.26.4.

Also fixes two pre-existing CI failures master shipped through:
- check-privacy.sh: src/core/mounts-cache.ts had two banned name references
  ("Wintermute"). Replaced with "your OpenClaw" per CLAUDE.md:550.
- check-no-legacy-getconnection.sh: src/commands/integrity.ts:355 was a new
  legacy db.getConnection() caller. Added to the script's allowlist with a
  PR 1 cleanup note (matches the existing 8 grandfathered entries).

Out of scope (filed for v0.26.4): manual ALTER TABLE on production Postgres
that never made it into source files (the actual v0.26.1 trigger; needs a
gbrain doctor --schema-audit mechanism); index parity; versioning hardening
guard.

Plan + codex review pivot: original plan compared raw schema.sql vs raw
pglite-schema.ts; codex showed they're intentionally divergent today (PGLite
reaches its end-state via PGLITE_SCHEMA_SQL + migrations). Pivoted to
end-state comparison, which catches real drift without false positives.

Closes #588.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Per user instruction. No code or test changes — VERSION + package.json +
CHANGELOG header/body + CLAUDE.md key-files entry. Regenerated llms-full.txt.
"NOT in this release" deferral targets bumped from v0.26.4 → v0.26.5
(those items are still deferred; they're now deferred from v0.26.4).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@garrytan garrytan changed the title v0.26.3 feat(schema): PGLite ↔ Postgres parity gate (closes #588) v0.26.4 feat(schema): PGLite ↔ Postgres parity gate (closes #588) May 3, 2026
garrytan and others added 2 commits May 3, 2026 11:52
Master added v0.26.2 (#593) and v0.26.1 (#577) entries via the OAuth
fix-wave landing path. Resolved three conflicts:

- VERSION: kept 0.26.4 (this branch's version)
- package.json: kept 0.26.4
- CHANGELOG.md: kept both — v0.26.4 entry on top, then master's new
  v0.26.2 + v0.26.1 entries below. Final order: 0.26.4 → 0.26.2 →
  0.26.1 → 0.26.0 (top to bottom, contiguous version sequence).

Also fixed a banned-name reference master shipped in the v0.26.1
CHANGELOG credit line ("Co-authored by Wintermute" → "Co-authored by
your OpenClaw") per CLAUDE.md:550 / scripts/check-privacy.sh.

Regenerated llms-full.txt to match the merged CHANGELOG.

Verified post-merge: typecheck clean, schema-diff unit tests 17/17 pass.
Master's diff did not touch src/schema.sql, src/core/pglite-schema.ts,
or src/core/migrate.ts, so the new schema-drift gate's behavior is
unchanged.
Per user instruction. Bookkeeping-only — VERSION + package.json +
CHANGELOG header/body + CLAUDE.md key-files entry. Regenerated
llms-full.txt. "NOT in this release" deferral targets bumped from
v0.26.5 → v0.26.7 (those items remain deferred; now from v0.26.6
instead of v0.26.4).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@garrytan garrytan changed the title v0.26.4 feat(schema): PGLite ↔ Postgres parity gate (closes #588) v0.26.6 feat(schema): PGLite ↔ Postgres parity gate (closes #588) May 3, 2026
garrytan added 2 commits May 3, 2026 17:10
Master added f082501 v0.26.3 feat(admin): observability + per-agent
config + auth hardening (#586), bringing migration v33 (oauth_clients
gets token_ttl + deleted_at; mcp_request_log gets agent_name + params
+ error_message), the magic-link admin login flow, and per-client TTL.

Resolved three conflicts (all bookkeeping, all version-related):

- VERSION: kept 0.26.6 (this branch's version, ahead of master's 0.26.3)
- package.json: kept 0.26.6
- CHANGELOG.md: my v0.26.6 entry on top, then master's new v0.26.3
  block, then the rest. Final order: 0.26.6 → 0.26.3 → 0.26.2 →
  0.26.1 → 0.26.0 (top to bottom, contiguous).

Auto-merged but needed cleanup:
- scripts/check-no-legacy-getconnection.sh: both sides added
  src/commands/integrity.ts to the allowlist with different notes.
  Deduped to one entry combining both notes.

Schema-drift gate sanity check post-merge:
- access_tokens.id is still UUID DEFAULT gen_random_uuid() in both
  schema.sql and pglite-schema.ts (my v0.26.3 D6 fix preserved)
- Master's new oauth_clients.token_ttl + .deleted_at columns are in
  both engines (master added them to schema.sql + pglite-schema.ts +
  migration v33's sqlFor — the gate is satisfied automatically)
- Master's new mcp_request_log.agent_name + .params + .error_message
  same story
- Typecheck clean, schema-diff unit tests 17/17 pass
- Privacy script clean

Regenerated llms-full.txt to match the merged CHANGELOG.
Master added two more commits:
- d97f159 v0.26.4 test: parallel unit-test loop (12x speedup, #605)
- 0de9eb6 v0.26.5 feat: destructive operation guard end-to-end (#600)

Resolved three conflicts (all version bookkeeping):

- VERSION: kept 0.26.6 (this branch's version, ahead of master's 0.26.5)
- package.json: kept 0.26.6
- CHANGELOG.md: my v0.26.6 entry on top, then master's new v0.26.5 +
  v0.26.4 blocks below. Final order: 0.26.6 → 0.26.5 → 0.26.4 →
  0.26.3 → 0.26.2 → 0.26.1 → 0.26.0 (top to bottom, contiguous).

Schema-drift gate sanity check post-merge:
- Master's v0.26.5 destructive-guard work added pages.deleted_at
  (with partial index pages_deleted_at_purge_idx) and three columns
  on sources (archived, archived_at, archive_expires_at). All four
  are present in BOTH src/schema.sql AND src/core/pglite-schema.ts —
  master kept them in lockstep, so the gate is satisfied automatically.
- access_tokens.id is still UUID DEFAULT gen_random_uuid() in both
  engines (my v0.26.3 D6 fix preserved across the merge).
- Typecheck clean, schema-diff unit tests 17/17 pass, privacy script
  clean (master's v0.26.4 work fixed the Wintermute references in
  mounts-cache.ts that I had patched earlier — converged independently).

Regenerated llms-full.txt to match the merged CHANGELOG.
@garrytan garrytan merged commit 9e2093f into master May 4, 2026
7 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant