Summary
Implementation specification for PostgreSQL persistence layer that intercepts SDK hook events via composable wrappers. Replaces the orchestrator-level StorageManager approach from JSON_DATABASE_MIGRATION.md with a 4-file, zero-behavior-change hook bridge.
Spec: docs/pending-updates/hookDBBridge-spec.md (v2.2, 17 sections, ~3800 lines)
Architecture
sdkHooksConfig → wrapHooksForDB() → wrapHooksForSSE() → manifest.wrapHooks() → [P0 gate] → agentQuery()
↑ NEW (outermost)
Hook events already flow through sdkHooks.js. The bridge intercepts them and persists to PostgreSQL without modifying any existing files. Feature-flagged behind HOOK_DB_PERSISTENCE=false.
Database Schema (4 Core Tables)
| Table |
Purpose |
Key Design Decisions |
sessions |
One row per research session |
tenant_id + RLS for multi-tenant isolation |
reports |
All markdown content from /reports/ |
Application-layer type validation (no CHECK constraints) |
agent_states |
JSON state files for cross-session recovery |
Glob pattern matching for section-writer multi-file state |
hook_audit_log |
All hook events (replaces file-based audit) |
Denormalized session_key for direct queries |
Key Technical Decisions
- State files via SubagentStop, not PostToolUse —
updateStateFileWithGateChecks() uses raw fs.writeFileSync which bypasses hooks. State persistence reads from disk after SubagentStop fires.
- Circuit breaker with half-open recovery — 5 failures → open → 5min timeout → probe write → close/re-open. Module-level health signal for cross-request awareness (§16.3).
- Config-driven classification —
hookDBBridgeConfig.js co-locates all type maps, validation sets, and filter configs. New agent types or report categories require changes to ONE file, zero ALTER TABLE.
- Application-layer validation —
VALID_REPORT_TYPES / VALID_SESSION_STATUSES Sets replace SQL CHECK constraints. Schema evolution without migrations.
- SessionCache with Promise deduplication —
pendingResolves Map prevents duplicate INSERT races from concurrent SubagentStart hooks. try/finally ensures cleanup on query error.
Enterprise Deployment (§15)
| Feature |
Approach |
| Tenant isolation |
tenant_id column + PostgreSQL Row-Level Security (RLS) on all tables |
| Data retention |
6mo live → 12mo warm (partitioned) → 24mo cold (S3/GCS) → purge |
| Schema versioning |
schema_migrations table + node-pg-migrate (replaces ensureHookSchema()) |
| Connection hardening |
SSL, 30s query timeout, keepAlive, graceful shutdown, credential rotation |
| Capacity |
50 tenants @ ~18GB live storage, ~$1.66/mo cold storage |
| Observability |
10 metrics, /health/db endpoint, circuit breaker alerting |
Robustness Audit (§16)
| ID |
Brittleness |
Severity |
Mitigation |
| B1 |
Config drift (state maps) |
CRITICAL |
CI sync test suite |
| B2 |
Sync I/O in async context |
HIGH |
fs/promises async I/O |
| B3 |
Request-scoped circuit breaker |
HIGH |
Module-level health signal |
| B4 |
RLS triple round-trip |
MEDIUM |
SET LOCAL in transactions |
| B5 |
No write verification |
MEDIUM |
Post-write hash check |
| B6 |
Partition boundary sessions |
LOW |
Don't partition sessions table |
| B7 |
SDK hook signature change |
LOW |
Pin version + CI test |
After mitigations: 0 silent failure modes.
Session History API & Frontend (§17) — NEW in v2.2
Closes the read-path gap from write-only persistence to user-visible session history.
| Component |
Description |
GET /api/sessions |
Keyset-paginated session list with status/date/model filters |
GET /api/sessions/:dir |
Full session detail with reports + agent states (parallel fetch) |
GET /api/sessions/stats |
Aggregate metrics dashboard (cost, QA scores, model breakdown) |
| Frontend sidebar |
Tabbed left panel (Sessions/Controls), session cards with status/cost/QA badges, infinite scroll, click-to-load/resume |
| Live auto-append |
New sessions auto-prepend to list on system_init, status badge updates on final |
| Metrics enrichment |
handleSessionEnd() enriches sessions.meta with word_count, qa_score, report/subagent counts |
| Historical backfill |
Script to populate 59 existing disk sessions into database |
| Graceful degradation |
503 probe auto-hides panel when DB unavailable |
Implementation Phases
Files
| Action |
File |
Purpose |
| Create |
src/config/hookDBBridgeConfig.js |
All type maps, validation sets, filter configs |
| Create |
src/utils/hookDBBridge.js |
wrapHooksForDB() + persistence handlers |
| Create |
scripts/backfill-sessions.mjs |
Historical session backfill |
| Modify |
src/config/featureFlags.js |
Add HOOK_DB_PERSISTENCE flag |
| Modify |
src/db/postgres.js |
Enterprise pool config + ensureHookSchema() → runMigrations() |
| Modify |
src/server/claude-sdk-server.js |
2 lines hook wiring + 3 session history API routes |
| Modify |
test/react-frontend/index.html |
Left panel tabs (Sessions/Controls) |
| Modify |
test/react-frontend/styles.css |
Session card styles, tab bar |
| Modify |
test/react-frontend/app.js |
Session list fetch, card rendering, load/resume flow |
Test Coverage
- 77 unit tests — config module, extraction helpers, validation, SessionCache, CircuitBreaker, persistence handlers, dispatcher routing
- 29 integration tests — report persistence, state files, P0 artifacts, session lifecycle, concurrency, retention, tenant isolation
- 12 API tests — session list pagination, filters, detail endpoint, stats aggregation, RLS isolation, graceful degradation
- 6 frontend tests — card rendering, click-to-load, infinite scroll, live append, status update, degradation
Total: 124 tests
Related
🤖 Generated with Claude Code
Summary
Implementation specification for PostgreSQL persistence layer that intercepts SDK hook events via composable wrappers. Replaces the orchestrator-level
StorageManagerapproach fromJSON_DATABASE_MIGRATION.mdwith a 4-file, zero-behavior-change hook bridge.Spec:
docs/pending-updates/hookDBBridge-spec.md(v2.2, 17 sections, ~3800 lines)Architecture
Hook events already flow through
sdkHooks.js. The bridge intercepts them and persists to PostgreSQL without modifying any existing files. Feature-flagged behindHOOK_DB_PERSISTENCE=false.Database Schema (4 Core Tables)
sessionstenant_id+ RLS for multi-tenant isolationreports/reports/agent_stateshook_audit_logsession_keyfor direct queriesKey Technical Decisions
updateStateFileWithGateChecks()uses rawfs.writeFileSyncwhich bypasses hooks. State persistence reads from disk after SubagentStop fires.hookDBBridgeConfig.jsco-locates all type maps, validation sets, and filter configs. New agent types or report categories require changes to ONE file, zero ALTER TABLE.VALID_REPORT_TYPES/VALID_SESSION_STATUSESSets replace SQL CHECK constraints. Schema evolution without migrations.pendingResolvesMap prevents duplicate INSERT races from concurrent SubagentStart hooks.try/finallyensures cleanup on query error.Enterprise Deployment (§15)
tenant_idcolumn + PostgreSQL Row-Level Security (RLS) on all tablesschema_migrationstable +node-pg-migrate(replacesensureHookSchema())/health/dbendpoint, circuit breaker alertingRobustness Audit (§16)
fs/promisesasync I/OSET LOCALin transactionsAfter mitigations: 0 silent failure modes.
Session History API & Frontend (§17) — NEW in v2.2
Closes the read-path gap from write-only persistence to user-visible session history.
GET /api/sessionsGET /api/sessions/:dirGET /api/sessions/statssystem_init, status badge updates onfinalhandleSessionEnd()enrichessessions.metawith word_count, qa_score, report/subagent countsImplementation Phases
hookDBBridgeConfig.js,hookDBBridge.js,postgres.jsupdate, server wiringFiles
src/config/hookDBBridgeConfig.jssrc/utils/hookDBBridge.jswrapHooksForDB()+ persistence handlersscripts/backfill-sessions.mjssrc/config/featureFlags.jsHOOK_DB_PERSISTENCEflagsrc/db/postgres.jsensureHookSchema()→runMigrations()src/server/claude-sdk-server.jstest/react-frontend/index.htmltest/react-frontend/styles.csstest/react-frontend/app.jsTest Coverage
Total: 124 tests
Related
JSON_DATABASE_MIGRATION.mdhookSSEBridge.js(§1.1)🤖 Generated with Claude Code