Reduce archived conversation disk usage with cold storage#4016
Reduce archived conversation disk usage with cold storage#4016Quicksaver wants to merge 75 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
|
Important Review skippedAuto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Repository UI Review profile: CHILL Plan: Pro Plus Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
ApprovabilityVerdict: Needs human review Diff is too large for automated approval analysis. A human reviewer should evaluate this PR. You can customize Macroscope's approvability policy. Learn more. |
- Serialize archive-tree lifecycle and recheck archived shells - Restore cleanup-pending bundles before unarchive commits - Preserve retry state for writer and filesystem failures
There was a problem hiding this comment.
One Effect service convention issue found in the new ThreadColdStorage service. See the inline comment.
Posted via Macroscope — Effect Service Conventions
- 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
- Add an archived-only project deletion command option - Reject archived-only deletion when any live thread remains - Use the option when Sidebar V2 has no visible threads
- Clarify archived-only deletion predicates and confirmation copy - Cover live-thread rejection and optional contract compatibility
- Guard the decoded command before optional-field checks - Keep upstream project.delete payload compatibility covered
…data-savings # Conflicts: # apps/web/src/components/Sidebar.logic.test.ts # apps/web/src/components/SidebarV2.tsx
- Derive command options from each member's own live threads - Exercise mixed live and archived-only grouped removal - Keep branch behavior documentation aligned with the tested path
- Retry failed shell evictions while removed threads stay absent - Revive detail caches before persisting active snapshots - Cover reconnect and eviction retry paths
- Finalize matching restored bundles for accepted unarchive receipts - Cover transient finalization failure without duplicate events
- Exclude optimistically archived threads from project activity - Keep invalid mobile archive dates behind valid timestamps
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 3f385ab. Configure here.
- Reuse centralized thread visibility for palette navigation - Cover project selection with isolated optimistic archive playback
…data-savings # Conflicts: # apps/server/src/persistence/Migrations.ts # apps/web/src/components/SidebarV2.tsx
There was a problem hiding this comment.
One convention finding on the new ThreadColdStorage service. The prior run's ThreadColdStorageShape concern has been resolved — the service interface is now inline in the Context.Service declaration.
Posted via Macroscope — Effect Service Conventions
- Preserve the underlying failure in every cold storage error

Summary
This adds compressed cold storage for archived conversations. Once a thread is archived, its conversation data and attachments are moved out of the hot state database into a dedicated
archive.sqlitebundle, while unarchiving restores the complete thread tree before applying the domain command.The lifecycle is durable and restart-safe: migrations 035/036 queue existing archived and deleted threads, background work resumes interrupted archive/delete jobs, and restoration keeps the cold bundle authoritative until the unarchive command commits successfully. Attachment ownership is preserved from exact persisted IDs and cold-bundle filenames throughout archive, restore, and durable deletion.
Archived thread shells remain consistent across clients without relying on compacted event history: each WebSocket session reconciles an authoritative shell snapshot, then replays live changes without losing archive removals during the handoff.
What Changed
archive.sqlite.cleanup_pendingbundles, and prevented queued archive work or post-commit failures from undoing a successful restore.cleanup_pendingmanifests even when the archived shell row is missing, while preserving exact attachment metadata until durable delete cleanup succeeds.t3-sqlite-stateinspection path for both hotstate.sqlitedata and coldarchive.sqlitemanifests/chunks.Why
With fairly moderate usage, the user
.t3folder grew to more than 30 GB. That is far too much local data growth for normal use, and without lifecycle controls it gets out of hand very quickly. Archived and deleted conversations need to stop accumulating indefinitely in hot storage while remaining reliably recoverable when a user unarchives them.Validation
pnpm exec vp checkpassed with exit 0; it reported 9 pre-existing lint warnings outside the changed files.pnpm exec vp run typecheckpassed with exit 0.pnpm exec vp run lint:mobilepassed with exit 0; optional SwiftLint, ktlint, and detekt checks were skipped because those tools are not installed locally.@t3tools/client-runtimetypecheck passed with one pre-existing Effect suggestion insrc/relay/discovery.ts.src/orchestration/decider.ts.src/orchestration/decider.ts.No threads yet.Proof
No attached visual proof is necessary; the data lifecycle and sidebar interaction are covered by the automated and isolated Playwright validation above.
Note
High Risk
Large architectural change to conversation persistence, destructive hot-row moves, and unarchive/restore boundaries; failures are designed to be retry-safe but bugs could lose or strand archived data.
Overview
Cold storage moves archived conversation SQL, events, and attachments out of hot
state.sqliteinto gzip-chunked bundles inarchive.sqlite, leaving a lightweight shell for the Archive UI. A newThreadColdStorageservice handles archive, restore, rollback, finalize, and permanent delete, with per-tree locking,cleanup_pendingretries, and incremental vacuum on both databases. Migrations 035/036 seed manifests and delete queues for existing archived and soft-deleted threads;t3-sqlite-stategains--database archivefor inspection.Lifecycle expands
ThreadDeletionReactorinto a background queue for archive, delete, and legacy compaction: it stops providers/terminals/log writers before archiving, coalesces failures into delayed rescans, and keeps delete cleanup until filesystem work succeeds. Unarchive inOrchestrationEnginerestores (or hot-reserves) the cold bundle before the command commits, rolls back on dispatch failure, and finalizes the bundle only after success—including on receipt retry.Clients: web sidebar, command palette, and project removal use optimistic archive hiding and per-member
deleteArchivedThreads/forceoptions; mobile archive lists omit invalid timestamps, sort them last, and show unarchive-in-progress. Mobile and web clear stale thread-detail caches on schema upgrade. Provider NDJSON loggers addcloseThreadfor safe teardown during archive/delete.Reviewed by Cursor Bugbot for commit 219c60c. Bugbot is set up for automated code reviews on this repo. Configure here.
Note
Reduce archived conversation disk usage by introducing cold storage for threads
ThreadColdStorageLivelayer backed by the filesystem and SQLite that archives, restores, rolls back, and finalizes thread data, including provider event logs and attachments.thread.unarchivedispatches now restore the archived bundle before appending, roll back on failure, and finalize after a successful commit.ThreadDeletionReactorlifecycle job scheduling for archive, delete, and compact-legacy-storage operations with 30-second retry and deduplication.thread_archive_manifestsandthread_cleanup_queuetables and backfill pending entries for existing archived/deleted threads.project.deleteto acceptdeleteArchivedThreads: true, allowing deletion of projects that contain only archived shells without requiringforce.threadobject store cleared on upgrade.Macroscope summarized 219c60c.