Repository navigation
fix(mobile): finished threads no longer stay hidden in the Working shelf - #28224
juliusmarminge wants to merge 2 commits into
Conversation
…shelf Renders useThreadListV2 through the React Compiler (react test project), moves a thread from running to finished, and advances past the exit grace. The tracker reports the thread as no longer working, but the hook keeps the stale observation because the compiled memo ignores the inbox version, so the thread stays inside the collapsed Working shelf. Fails on main. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…grace The React Compiler memoizes on what a memo callback uses, so it dropped the inbox tracker version from the observation's cache guard and the grace timer's re-render reused the stale observation. Pass the version into the observation so the compiled guard depends on it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
ApprovabilityVerdict: Approved at Macroscope's review found this PR approvable — The change refreshes mobile thread placement when the existing Working-shelf grace period expires, preventing finished threads from remaining hidden. Its effects are limited to list presentation, with no changes to product defaults or persisted thread state. You can add or adjust custom eligibility rules. Learn more. |
|
No actionable comments were generated in the recent review.
|
| Layer / File(s) | Summary |
|---|---|
Refresh inbox observation with the inbox version apps/mobile/src/features/threads/useThreadListV2.ts, apps/mobile/src/features/threads/useThreadListV2.test.tsx, apps/mobile/vite.config.ts |
The hook passes inboxVersion through a local observation helper. The regression test checks the working count before and after WORKING_EXIT_GRACE_MS, and the Vite React test list includes the test. |
Estimated code review effort: 2 (Simple) | ~10 minutes
Change: Bug fix · Severity of issue fixed: Medium
Possibly related issues
- Issue 24901 describes a completed thread that remains in the mobile Working shelf after the exit-grace period. This change tests that behavior and updates inbox observation when its version changes.
Possibly related PRs
- pingdotgg/t3code#24907: Adds the version argument to
threadListInboxReturns.observe; this change routes the version through the hook’s observation helper. - pingdotgg/t3code#28169: Fixes stale Working-shelf observation after the grace period; this change adds regression coverage for that behavior.
- pingdotgg/t3code#25400: Passes
inboxVersionintoobserve; this change uses that input in the hook’s memoized observation path.
Suggested reviewers: t3dotgg
Merge Risk: 
Minimal · up to 28433
-
The change refreshes the Working-shelf observation after the grace timer. No merge-blocking risk was identified.
Pre-merge checks: 
3 · 
1
-
Failed checks: 1 warning Passed checks: 3 passed-
Full details: Description check
-
Explanation
The description includes all template sections. It gives a specific problem and reproduction, explains the implementation, links the issue, documents focused tests and observed results, includes UI evidence, and lists verification limits.
Full details: Out of Scope Changes check
-
Explanation
All changed files relate to one objective: the finished-thread Working-shelf return after the exit grace. useThreadListV2.ts changes only the inbox observation call and its memoization input, which removes the stale observation. The new test in useThreadListV2.test.tsx covers that path (running to completed, then past WORKING_EXIT_GRACE_MS). The vite.config.ts change adds the new React test file to the reactTests list, so it runs under the react project. No unrelated refactoring, dependency change, or web Sidebar change appears in the diff. The change set is small and matches the stated scope of the PR description. The unchanged web Sidebar is not an out-of-scope change.
-
Full details: Linked Issues check
-
Explanation
Directly linked issue
#24997requires the mobile thread list to match the desktop sidebar for the same environments. Reported symptoms: recent threads from two online environments are missing from the list, including threads created on the phone. Force-quit does not fix it. Toggling an environment fixes it. The reporter suspects a saved list copy is kept instead of the full loaded snapshot. The PR diff (merge base 15fe283 to head 2843373) changes only useThreadListV2.ts: an observeInboxReturns helper that forwards an unused _version argument so the React Compiler re-observes the Working-shelf inbox tracker after the exit grace timer. It also adds a test in useThreadListV2.test.tsx and registers that test in apps/mobile/vite.config.ts. None of these changes touch shell snapshot loading, environment subscriptions, or the thread list data source. The fixed defect is a finished thread that stays hidden in the collapsed Working shelf after WORKING_EXIT_GRACE_MS. The issue's missing-thread reports do not mention the Working shelf. The PR's own description limits its claim to that narrower case and says it does not verify the multi-day, multi-environment scenario. The PR still states 'Fixes #24997'. The Working-shelf fix may be a valid, separate fix, but the reviewed evidence does not show it resolves the issue's listed requirement.Resolution
Choose one of two paths. (a) Change the PR to reference only the Working-shelf defect, for example 'Relates to
#24997' or no closing reference, so the linked issue is not claimed as fixed. (b) Keep 'Fixes#24997' and add coding changes and automated tests for the reported behavior. Those tests must show that threads from multiple online environments, including threads created from mobile, appear in the Home and sidebar lists after a shell snapshot or subscription interruption, without an environment toggle. Also show that the list does not keep a stale saved copy in place of the loaded snapshot.
-
Finishing Touches
-
Generate docstrings
-
- Commit to this branch
- Create a new PR
Generate unit tests (beta)
-
- Commit to this branch
- Create a new PR
-
- Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts
Integrate upstream PR pingdotgg#28224, including its React Compiler regression test. Confirmed that the test fails before the fix and passes afterward.
|
Note 🤖 GPT-6.1 Terra [1m] responding on behalf of Theo Closing because main passes the grace-expiry version into the mobile inbox observation, so finished threads leave Working when the timer fires. #28169 useThreadListV2.ts |
Problem
On mobile Home (iOS and Android), a thread whose run finishes stays hidden inside the collapsed Working shelf instead of returning to the list after the 2-second exit grace, so a thread you just created from the phone appears to vanish until the app is relaunched or something else rebuilds the list. Reproduced on main (15fe283272) in the T3 Code dev client on an iPhone 17 Pro simulator against a fresh dev server with Codex: New chat → Demo → send "Reply with the single word echo." → Back to Home while it runs. The server reports the run completed, but Home keeps showing
Working (1)with the thread hidden for as long as nothing else changes (40+ s in the capture below).Change
useThreadListV2memoizedthreadListInboxReturns.observe(...)withinboxVersionin its dependency list, but the callback never used the version. The React Compiler memoizes on the values a callback uses, so the compiled cache guard wasif ($[0] !== threads || $[1] !== workingShelfEnabled): when the grace timer fired and bumped the tracker version, the hook re-rendered with the stale observation that still said "working". The hook now passes the version into the observation call, so the compiled guard isif ($[0] !== inboxVersion || $[1] !== t0)and the timer's version bump re-observes. Web's Sidebar already passes the version into its partition (clockKey) and is unchanged.Scope and approval
Fixes #24997
Verification
vp test run --project react src/features/threads/useThreadListV2.test.tsx(apps/mobile): the new test renders the real hook through the React Compiler, finishes a Working thread and advances pastWORKING_EXIT_GRACE_MS. On main it fails withthe list still shows the finished thread as working: expected 1 to be +0while the tracker itself already reports the thread as not working; it passes with the fix.vp run check:changed: format, lint, knip, typecheck (2 packages), the test beside the change and the React Compiler check pass.useThreadListV2.tswith the mobile Babel config: beforeif($[0]!==threads||$[1]!==workingShelfEnabled), afterif($[0]!==inboxVersion||$[1]!==t0).Evidence
Real T3 Code Dev client on pool simulators/emulators, fresh dev server and database per side (
T3CODE_DEV_SEED=0, own--home-dir), Codex GPT-6-Astra, same native fingerprint on both sides; each side's JS from its own checkout's Metro. Recordings are timestamped screenshot sequences (~1.5–2 s apart), no touch on the list between completion and the return. Data is synthetic: a Demo project with one finished "Older inbox" comparator thread.R1: iPhone 17 Pro, portrait, dark, Home. Before: 17:08:16 send "Reply with the single word echo.", 17:08:17 Back; the completed thread stays under
Working (1)through 17:09:08. After: 17:10:58 send, 17:11:00 Back; Echo appears above Older inbox and the Working header disappears by 17:11:06 with no input.What else differs in the R1 endpoints: the intended Echo row (Done badge) at the top with the rows below shifted down and
Working (1)removed, and New thread's age label (4m vs 6m, wall-clock). The top 50 px status bar (clock, battery fill) is masked in both diffs; with it masked, 4,570 of 351,348 pixels differ at zero threshold and 3,122 above 16, all in the list rows.The GIFs show sampled frames with the time each screenshot returned; frame delays are not exact wall-clock intervals. The R1 after run lost the device connection from 17:11:16 to 17:11:34 after the return, so that stretch has no frames; Echo had already returned at 17:11:06.
After-only rows, head 2843373:
Working (1), then returns to the top on its ownThreadNavigationSidebar), target unselectedWorking (1)disappears and the thread returns on its ownWorking (2)→Working (1): the finished thread returns, the running one staysDriver and seed used for the captures (scratch, not committed)
Not checked:
Models and harnesses: Claude Opus 5.5 through Claude Code (lead, test, implementation, review B); GPT-6.1-Sol through Codex in T3 Code (review A, proof audits, captures).