Files
gbrain/scripts/check-no-legacy-getconnection.sh
T
9e2093fc9b v0.26.6 feat(schema): PGLite ↔ Postgres parity gate (closes #588) (#590)
* v0.26.3 feat(schema): PGLite ↔ Postgres parity gate + access_tokens.id 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>

* chore: bump v0.26.3 → v0.26.4

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>

* chore: bump v0.26.4 → v0.26.6

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>

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-03 20:48:39 -07:00

86 lines
3.7 KiB
Bash
Executable File

#!/bin/bash
# CI guard against silent singleton reuse in connected-gbrains code paths.
#
# Codex finding #7 (plan review 2026-04-22): the module singleton in
# src/core/db.ts is shared across the process. With multi-brain routing,
# any `db.getConnection()` call in an op-dispatch code path means that op
# silently targets whichever brain connected to the singleton first,
# regardless of ctx.brainId / ctx.engine. This is exactly the bug Codex
# #1 flagged in postgres-engine.ts internals.
#
# This script fails the build when NEW `db.getConnection()` calls appear
# in src/core/operations.ts (the per-op handler surface) or in any new
# `src/commands/*.ts` file. Existing legitimate callers are grandfathered
# via an explicit allowlist — cleanups land in PR 1.
#
# When you hit this guard: instead of `db.getConnection()` or `db.connect(...)`,
# use `ctx.engine` from the passed-in OperationContext. See
# src/core/brain-registry.ts for how ctx.engine gets populated per-call.
#
# Run manually: bash scripts/check-no-legacy-getconnection.sh
# Wired into CI: `bun test` (via package.json scripts.test)
set -euo pipefail
ROOT=$(git rev-parse --show-toplevel 2>/dev/null || pwd)
cd "$ROOT"
# Files that are allowed to touch the singleton today. Every other file
# under src/core or src/commands is forbidden. This list shrinks in PR 1.
ALLOWED=(
"src/core/db.ts" # the singleton's definition
"src/core/postgres-engine.ts" # calls db.connect + fallback in sql getter — PR 1 removes the fallback
"src/commands/init.ts" # first-time setup path, no engine yet
"src/commands/doctor.ts" # PR 1 refactors to accept engine
"src/commands/files.ts" # PR 1 refactors to accept engine
"src/commands/repair-jsonb.ts" # PR 1 refactors
"src/commands/serve-http.ts" # PR 1 threads engine through the OAuth dispatch path
"src/commands/integrity.ts" # v0.22.8 batch-load fast path + scanIntegrityBatch; PR 1 refactors to accept engine
"src/core/operations.ts" # 3 localOnly ops (file_list/upload/url) move to ctx.engine in PR 1
)
# Build an argument list for `grep` that excludes allowed files.
EXCLUDE_ARGS=()
for file in "${ALLOWED[@]}"; do
EXCLUDE_ARGS+=(--exclude="$file")
done
# Search src/core/ and src/commands/ for db.getConnection or db.connect calls.
# We look for the `db.` prefix so references to the symbol elsewhere (e.g.
# the grep guard itself) don't trip the check.
VIOLATIONS=$(
grep -rn "db\.\(getConnection\|connect\)(" \
--include="*.ts" \
"${EXCLUDE_ARGS[@]}" \
src/core src/commands 2>/dev/null \
| grep -v -F "src/core/db.ts" \
| grep -v "^[^:]*:[0-9]*:[[:space:]]*\(//\|\*\)" \
|| true
)
if [ -n "$VIOLATIONS" ]; then
# Filter out allowed files from the result (the --exclude only matches basename)
FILTERED=$(printf '%s\n' "$VIOLATIONS" | while IFS= read -r line; do
path="${line%%:*}"
allow=0
for ok in "${ALLOWED[@]}"; do
if [ "$path" = "$ok" ]; then allow=1; break; fi
done
if [ "$allow" -eq 0 ]; then printf '%s\n' "$line"; fi
done)
if [ -n "$FILTERED" ]; then
echo "ERROR: new direct db.getConnection() / db.connect() call found in multi-brain code path:" >&2
echo "" >&2
printf '%s\n' "$FILTERED" >&2
echo "" >&2
echo "Use ctx.engine from the passed-in OperationContext instead." >&2
echo "See src/core/brain-registry.ts for the routing model." >&2
echo "If this call is legitimate, add its path to the ALLOWED list in" >&2
echo "scripts/check-no-legacy-getconnection.sh with a PR 1 cleanup note." >&2
exit 1
fi
fi
echo "check-no-legacy-getconnection: ok (no new singleton callers)"