Skip to content

[3306 by Claude Opus 5.5] Prevent delayed list search updates from overriding navigation during lazy route loading - #3308

Merged
integry merged 1 commit into
mainfrom
3306/claude-opus-5-5-prevent-delayed-list-sear-20261021-0406-ac6
Oct 21, 2026
Merged

integry merged 1 commit into
mainfrom
3306/claude-opus-5-5-prevent-delayed-list-sear-20261021-0406-ac6

Conversation

@propr-dev

@propr-dev propr-dev Bot commented Oct 21, 2026 •

Copy link
Copy Markdown
Contributor

AI Implementation Summary

Closes #3306

Branch: 3306/claude-opus-5-5-prevent-delayed-list-sear-20261021-0406-ac6
Commits: ✅ Changes committed (e497b46)


AI Processing Completed

Execution Details:

Summary:
Goals and Plans no longer pull navigation back while a lazy destination route is loading. With the fix, the issue's journey (Goals search → clear → More → Settings, with the Settings chunk delayed) stays on /settings and shows Settings.

Cause. React Router keeps the list on screen while the next page's code loads. Tasks already blocks its own late URL writes during this window (its shared setter in TaskList/hooks.ts), but Goals and Plans used the router's plain search-param setter. A search write that fired after the user tapped Settings was resolved against /goals (or /plans), which moved the user back. Goals hit this even after Clear: Clear writes the URL immediately, then the 400ms debounce writes it again.

Fix. Goals and Plans now use the same guarded setter as Tasks, so the cause is fixed in the shared helper rather than per page. I renamed it from useTaskSearchParams to useListSearchParams and updated its three existing callers and one test mock. A late list write is dropped if the route has changed or Back/Forward has moved history. Typing, the debounce timings, URL-backed filters, clear and focus, back/forward, task selection and lazy loading are unchanged. There are no sleeps, reloads or Settings-specific code.

Tests

  • Browser test (e2e/list-search-lazy-navigation.pw.ts): uses the production build and holds the real Settings chunk, with no fake API responses for the navigation itself. It runs at 320, 390 (touch, through More → Settings) and 1280 (sidebar link). For Tasks, Plans and Goals it checks two cases, a cleared search and a typed search not yet saved, plus a no-edit control. The test pauses the page clock across the edit and navigation, then runs it past every debounce while the chunk is still held. Without that, 390px passed on the old code only because the menu taps happened to beat the 400ms debounce. It also checks that Back returns to the list without the cleared search.
    • With the fix: 21/21 pass.
    • On the original Goals/Plans code: 9 fail at every width, ending on /goals (6) or /plans (3) instead of /settings. Tasks and the controls pass on both, as expected, since Tasks was already guarded and the control involves no edit.
  • HashRouter: the native HashRouter only exists in the desktop app build, so the browser tests can't reach it. A new unit test (src/components/TaskList/hooks.lazyRoute.test.tsx) runs the same case under both BrowserRouter and HashRouter with a held lazy route: 8/8 pass. With the old plain setter, 4 fail with exactly the reported URL.
  • Existing coverage: 3033/3033 unit tests pass, tsc and eslint . are clean, and 78/78 related browser tests pass (list search, mobile menu, keyboard viewport, Goals, timeline, follow-up, diff).

Previews. I captured Settings shown after a cleared Goals search with synthetic fixture data, at 320px and at desktop width. The red browser-push "HTTP 503" notice on the desktop shot comes from the test fixture, which doesn't fake that endpoint; it isn't a product error.

Detailed Logs:

  • Conversation: 169 messages
  • Session: e8792833-1c5b-403e-8954-3ca3bbb024f7

Log files stored at:

  • conversation: /usr/src/app/logs/executions/issue-3306-2026-10-21T04-40-38-785Z-conversation.json
  • output: /usr/src/app/logs/executions/issue-3306-2026-10-21T04-40-38-785Z-output.txt
Latest Conversation Messages
ASSISTANT: [content unavailable]

ASSISTANT: Goals and Plans no longer pull navigation back while a lazy destination route is loading. With the fix, the issue's journey (Goals search → clear → More → Settings, with the Settings chunk delayed) st...


This PR was created automatically by ProPR after processing issue #3306.


💡 Need changes?

Comment on this PR to request refinements — the AI agent monitors comments and will update the implementation based on your feedback. Keep iterating until you're satisfied!


Visual preview

320px phone: Settings opens after a cleared Goals search

320px phone: Settings opens after a cleared Goals search

Production build, trusted touch. Goals search typed, cleared, then More → Settings with the real Settings route chunk held and every search debounce elapsed meanwhile. Settings stays on /settings instead of returning to /goals. Synthetic fixture data.

Original unavailable: managed storage is unavailable or the upload has expired.

Desktop: Settings opens after a cleared Goals search

Desktop: Settings opens after a cleared Goals search

Same delayed-chunk flow through the sidebar Settings link at 1280px. The browser-push 503 notice comes from the fixture, which does not stub that endpoint.

Original unavailable: managed storage is unavailable or the upload has expired.

…on route is loading. With the fix, the issue's journey (Goals search → clear → More → Settings, with the Settings chunk delayed) stays on `/settings` and shows Settings.

Cause. React Router keeps the list on screen while the next page's code loads. Tasks already blocks its own late URL writes during this window (its shared setter in `TaskList/hooks.ts`), but Goals and Plans used the router's plain search-param setter. A search write that fired after the user tapped Settings was resolved against `/goals` (or `/plans`), which moved the user back. Goals hit this even after Clear: Clear writes the URL immediately, then the 400ms debounce writes it again.

Fix. Goals and Plans now use the same guarded setter as Tasks, so the cause is fixed in the shared helper rather than per page. I renamed it from `useTaskSearchParams` to `useListSearchParams` and updated its three existing callers and one test mock. A late list write is dropped if the route has changed or Back/Forward has moved history. Typing, the debounce timings, URL-backed filters, clear and focus, back/forward, task selection and lazy loading are unchanged. There are no sleeps, reloads or Settings-specific code.

Tests
- Browser test (`e2e/list-search-lazy-navigation.pw.ts`): uses the production build and holds the real Settings chunk, with no fake API responses for the navigation itself. It runs at 320, 390 (touch, through More → Settings) and 1280 (sidebar link). For Tasks, Plans and Goals it checks two cases, a cleared search and a typed search not yet saved, plus a no-edit control. The test pauses the page clock across the edit and navigation, then runs it past every debounce while the chunk is still held. Without that, 390px passed on the old code only because the menu taps happened to beat the 400ms debounce. It also checks that Back returns to the list without the cleared search.
  - With the fix: 21/21 pass.
  - On the original Goals/Plans code: 9 fail at every width, ending on `/goals` (6) or `/plans` (3) instead of `/settings`. Tasks and the controls pass on both, as expected, since Tasks was already guarded and the control involves no edit.
- HashRouter: the native HashRouter only exists in the desktop app build, so the browser tests can't reach it. A new unit test (`src/components/TaskList/hooks.lazyRoute.test.tsx`) runs the same case under both BrowserRouter and HashRouter with a held lazy route: 8/8 pass. With the old plain setter, 4 fail with exactly the reported URL.
- Existing coverage: 3033/3033 unit tests pass, `tsc` and `eslint .` are clean, and 78/78 related browser tests pass (list search, mobile menu, keyboard viewport, Goals, timeline, follow-up, diff).

Previews. I captured Settings shown after a cleared Goals search with synthetic fixture data, at 320px and at desktop width. The red browser-push "HTTP 503" notice on the desktop shot comes from the test fixture, which doesn't fake that endpoint; it isn't a product error.
@integry

integry commented Oct 21, 2026

Copy link
Copy Markdown
Owner

/review codex-astra

Please independently review issue #3306's navigation correction at exact head e497b46 against all original acceptance criteria. The observed release blocker is a real authenticated Goals search → clear → More → Settings journey returning to /goals while the real Settings JS chunk is delayed. A no-clear control reaches Settings; the failure persists with tab-only service-worker/cache bypass. This was independently reproduced by the operator before this PR.

Assess the shared useListSearchParams boundary, adoption by both Goals and Plans, existing Tasks/inbox selection consumers, BrowserRouter and native HashRouter, lazy transitions and history.Back/Forward. Preserve debounce/URL/filter/selection semantics, same-path selection writes, focus, unsent work and ordinary no-edit navigation. Verify no stale list update can retake navigation after another route begins loading. No hard reload, timer delay or Settings-only special case should hide the issue.

Inspect the new browser and unit regressions for a genuine old-code-failing/new-code-passing assertion, including 320/390 trusted touch, desktop, typed and cleared searches, held unchanged real lazy module, clock behavior and no-edit controls. Check all existing tests and modified mocks for waived coverage. The native HashRouter unit proof is not final installed native acceptance. Operator independent full Chromium/WebKit runs, units, old-code negative controls, native and authenticated final-build checks remain separate; do not infer them from author claims or static review.

No provider/model calls, permission changes or credential access are needed. Keep the independent mobile task-header overlap #3307 separate; this PR does not resolve it or authorize release publication. Independent exact-head operator validation and green required CI are needed before authorized merge.

@propr-dev

propr-dev Bot commented Oct 21, 2026 •

Copy link
Copy Markdown
Contributor Author

✅ AI Code Review Complete requested by @integry

Posted 1 review:

View Task Details

@propr-dev

propr-dev Bot commented Oct 21, 2026

Copy link
Copy Markdown
Contributor Author

🔍 AI Code Review — codex-astra

Overall Evaluation

The implementation is narrowly scoped and appears to fix the reported cross-route navigation race. No PR-introduced code blocker is demonstrated by the supplied diff. Merge readiness remains unverified pending independent exact-head operator validation and required CI completion.

✅ Shared navigation boundary — Goals and Plans now use the existing Tasks guard, which checks live router history before writing. Once history moves to /settings, a departing list’s setter refuses the write even while its component remains mounted.

✅ Selection semantics preserved — Tasks and inbox changes rename the hook without changing its implementation. Same-path replacement writes continue to merge current query parameters, preserving concurrent selection and filter changes.

✅ Focused regression coverage — Added tests cover pending typing, cleared searches, no-edit controls, and both router types. Browser tests hold the unchanged Settings module response and exercise mobile menu taps at 320/390 widths plus desktop navigation.

This is a static review only; no commands or tests were run. The supplied original issue description is truncated, so acceptance criteria beyond the visible specification cannot be assessed. The authoritative status reports zero failures and six pending checks. Authenticated final-build reproduction, Chromium/WebKit execution, old-code negative controls, installed native acceptance, and preservation of unsent work are not established by static inspection. Issue #3307 and release publication remain separate.

Merge blockers

No merge blockers.

Suggestions

These are optional follow-ups and are not sent to /fix.

S1: 🟢 Expand history transition regressions

The new browser helper checks Back only after Settings finishes loading; it does not exercise Forward or history traversal while the destination remains suspended. Additional focused tests would strengthen confidence in the live-history guard, particularly for Goals and Plans: their pending callbacks can be rescheduled when the setter changes, whereas Tasks explicitly retains the edit’s original callback. This is a coverage improvement, not a demonstrated PR-introduced defect.

S2: 🟢 Distinguish clear-test scheduling guarantees

The browser tests intentionally await Tasks’ clear debounce before navigating, and Plans clears both its input and debounced state immediately. Consequently, those clear cases do not establish that a pending clear writer was rejected; the pending-typing cases provide that coverage. Clarifying the comments would make the evidence easier to interpret. Goals’ clear case does exercise its queued debounced recommit. The renamed inbox mock preserves its existing refusal test rather than waiving an assertion.

Score

The code is focused and the regression design addresses the reported failure, but material independent runtime validation remains outstanding. Pending checks alone are not the reason for withholding a merge-ready assessment.

Score: 7/10


🤖 Review Details

  • Model: GPT-6 Astra
  • Time: 35s
  • Tokens: 45,992 (45,206 in / 786 out)
  • Cost: $0.38

View Task


💡 Next step: Comment /fix to address every F# merge blocker, /fix all for every pending blocker and suggestion, or name records explicitly, as in /fix F3 S5.
F# and S# IDs increment across review comments and remain permanent, so selectors such as /fix F3 F5 stay unambiguous across cycles. S# suggestions stay optional: they are implemented only when you name them or request /fix all, and they never relax a merge blocker.

Copy and edit to choose records from this review:

/fix S1 S2

🤖 Review by ProPR

@integry
integry merged commit 5cdac87 into main Oct 21, 2026
52 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Prevent delayed list search updates from overriding navigation during lazy route loading

1 participant