Repository navigation
fix(web): mark new background completions unread - #10077
juliusmarminge wants to merge 7 commits into
Conversation
…completions-unread
…t-background-completions-unread
Thread transfer impact✅ Thread transfer remains within every enforced ceiling.
Baseline: Scenario and decoded snapshot size10 historical turns, 5 command tools per turn, 878.9 KiB retained MCP result per historical turn, and a 1.05 MiB retained result in the measured turn.
Updated in place by a trusted workflow. PR artifacts are strictly validated and never executed. |
ApprovabilityVerdict: Not approved Macroscope's review found this PR not approvable — This PR adds an always-on observer that changes and persists unread state for background completions across environments and route transitions. Although the focused test coverage is extensive, the new lifecycle and bootstrap read-state policy represent nontrivial user-visible behavior that warrants human review. Notes:
You can add or adjust custom eligibility rules. Learn more. |
Dismissing prior approval to re-evaluate 429a878
|
Thanks for working on this. We merged the orchestrator V2 rewrite in #2829, and we are closing this PR as part of that transition. This PR adds live completion detection based on thread.latestTurn.state and completedAt. V2 completion state lives on latestRun/runs, so the new unread observer is built against the retired snapshot shape. Sorry for the extra work this creates. If the change is still needed on V2, please rebuild it on current main, verify it there, and open a new PR linking back here. We're closing the current implementation without assuming the underlying request is resolved. |
Closes #3131.
New background threads can finish without showing an unread completion indicator. The current selector treats a missing visit timestamp as read, even when the client received the completion without opening the thread.
This observes each environment's live shell snapshots. The first live snapshot establishes a baseline without creating unread markers for previously untracked completions. Later new or changed completions receive the existing unread timestamp only when no visit entry exists. Existing unread markers, real visits and manual read/unread state remain unchanged. Updates use one lazy visit-map copy and one store notification per snapshot, with the existing persistence debounce.
Closes #3131. This is a credited replacement for Wout Stiens's #3235, which the author closed and maintainers cannot edit. It adapts that proposal to the current live/cached shell lifecycle rather than restoring the removed visit-seeding code.
Human decision required
Keep this unmerged until the bootstrap/read-state policy is approved. The first live snapshot does not retroactively mark previously untracked completions unread; existing unread markers survive reload. Ordinary disconnect/reconnect retains the observer's baseline, so later completion changes can still become unread, including in a full resynchronization snapshot. Recreating the observer through page reload, environment removal/re-addition or disabled-to-enabled auth state establishes a fresh historical baseline. Observation remains mounted across eligible hosted connect routes, but stays disabled for unauthenticated and hosted-pairing states. This PR does not change the default unread selector, persisted schema, mobile status policy, providers, or wire protocol, and does not close the issue automatically.
Verification
The native-before used main eced382b. The after integrated candidate 2e1a60c, based on 4631000f, into the retained test app with backend 5a4287cd. The relevant root, store, shell and sidebar behavior matched those bases. Unrelated held audit integrations were present. The normal merge of main 25cbcd62 initially left all four proposal files byte-identical to that tested candidate. The later route-lifetime correction changes hook ownership and auth gating, not the visit-map or ordinary completion policy.
Before and after use separate fresh threads following the same flow, case 2 before and case 3 after. They are not the same thread replayed. Electron, macOS, mobile and a deployed hosted client were not exercised. Hosted and multi-environment lifecycle coverage is at the shared runtime/Registry boundary.
An additional ordinary-client control integrated the exact e36ed081 route correction. A fresh case 4 completed in the background at September 5, 2026,09:20:13.637UTC; Done appeared with a persisted visit timestamp one millisecond earlier, survived reload, and cleared on actual open. Historical and previously visited threads remained unchanged. Its local capture contains a fixture-health banner and is not published. The later main merge preserves the upstream welcome wizard and changes the root only by adding the observer import/call; welcome-route verification is the lifecycle test above, not a native wizard walkthrough.
Before: the newly completed background thread has no Done indicator.
After: a new background completion shows Done while another thread remains selected.
Control: opening the completed thread clears Done normally.
Continuous four-second client clip showing the background thread's arrival and Done indicator. This clip does not show the separate reload or open controls and is not a latency benchmark.
GPT 6 Astra via Codex in T3 Code.
Note
Mark newly completed background turns as unread in web UI
subscribeToUnvisitedCompletionsin useUnvisitedCompletionsUnread.ts which subscribes to each catalog environment's shell atom, takes a live snapshot as the baseline, and persists unread markers for threads that complete after that baselineuseUnvisitedCompletionsUnreadReact hook and wires it into __root.tsx for authenticated or hosted-static sessions; subscriptions are disposed on unmount or disablegetUnreadVisitedAtin uiStateStore.ts to validate completion timestamps and compute a visited timestamp one millisecond before completion; invalid or missing timestamps return null and skip markingMacroscope summarized 429a878.