fix(web): restore composer tasks after relaunch - #8097
Conversation
Kick off the approved pingdotgg#8091 plan with the regression-test slice first.
|
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:
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 |
Add the approved active-plan seam and a failing regression case for pingdotgg#8091. The test proves persisted unfinished tasks are currently hidden after process-local progress resets.
Derive the composer Tasks tab from persisted active-turn plan activities instead of process-local shell progress. Keep completed, settled, and superseded plans hidden.
Capture the restored task drawer from the isolated pingdotgg#8091 restart fixture. The asset-only pre-commit formatter has no supported target, so this commit bypasses that hook.
Remove the verification image from the branch tip because CI requires PR evidence to be hosted on GitHub rather than tracked. The PR references the immutable image commit.
Reproduce pingdotgg#8091 when 500 later activities push the active turn plan outside bounded thread-detail hydration.
Pin the latest unsettled-turn plan beside unresolved request activities so restart hydration still restores composer tasks after 500 later activities.
Assert that SQLite uses a plan-specific index for the single latest plan row needed by restart hydration.
Replace unbounded plan ranking with a single-row lookup backed by a partial thread-turn plan index.
There was a problem hiding this comment.
One finding: the new migration test targets a migration (042) that this PR never adds or registers, so the test and the index it asserts cannot exist.
Posted via Macroscope — Effect Service Conventions
Use one active-plan result for both the composer Tasks tab and in-chat Working label so their current-step rules stay aligned.
Drive composer task derivation from persisted activities at the ChatView seam and explain the production CTE so wiring and index regressions fail focused tests.
Document the Tasks tab, reconnect restoration, settlement behavior, and session-local dismissal.
ApprovabilityVerdict: Not approved Macroscope's review found this PR not approvable — This change crosses server lifecycle handling, bounded database hydration, a production index migration, and client task-state derivation. It alters how aborted turns are settled and how persisted plans are restored, so the runtime and state-management impact warrants human review. You can add or adjust custom eligibility rules. Learn more. |
Reproduce stale persisted task progress when turn.aborted clears only the in-memory plan registry and leaves the active session running.
Project an active turn.aborted event as an interrupted session with no active turn, while ignoring aborts for other turns. This prevents persisted task plans from lingering after abort or relaunch.
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 52a3944. Configure here.
Reproduce wrong-plan hydration when session.activeTurnId differs from latestTurnId, and prove an untargeted abort must not clear active plan progress.
Keep plan progress when an untargeted or foreign turn.aborted event is rejected, while matched aborts settle and clear the active turn. Cover late duplicates without polling.
Prefer session.activeTurnId while a session runs in both hydration and web derivation, falling back to latestTurnId only outside a running session.
Reproduce lost buffered assistant text and proposed-plan deltas when turn.aborted is the only accepted terminal event.
Treat an accepted turn.aborted as a terminal event for buffered assistant messages and proposed plans before clearing turn state.
Pass the resolved session-active Tasks turn into ChatComposer so session-local dismissal cannot target a stale latestTurnId.

What Changed
This draft restores the composer Tasks tab from the persisted plan for the active turn after T3 Code relaunches. Bounded thread hydration retains that plan through an indexed single-row lookup.
Why
The task plan survives a server restart as thread activity, but the composer also requires process-local plan progress. That temporary state starts empty after relaunch, so the active Tasks tab disappears while the turn continues.
Closes #8091.
UI Changes
There is no visual redesign. Both captures use the same disposable database with 501 activities.
The upstream base shows 500 retained tool activities and no composer Tasks tab. The fixed branch restores the sequence-1 plan and expands all three steps.
Scope and non-goals
Decisions
Implementation checklist
Verification checklist
Security and compatibility
The change adds no input, stored user data, network, authentication, or authorization behavior. It keeps the existing optional wire field for compatibility. Migration 42 adds only a partial SQLite index for the parameterized latest-plan lookup.
Deviations
turn.abortedevent cleared only process-local progress and could leave the persisted turn running; the plan now includes a lifecycle regression and fix.vp fmthad no supported target. Both code commits ran the hook normally.Checklist
Built with GPT-5.6-sol in the Codex harness.
Note
Fix composer
Taskstab to restore active turn plan after relaunchresolveActivePlanTurnIdandderiveActiveComposerTasksin session-logic.ts to compute Tasks drawer progress and steps from the latestturn.plan.updatedactivity, scoped to the active or running turn; hides the drawer once the turn settles or all steps complete.listPinnedThreadActivityRowsByThreadin ProjectionSnapshotQuery.ts to always pin the latestturn.plan.updatedfor the active or unsettled turn, even when it falls outside the recent-activity window.turn.abortedhandling in ProviderRuntimeIngestion.ts: ignores untargeted or wrong-turn aborts, sets session status tointerrupted, clearsactiveTurnId, finalizes assistant messages, and only clears plan progress when the lifecycle actually settles the active turn.projection_thread_activitiesforkind = 'turn.plan.updated'to speed up the plan activity lookup.ProviderRuntimeIngestion.acceptEventForThreadnow rejectsturn.abortedevents with a nullactiveTurnIdor a turn mismatch; callers relying on untargeted aborts being applied will see them dropped.Macroscope summarized 1e8276a.
Note
Medium Risk
Changes turn lifecycle handling for
turn.aborted(ignore untargeted aborts; interrupt and finalize the active turn) and the thread-detail activity pin query. Wrong matching would leave a turn running or drop the restored Tasks tab.Overview
Restores the composer Tasks tab after relaunch from the persisted active-turn plan, instead of relying on empty process-local shell progress.
Thread detail now pins the latest
turn.plan.updatedactivity for the unsettled turn (preferring a running session’sactiveTurnIdwhen it diverges fromlatest_turn_id) so the plan survives the 500-activity hydration cap. Migration 042 adds a partial index for that lookup.turn.abortednow settles only the matching active turn: session becomesinterrupted, buffered assistant text and proposed plans finalize, and plan progress clears. Untargeted or other-turn aborts are ignored.The UI derives composer tasks from that pinned plan via
deriveActiveComposerTasks/resolveActivePlanTurnId, hiding the tab when the turn settles or every step is complete.Reviewed by Cursor Bugbot for commit 1e8276a. Bugbot is set up for automated code reviews on this repo. Configure here.