Reduce archived conversation disk usage with cold storage - #7386
Reduce archived conversation disk usage with cold storage#7386Quicksaver wants to merge 167 commits into
Conversation
- Preserve binary SQL values across archive round trips - Keep cold bundles authoritative until attachments restore safely - Bound restore memory and tolerate compatible schema changes
- Serialize archive-tree lifecycle and recheck archived shells - Restore cleanup-pending bundles before unarchive commits - Preserve retry state for writer and filesystem failures
- Reference-count archive-tree lock users and waiters - Remove lock entries after the final operation releases them
- Define the service members inline with Context.Service - Use the inferred Service type in the layer and orchestration test
- Match archived attachments by exact persisted ids - Resume cleanup pending manifests without shell rows - Preserve attachment metadata until durable delete cleanup succeeds
- Reuse archive filtering for project rows and navigation - Cover persisted and optimistic archive visibility
- Treat persisted shell data as fast-paint cache only - Resume WebSocket events from freshly loaded snapshots
- Describe cold archive, restoration, and deletion behavior - Record sidebar consistency requirements and development ports
- Document authoritative shell refresh, event replay, and deferred cache writes - Preserve mobile archive timestamp and shell subscription safeguards
- Query existing archive manifest columns - Derive chunk counts from the archive chunk table
- Scope the Effect Vitest extension to beta.78 - Regenerate the lockfile with updated peer resolution
…data-savings # Conflicts: # apps/server/src/server.test.ts # apps/web/src/components/Sidebar.tsx # apps/web/src/hooks/useThreadActions.ts # packages/client-runtime/src/rpc/client.ts # packages/client-runtime/src/state/shell-sync.test.ts # packages/client-runtime/src/state/shell.ts
Run preview cleanup only inside the locked archive eligibility boundary. Allow settled deletions without a provider binding while keeping active provider quiescence fail-closed. 🤖 Co-authored by GPT-5 in Codex via T3 Code
Release the pre-archive server preview lease on the authoritative unarchive transition when a fast restore makes archive quiescence skip. Keep stale queued archive work from closing newly active previews. 🤖 Co-authored by GPT-5 in Codex via T3 Code
- keep optimistic archive filtering under component-level coverage - preserve project location descriptions and search metadata 🤖 Co-authored by GPT-5 in Codex via T3 Code
- record location subtitle and search behavior - link focused optimistic archive regression coverage 🤖 Co-authored by GPT-5 in Codex via T3 Code
- share archive filtering with the command palette regression test - share project location metadata wiring with the production picker - document the extracted item-builder integration 🤖 Co-authored by GPT-5 in Codex via T3 Code
- apply the rendered Remote fallback to command palette search - cover undefined project locations in the merged seam test - document the fallback search contract 🤖 Co-authored by GPT-5 in Codex via T3 Code
|
@macroscopeapp Addressed the newly reported command-palette fallback-search blocker in 0d740e0. The rendered
|
🤖 Co-authored by GPT-5 in Codex via T3 Code
🤖 Co-authored by GPT-5 in Codex via T3 Code
🤖 Co-authored by GPT-5 in Codex via T3 Code
🤖 Co-authored by GPT-5 in Codex via T3 Code
- show partial archive load failures beside successful results - allow transient unarchive restore failures to retry - defer legacy compaction until lifecycle work drains 🤖 Co-authored by GPT-5 in Codex via T3 Code
🤖 Co-authored by GPT-5 in Codex via T3 Code
🤖 Co-authored by GPT-5 in Codex via T3 Code
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes using high effort and found 1 potential issue.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit 65c62f4. Configure here.
Keep the restore transaction and ownership handoff under one interruption mask so cancellation cannot roll accepted data back after SQL commits. 🤖 Co-authored by GPT-5 in Codex via T3 Code
|
Note 🤖 GPT-5.6 Sol responding on behalf of Theo Closing this PR after an automated pass over open pull requests. Adds a second SQLite database, archive compression, migration, and restore systems across 94 files. |
|
@t3dotgg could use an explanation for the close, that's just a list of spec changes the PR does 🙃 unwanted? superseded? redundant? |

Summary
With moderate use,
.t3can grow past 30 GB because archiving a conversation leaves its full event history, projections, diagnostics, and attachments in hot storage. Deleting conversations also leaves storage that needs later cleanup. Archived conversations still need to be recoverable, so simply discarding that data is not an option.This moves archived SQL rows and attachments into bounded, gzip-compressed chunks in a separate
archive.sqlitedatabase.state.sqlitekeeps only the archived thread shell needed by the Archive page and command receipts needed for idempotent retries. Unarchive restores the conversation before committing the domain command, while permanent deletion removes both hot and cold data.The result is much less hot-database growth without weakening diagnostics for active conversations. A durable lifecycle queue recovers interrupted work, reclaims freed pages, and keeps the server, web app, and mobile app in sync as conversations move between active, archived, restored, and deleted states.
Reopened
This reopens #4016. That PR was closed because its relationship to #2829 was only tangential. The behavior here is independent. This branch continues to track main, and #2829 can be integrated here later if it lands first.
Together with #7387 and #4384, this is useful now and does not need to wait for orchestration v2.
What changed
ThreadColdStorage, which writes thread-scoped SQL rows and attachments as validated, gzip-compressed chunks inarchive.sqlite. It preserves binary values, pages reads, accepts compatible older schemas, and rejects unknown tables, chunk kinds, or unsafe attachment names.state.sqlite; provider diagnostics and terminal logs are deleted rather than restored later.cleanup_pendingandrestoredmanifests preserve the operation boundary, abandoned reservations recover on startup, archive epochs prevent stale finalization from blocking a later archive, transient restore failures roll back partially restored tree members while remaining retryable with the same command ID, and interrupts cannot trigger rollback after an accepted receipt commits.t3-sqlite-state --database archiveas the supported inspection path for cold manifests and chunks.Validation
pnpm exec vp checkpassed with 9 pre-existing lint warnings outside the changed files.pnpm exec vp run typecheckpassed.pnpm exec vp run lint:mobilepassed; optional SwiftLint, ktlint, and detekt checks were skipped because those tools were not installed locally.ThreadDeletionReactortests, and 1 command-palette file with 2 tests.pnpm exec vp test run apps/web/src/contextMenuFallback.test.ts apps/desktop/src/electron/ElectronMenu.test.tspassed (2 files, 17 tests).pnpm exec vp test run apps/server/src/orchestration/Layers/OrchestrationEngine.test.ts apps/server/src/orchestration/ThreadColdStorage.test.ts apps/web/src/components/settings/ArchivedThreadsPanel.environment.test.tsxpassed (3 files, 41 tests).vp checkpassed for all 6 review-fix files; type-aware and type-check lint reported only one pre-existing string-spread warning inThreadColdStorage.ts.Note
High Risk
Touches orchestration commands, SQLite migrations, filesystem cleanup, and data recovery paths for archived threads—errors could cause data loss or stuck lifecycle state.
Overview
Archived conversations move off hot
state.sqliteinto gzip-compressed chunks inarchive.sqlite, with a durable background lifecycle queue and coordinated unarchive/restore semantics.ThreadColdStoragearchives thread projections, events, and attachments while keeping a lightweight archived shell and hot command receipts for idempotent retries; provider/terminal logs are dropped on archive rather than bundled.ThreadDeletionReactornow schedules archive, delete, and legacy-compaction jobs with provider/preview/terminal quiescence, coalesced retries, and startup rediscovery of pending work.OrchestrationEnginerestores (or rolls back) cold bundles aroundthread.unarchiveinside an uninterruptible commit boundary and retries bundle finalization on accepted receipt replay.Tooling and clients:
deriveServerPathsexposesarchiveDbPath;t3-sqlite-stategains--database archive(docs updated). Mobile archive list treats invalid archive timestamps as absent, sorts them last, and blocks duplicate unarchive via per-thread keys; mobile SQLite schema v2 clears persisted thread-detail cache on upgrade. Tests and harness layers wireThreadColdStorage(or no-op) across orchestration suites.Reviewed by Cursor Bugbot for commit d7b897c. Bugbot is set up for automated code reviews on this repo. Configure here.
Note
Add cold storage for archived conversations to reduce disk usage
ThreadColdStorageservice on the server that archives thread event trees to a gzip-compressedarchive.sqlite, restores them on unarchive with rollback on failure, and deletes them permanently onthread.deletedThreadDeletionReactorto process archive/delete as idempotent lifecycle jobs with quiescence checks and retry loops;OrchestrationEnginenow restores cold-storage data pre-commit beforethread.unarchivethreadCache.tswith generation tokens, eviction on archive, and revival on unarchive; the shell and detail state coordinate cache persistence and evict stale entries when threads disappear from snapshotsthread_archive_manifestsandthread_cleanup_queuetables and backfilling existing archived/deleted threads; bumps client IndexedDB to v5 and mobile SQLite to v2, both clearing cached thread data on upgradeArchivedThreadsPanelfor browsing/unarchiving, optimistic archive hiding viauseOptimisticThreadArchiveStore, sidebar/command-palette filtering,deleteArchivedThreadsflag inproject.delete, and--database archiveont3-sqlite-stateCLIthread_archive_manifestsfor all existing archived threads — servers with many archived threads will see a large pending queue on first boot. Thet3-sqlite-stateCLI now defaults--databasetostate; scripts inspecting archives must pass--database archive. Client DB upgrades from <v5 (web) or <v2 (mobile) clear all cached thread data, causing a one-time refetch.Macroscope summarized d7b897c.