feat: rework thread archive lifecycle with recovery and auto-delete - #436
Merged
Conversation
…delete Archived threads no longer keep their expanded worktree on disk. On archive the server captures the full working tree — including uncommitted and untracked work — into a per-thread checkpoint ref in the repository, then removes the worktree, so an archived thread costs no disk beyond its branch and one checkpoint commit. Unarchiving, or simply sending a message to an archived thread, materializes it back: from the legacy tar.xz when one exists, otherwise by recreating the worktree from the thread's persisted branch and overlaying the archived checkpoint so uncommitted work reappears as uncommitted work. - thread.turn.start on an archived thread emits thread.unarchived (matching the unsettle/unsnooze-on-activity behavior), and the turn execution boundary materializes the worktree before the session starts — previously the turn proceeded against a missing directory and failed downstream instead of recovering - snoozed threads are no longer auto-archived while the snooze is active (malformed or expired timestamps fall through safely) - new auto-delete janitor permanently deletes threads archived longer than ServerSettings.autoDeleteArchivedAfter (default six months, null disables), cascading through the normal thread.delete pipeline - WorktreeArchiveStore loses its tar-creation path (superseded by branch-based retirement in WorktreeLifecycle); restore/discard remain for archives created before the cutover - thread deletion now also cleans up the archive checkpoint ref
The Archive settings tab gains an Auto-delete section (toggle plus amount/unit editor, sharing a new durationWindowRow helper with the Auto-archive row) bound to the server's autoDeleteArchivedAfter setting, and a Delete All action that pages through the whole archived list and permanently deletes every archived thread after confirmation, tolerating per-thread failures.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Reworks what happens when a thread is archived, fixing three related problems:
refs/t3/checkpoints/<thread>/archive) in the repository, then removes the expanded worktree withgit worktree remove. An archived thread now costs no disk beyond its branch and one checkpoint commit.thread.unarchivedfirst (matching the existing unsettle/unsnooze-on-activity behavior), and both the unarchive reactor and the turn-execution boundary materialize the worktree — from the legacy tar.xz when one exists, otherwise by recreating it from the thread's persisted branch and overlaying the archived checkpoint so uncommitted work reappears as uncommitted work.It also adds archive retention controls:
ServerSettings.autoDeleteArchivedAfter(milliseconds ornull; default six months = 183 days,nulldisables) and a new hourly auto-delete janitor that permanently deletes threads archived longer than the window, cascading through the normalthread.deletepipeline (session stop, terminal close, worktree removal, checkpoint-ref cleanup).durationWindowRowhelper with the Auto-archive row) and a Delete All button that pages through the whole archive and permanently deletes every archived thread after confirmation.WorktreeArchiveStoreloses its tar-creation path (superseded by branch-based retirement);restore/discardArchiveremain so tarballs created before this change still restore. Thread deletion now also cleans up the archive checkpoint ref.Area
apps/mac— native macOS appapps/windows— Windows desktop appapps/mobile— iPhone companion appapps/server— backend serverpackages/contracts,packages/shared,packages/client-runtime, …) or relay (infra/relay)Release size
size:XS— tiny internal, documentation, or narrowly scoped fixsize:S— small user-visible fix or contained improvementsize:M— normal feature or meaningful behavior changesize:L— broad feature or substantial cross-package changesize:XL— release-system, architecture, or ecosystem-scale changeVerification
pnpm run verify --allpasses (check + typecheck + tests; addslint:mobileand the Swift suite) — 5/5 steps. Note: the Swift step flaked twice inSurgeCodeMacTestsunder load on earlier runs and passed on rerun with no changes; appears to be a pre-existing flake unrelated to this diff (the archive/settings Swift coverage added here passes consistently).pnpm run test:macpasses (required forapps/macchanges) — 1158 tests across all three targets.New/updated automated coverage:
WorktreeLifecycle.test.ts(real-git): retire deletes the worktree and materialize recreates it from the branch with uncommitted + untracked state restored as uncommitted state; capture failure keeps the directory; missing branch/dir and unmanaged paths are safe no-ops.WorktreeArchiveStore.test.ts: legacy tar.xz restore/discard and managed-path guards.decider.archive.test.ts:thread.turn.starton an archived thread emits[thread.unarchived, thread.message-sent, thread.turn-start-requested].AutoArchiveJanitor.test.ts: active/expired/malformed snooze eligibility.AutoDeleteJanitor.test.ts: window boundary, malformedarchivedAt, null setting, per-thread error isolation.settings.test.ts/serverSettings.test.ts: 183-day default, explicitnullsurvives, patch merge semantics.AutoDeleteArchivedSettingsCodingTests.swift: wire coding of the new setting and patch (present/null/missing; explicit-null disable).restore --sourceoverlay +clean+resetin a recreated worktree) were also verified end-to-end in a scratch repository.Screenshots / Recordings
Not captured — headless agent environment without a running app instance. The UI reuses the existing Auto-archive settings row pattern (
SettingsToggleRow+ a shared amount/unit editor) and the existing destructive-action alert idiom.Release notes
Archived threads now free their worktree disk space and recover automatically when messaged or unarchived; snoozed threads are no longer auto-archived; and archived threads older than six months are auto-deleted (configurable or disableable in Settings → Archive, alongside a new Delete All action).