Sidebar v2 beta: flat thread list with a server-backed settled lifecycle - #4026
Merged
Conversation
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.
What
Opt-in Sidebar v2 (web) and Thread List v2 (mobile): one flat thread list ordered by creation time, replacing the project-grouped sidebar. Active threads render as status cards; finished work "settles" into a slim, paged tail at the bottom. Threads settle manually (hover ✓ / context menu / swipe), or automatically when their PR merges/closes or after N days of inactivity — and un-settle automatically on real activity (a user message, a session coming alive, a new approval/user-input request).
Settled is a first-class server-side lifecycle (this PR absorbed #4243):
thread.settle/thread.unsettlecommands,thread.settled/thread.unsettledevents, andsettled_override+settled_atcolumns onprojection_threads(migration 033). Settled ≠ archived — settled threads stay in the live shell stream, so their history stays readable without un-settling, and archive keeps its original "hidden from lists" meaning.BEFORE:

AFTER:

MOBILE:

Safety / gating
sidebarV2Enabled(web, Settings → Beta) andthreadListV2Enabled(mobile device preference). Flags off, v1 renders unchanged.PRAGMA-guarded); no backfill, no data rewritten. Threads archived before this lands stay archived.threadSettlementcapability. Clients never send the new commands to a pre-settlement server (absent capability = unsupported), and threads on such environments never classify as settled — no stranded tails with dead affordances.canSettle(running/starting sessions, pending approvals/user input, queued turn starts), the same blockers ineffectiveSettled(auto-settle can't misclassify either), and a server-side decider invariant for raced or stale clients.What was touched / review focus
Server (
apps/server):orchestration/decider.ts— settle/unsettle command handling, the live-session + queued-turn invariants, and the three activity-un-settle injection points (message send, session set, activity append). Real activity clears ANY override: settled threads wake, keep-active pins reset to neutral. Re-emissions are projected no-ops (idempotency withoutupdatedAtchurn). Best place to start.orchestration/projector.ts+Layers/ProjectionPipeline.ts— event application:reason: "user"→ the keep-active pin,reason: "activity"→ neutral.persistence/Migrations/033_ProjectionThreadsSettled.ts+ row plumbing.Shared:
packages/client-runtime/src/state/threadSettled.ts—effectiveSettled/canSettle/hasQueuedTurnStart, heavily tested (172 tests incl. a truth table over all override states).packages/contracts— commands/events (client-dispatchable unsettle reason is"user"only;"activity"is server-owned),threadSettlementcapability, two client settings.Web (
apps/web):components/SidebarV2.tsx— the new sidebar. Review focus: post-settle navigation (next card / project draft, validated against the current route), per-thread in-flight guards, capability-aware partition, bulk actions over exactly the rendered selection.hooks/useThreadActions.ts— settle/unsettle dispatch + typed errors.sidebar/SidebarChrome.tsx(v1 and v2 render identical header/footer); new/settings/betapage; minor chat-header/composer tweaks.Mobile (
apps/mobile):features/threads/threadListV2.ts+thread-list-v2-items.tsx+HomeScreen.tsx— the same model with native list anatomy (swipe actions, long-press menus, settled tail paging, ticking auto-settle clock).Known follow-ups (beta-period, not blockers)
Mobile split-view v2 wiring, mobile PR auto-settle without virtualized row mounts, a11y polish (composite row labels, ARIA patterns), v2 search parity with project titles, and the auto-settle policy knob on mobile.
🤖 Generated with Claude Code
Note
Add flat thread list Sidebar v2 with server-backed thread settle/unsettle lifecycle
SidebarV2on web and a thread list v2 on mobile, both rendering threads sorted by creation time with active threads as cards and settled threads as a paginated tail; enabled via a new Beta settings page (/settings/beta) guarded by asidebarV2Enabledclient setting.thread.settleandthread.unsettlecommands andthread.settled/thread.unsettledevents to the contracts, with full decider enforcement (no live session, no open blocking requests, no queued turn start within a grace window) and idempotent re-emit on already-settled threads.settledOverride,settledAt) through the server projection pipeline, persistence layer (migration 033 addssettled_override/settled_atcolumns toprojection_threads), in-memory projector, client-runtime reducers, and shell/detail merge.settleThread/unsettleThreadinuseThreadActions(web) anduseThreadListActions(mobile), gated by a newthreadSettlementenvironment capability flag; mobile also adds swipe-to-settle gesture support.thread.unsettledwith reasonactivitybefore the triggering event.threadSettlementcapability, so clients silently suppress settle/unsettle actions on those environments.Macroscope summarized cb57915.
Note
Medium Risk
Touches orchestration command invariants and projection schema with new user-visible lifecycle behavior, but it is gated behind beta flags default-off and capability checks with extensive test coverage.
Overview
Introduces thread settlement as a server lifecycle distinct from archive:
thread.settle/thread.unsettlecommands, matching events, and nullablesettled_override/settled_atonprojection_threads(migration 033). Settled threads remain in the live shell stream; the decider blocks settling during active sessions, open approval/user-input requests, and queued turn starts, and prependsthread.unsettledon real activity (messages, live session updates, blocking activities). Servers advertisethreadSettlementso older environments never get settle commands or settled classification.Web adds beta Sidebar v2 (
sidebarV2Enabled, Settings → Beta): creation-order flat list, active cards vs paged settled slim rows, auto-settle via inactivity days and merged/closed PR signals, with shared chrome extracted from v1. Mobile mirrors the model behindthreadListV2Enabledin device preferences—FlatListv2 path, horizontal project scope chips, settle/unsettle inuseThreadListActions, and swipe full-swipe commits the primary lifecycle action (settle/un-settle) instead of delete.Shared
effectiveSettled/canSettlein client-runtime drive partitioning; thread shells and tests across apps pick upsettledOverride/settledAtfields.Reviewed by Cursor Bugbot for commit cb57915. Bugbot is set up for automated code reviews on this repo. Configure here.
Summary by CodeRabbit