refactor(storage): consolidate common store behavior through Bun - #1344
refactor(storage): consolidate common store behavior through Bun#1344mariusvniekerk wants to merge 24 commits into
Conversation
roborev: Combined Review (
|
1b69f35 to
9fe49a4
Compare
roborev: Combined Review (
|
9fe49a4 to
191fc71
Compare
roborev: Combined Review (
|
191fc71 to
30ad076
Compare
roborev: Combined Review (
|
30ad076 to
e21d19f
Compare
roborev: Combined Review (
|
e21d19f to
bea681c
Compare
roborev: Combined Review (
|
bea681c to
b1948da
Compare
roborev: Combined Review (
|
b1948da to
9fcd600
Compare
roborev: Combined Review (
|
9fcd600 to
3d56eec
Compare
roborev: Combined Review (
|
Core storage reads had drifted across SQLite, PostgreSQL, and DuckDB, making lifecycle and schema parity difficult to preserve. Centralize canonical session, message, timing, cursor, and metadata reads in BunStore so adapters only own guarded handles and engine lifecycle. Keep result hydration bounded, preserve case-sensitive partial-ID behavior portably, and expose source-file metadata only through full-session reads.
The shared-store cutover cannot trust a compatibility stamp unless canonical parents, indexes, triggers, and backend column contracts still match. Fail closed on stamped drift and advance the archive data version so older binaries cannot write through the one-time identity cutover. Keep core reads bounded and chronological across large transcripts and mixed SQLite timestamps, while preserving PostgreSQL's native update marker and serialized convergence semantics.
Project identity must resolve from the same source-scoped rows on every serving backend before inventory and governance can move into the common store. Centralize observation selection and archive-scope aggregation in BunStore so SQLite, PostgreSQL, and DuckDB derive identical project identities without a backend-specific scanner.
The shared-store cutover must preserve archive chronology, parser bookkeeping, and source provenance while retiring backend-specific identity readers. Without these invariants, an upgraded archive could be reopened by an older binary or leave a required resync incomplete across restarts.\n\nMake the cutover version floor and pending-resync state atomic, keep canonical identity replacement and artifact writes provenance-complete, and restrict adapter-specific hydration to SQLite operational fields.
Inventory, governance rules, and worktree candidates must observe the same canonical session provenance and source-scoped mappings on every serving backend. Separate scanners made cross-archive isolation and snapshot selection depend on the adapter in use.\n\nRun these reads once through BunStore, preserve the existing pure identity and governance reducers, and prove the literal contract against SQLite, DuckDB, and PostgreSQL before deleting the concrete implementations.
The long-lived canonical cutover must stay ahead of main-line data versions and treat an installed replacement as final even when its caller is canceled after the atomic swap. Otherwise a successful resync can be reported stale or reopened by an older writer. Keep composite inventory, governance, and candidate reads on one backend snapshot with bounded hydration, and compare SQLite activity as instants so mixed-offset archives preserve the cross-backend contract.
Stars and pins are dashboard curation regardless of the serving engine, but concrete implementations encoded different message identities and read-only behavior. Keeping them separate made PostgreSQL and DuckDB semantics drift from the canonical session/ordinal model. Route the family through operation-scoped Bun writes, preserve target-generated pin identities during replicated upserts, and keep DuckDB rejection ahead of SQL while allowing PostgreSQL curation despite its public remote-mode status.
Insight persistence and reads are dashboard behavior regardless of serving engine, but concrete implementations duplicated filtering, timestamp normalization, and write policy. That drift made future schema work depend on three method families instead of the canonical Bun model.\n\nRoute insights through the operation-scoped common store, keep DuckDB read-only before SQL, and require PostgreSQL's existing insight capability probe before permitting writes.
Composite reads and curation writes must remain coherent across engine lifecycle changes, canonical ID transitions, and restricted PostgreSQL roles. Without explicit adapter guarantees, a unified method could still mix DuckDB generations, hydrate an entire archive, collide replicated pin IDs, or lose the established read-only sentinel.\n\nMake snapshot semantics mandatory, keep the one-time cutover fence fixed, bound candidate hydration by selected projects, and separate generated versus mirrored curation identities. Probe PostgreSQL insight insertion and deletion independently while preserving permission failures as ErrReadOnly.
Session rename and trash behavior must preserve the same user-tombstone, alias-exclusion, and atomicity rules regardless of the serving engine. Separate implementations made PostgreSQL ownership timestamps, SQLite watcher baselines, and DuckDB read-only policy easy to drift. Route the family through operation-scoped Bun transactions, retain only adapter-owned operational touches, and reject unsupported writes before opening a backend write guard.
Unified Bun mutations must retain each adapter’s operational guarantees and expose result counts only after atomic success. Quack also needs a replacement identity that cannot collide when descriptive metadata repeats. Keep SQLite FTS cleanup and PostgreSQL database-clock revisions behind adapter hooks, and stamp disposable DuckDB mirrors with a schema-v11 opaque generation.
Recall should follow the same guarded handle and capability policy as the other common Store families. Concrete remote stubs split method ownership and made read-only rejection depend on each wrapper. Route canonical entry reads, inserts, and query events through BunStore while retaining SQLite-only FTS, vector, import, and eval behavior behind an explicit capability.
Shared storage callbacks and mirror replication must remain correct under replay, read-only operation, stale replicated identities, and partial metadata failures. These cases sit at adapter boundaries where a nominally unified query can otherwise publish rejected state or make a valid mirror unreadable. Preserve read-only Recall validation, make composite results attempt-local, reconcile mirror-owned pin IDs, and publish DuckDB generation metadata atomically with an explicit compatibility requirement. Keep the implementation plan aligned so later Bun cutover work retains those constraints.
Pricing and usage are common storage behavior, but three independent implementations made exact money arithmetic, catalog fallback, windowing, and transaction semantics vulnerable to backend drift.\n\nRoute all usage reads and pricing state through BunStore, preserve engine-neutral cost reducers, and make pricing-row plus band replacement atomic so every adapter observes the same catalog snapshot.
Unified reads and writes must stay inside one backend snapshot and one atomic publication boundary, including retry, optional-schema, pricing, and mirror identity edge cases. Without those guarantees the common Bun path could combine generations, leak replay state, or partially publish adapter metadata. Keep filtering and time windows in SQL, use database time for portable insight ordering, and make the remaining adapter-specific capabilities explicit so the next cutover slices can delete concrete paths safely.
Analytics, trends, activity reports, and recent edits are common serving behavior, but independent backend implementations made filters, timestamp handling, and aggregation semantics drift across engines.\n\nRoute the full family through one replay-safe Bun snapshot and shared reducers, leaving backend wrappers with only their operational usage helpers. Remove the private SQL-builder tests and concrete scanners so future analytics changes have one owner and one literal cross-backend contract.
The shared analytics and usage paths must retain backend ordering, optional-schema, replay, and activity-window contracts while keeping remote reads bounded. Review exposed edge cases where the first unified implementation could mix rejected snapshots, drift across dialects, or substitute the wrong timestamps. Keep only genuinely dialect-specific timestamp expressions at the adapter boundary and preserve the established empty-result and nil-safety contracts so the next search/vector cutover starts from a verified common store.
Remote snapshot adapters can replay a read callback after the underlying generation changes. Composite session and message reads must therefore discard the first attempt and publish only the accepted retry, including count, hydration, and timing data.\n\nThe PostgreSQL usage cutover also left an unreachable query renderer behind and dropped a required schema-validation return. Remove only renderer paths with no production caller, retain the activity-report row and pricing contracts, and restore the schema prerequisite.
Signal and trend requests can span an entire archive, so retaining every matching transcript body makes peak memory grow with archive size. Reduce narrow content projections as database rows arrive and bound each query to a small session batch without limiting the final totals.\n\nSignal drill-down still ranks the complete candidate set, then loads full message content only for the requested examples. Trend session filtering keeps date bounds at message time so timestamp fallback behavior does not change.
Foundation now gives Bun-owned identity triggers non-colliding names so legacy startup DDL cannot overwrite them. Keep the later stamped-drift regression pointed at that canonical trigger while preserving its fail-without-repair assertion.
Bun SelectQuery.Clone drops a transaction connection, which deadlocks composite reads on a single-connection DuckDB store. Rebind cloned session queries to the guarded view handle. Bun also renders zero-valued default-tagged fields as DEFAULT, but existing PostgreSQL pricing-band columns have no physical defaults. Bind every canonical numeric price explicitly while retaining bounded shared batches across all adapters.
Stamped convergence now validates the metadata value rather than only key existence. Make the schema probe return the stored compatibility value so fail-closed and DDL-skip tests continue to exercise the stamped path.
The usage cutover must select complete Claude snapshots before applying model and session filters, then credit the surviving tokens and fees to the original session. Otherwise delegated transcripts change dashboard totals, session counts, activity reports, and web-search costs when the shared Bun path replaces backend-specific SQL.\n\nKeep operational DuckDB costing aligned with that contract and remove renderer-only tests whose implementation no longer has a production caller.
Current main requires incremental session lookup to include the agent because one path can belong to multiple provider namespaces. Preserve the existing test intent by selecting the seeded Claude and Codex sessions explicitly.
3d56eec to
1f81c0f
Compare
roborev: Combined Review (
|
The concrete stores separately implemented sessions, project identity, curation, Recall, usage, and analytics, so fixes had to be repeated and snapshot semantics could drift. This layer makes the guarded BunStore the owner of those common behaviors while leaving adapters responsible for lifecycle and genuine engine capabilities.
Composite session pages, sidebars, message hydration, and timing reads now use replay-safe consistent views. Signal and trend analytics stream transcript content in bounded batches, and the dead PostgreSQL usage renderer is removed. Shared contracts preserve filtering, ordering, hydration, reduction, and supported mutations across real SQLite, PostgreSQL, and DuckDB stores. Stack 2 of 6.
generated by a clanker