fix: detect PgBouncer transaction-mode pooler and set GBRAIN_PREPARE=true
Summary
Fixes #1435 — /sync-gbrain capability check fails under PgBouncer transaction mode because gbrain search silently returns no results when prepared statements are disabled.
lib/gbrain-exec.ts:buildGbrainEnv()now detects port 6543 in DATABASE_URL and auto-setsGBRAIN_PREPARE=truein the env for all gbrain spawns. Single chokepoint fix — every gstack-originated gbrain call inherits it. Callers can override withGBRAIN_PREPARE=false.sync-gbrain/SKILL.md{,.tmpl}: Capability check exportsGBRAIN_PREPARE=trueand retries search up to 3x with 1s delay for async index propagation under connection pooling.bin/gstack-gbrain-detect: Surfacesgbrain_pooler_mode("transaction" | "session" | null) in the preamble probe JSON for downstream skill visibility.
Test plan
- [ ] New unit tests for
isTransactionModePooler()— port 6543, 5432, no-port, unparseable, postgres:// scheme - [ ] New unit tests for
buildGbrainEnv()GBRAIN_PREPARE auto-detection — pooler URL sets it, session URL doesn't, caller opt-out respected, already-matching URL still sets it - [ ] Existing
buildGbrainEnvtests still pass (DATABASE_URL seeding behavior unchanged) - [ ] Manual: on a PgBouncer transaction-mode setup, run
/sync-gbrainand verify the capability check passes and## GBrain Search Guidanceblock is written to CLAUDE.md
Continuous collaboration, powered by ClosedLoop.AI (GitHub)
Co-Authored-By: Claude Opus 4.6 (1M context) [email protected]
This detection is using only port 6543 as transaction. That conflicts with existing gstack-gbrain-supabase-verify, which accepts 6543 as the canonical Session Pooler URL, and with #1582, which treats pool_mode as the thing that distinguishes transaction vs session. As written, gbrain_pooler_mode can report transaction for a working session pooler, and buildGbrainEnv will force GBRAIN_PREPARE=true without knowing the actual mode. Could we avoid naming it transaction from the URL alone, or persist/read Supabase pool_mode when available, and add tests for session/6543 and transaction/6543 separately?
Resolved on closedloop-ai:fix/1435-pgbouncer-transaction-mode-search in 757f684f.
I removed the URL-only :6543 => transaction inference. buildGbrainEnv() now only auto-sets GBRAIN_PREPARE=true when ~/.gbrain/config.json has a persisted Supabase pool_mode: "transaction". If the URL is a Supabase pooler but we do not have persisted mode metadata yet, gstack-gbrain-detect now reports gbrain_pooler_mode: "unknown" instead of guessing.
To make that metadata available, gstack-gbrain-supabase-provision pooler-url --json now returns pool_mode, and /setup-gbrain persists it into ~/.gbrain/config.json right after gbrain init. I also added coverage for transaction/session on the same :6543 pooled URL, the unknown fallback, detect output shape, and the provisioner JSON payload.
Thanks @mikeangstadt — PgBouncer transaction-mode detection shipped in v1.43.2.0 post-Daegu wave (#1642). Closes #1435.