Skip to content

Thread lifecycle: make Settled a real state and remove auto-archive #526

Description

@Tryanks

Summary

Make Settled a real lifecycle state, following T3 Code's design, and remove auto-archive. Settled becomes the only automatic lifecycle transition; archive is always an explicit user action.

Why

Today Settled (#408) has almost no effect:

  • It is manual only (settle_session / make_session_active, crates/runtime/src/app/sessions.rs).
  • Its only behavioural effect runs the wrong way: settled threads are exempt from auto-archive (auto_archive_candidates, crates/core/src/project.rs) and from orchestrate archive_on_complete, so finished work lives longer than untouched work.
  • settled_at is only a cascade-undo key; nothing sorts by it and the Settled group has no paging.

Auto-archive is removed because archive is becoming destructive in part (it will move logs, call provider-native archive and clean up worktrees; see #529 and #530). The rule is: automatic transitions must be losslessly reversible. Settle is; archive no longer is. Neither T3 Code nor the Codex app auto-archives threads. Sidebar length is handled by the Settled shelf; disk growth by storage compaction and cold compression, independent of archive.

Spec

State. SessionMeta gains settled_override: Option<Settled | KeepActive> (replacing the bare settled_at flag), settled_at, unsettled_at. KeepActive is the user's "keep active" choice and suppresses auto-settle. Snooze (#527) and pinned (#528) are separate issues.

Last activity = the latest of: last user message, and the latest turn's requested / started / completed times. A thread that never ran never inactivity-settles.

Auto-settle when last activity is strictly older than N days. The auto settled_at is the activity time, not now.

Never auto-settle a thread that: is archived; has any override; has pending approvals or user input; has a starting or running session; has background work; has a user message still inside a 2-minute queued-turn grace window; is snoozed (unless it woke on an error or completion after the snooze). Linked-PR rules are in #534.

Automatic un-settle (resets any override, KeepActive included, to none):

  • the user sends a message (also clears snooze);
  • the session moves to starting or running;
  • an approval or user-input request arrives.

Nothing else wakes a settled thread.

Settle stops the provider session, as in T3 Code. A thread idle for days has no warm prompt cache left (see #531), so stopping costs nothing and frees memory. The stop is dropped if the thread was re-engaged in the meantime. Manual settle keeps refusing while the thread family is busy.

Host sweep. The host evaluates auto-settle every 60 s, and immediately on settings changes and turn ends. It must not depend on a client: today auto-archive only runs when a desktop sidebar triggers it (crates/ui/src/sidebar.rs), which violates Principle 2.

Settings. auto_settle_after_days: Option<u32> (default 3, range 1–90, None = never), per-project overrides stored in host settings keyed by project id. Changing them does not reopen already-settled threads.

Sidebar.

  • Section order: pinned, active, snoozed, settled.
  • Settled shelf starts collapsed; shows 10 rows, then 25 more per "Show more"; sorted by settled_at desc, then last activity, then updated_at.
  • Reopened threads enter the top of the active list via unsettled_at; later activity does not reorder the active list.
  • The partition and sort live in core so desktop, phone and browser share them.

Undo. Settle shows a 5-second toast with Undo (and mod+z when no text field or terminal has focus). Undo is a client-sent reverse command; a claim token stops a stale toast from reversing a newer action. The same toast serves snooze, unpin and archive.

Protocol. SettleSessions, UnsettleSessions (keep-active), settle-related SettingsPatch fields; events carry the reason (user vs activity).

Removing auto-archive

  • Remove auto_archive_disabled, auto_archive_max_idle_days, auto_archive_keep_count (crates/core/src/settings.rs), auto_archive_candidates, Command::AutoArchiveSweep and the sidebar-triggered sweeps.
  • Threads already auto-archived stay archived.
  • Open question: orchestrate archive_on_complete is an explicit per-orchestration choice (comparable to the Codex app archiving automation threads on completion). Keep it, but settled should no longer exempt a child from it.

Related work

  • Add support for settling threads #408 introduced the current manual settle.
  • guivieiras/tcode has unmerged work on local/integration touching the same surface: last_user_message_at and a "last user message" thread sort (45e74fc), thread state presentation and completed markers (874fefd, 9cd8805), and "settle actions" refinements (cf68c7e). It also replaces the unread dot with a "Mark completed" marker, which changes what the "unread" exemption means here. 874fefd rewrites ~1.2k lines of crates/ui/src/sidebar.rs into a sidebar/thread_row/ module, so the sidebar parts of this issue, Thread lifecycle: snooze #527 and Thread lifecycle: pinned threads #528 should be sequenced with it. The last-activity definition above should reuse last_user_message_at if that lands.

Reference

T3 Code origin/main f5ef0ddb9: apps/server/src/orchestration/ThreadSettlementPolicy.ts, ThreadSettlementReactor.ts, decider.ts (settle/unsettle), packages/contracts/src/settings.ts, docs/user/thread-sidebar.md; history in #4026, #4243, #8600, #9254, #12848.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions