fix(mobile-inbox): clear rows on tab switch and derive actions from row.kind (#2766) #2774
Reference in New Issue
Block a user
Delete Branch "fix/2766-mobile-inbox-wrong-action-race"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Fixes #2766.
Bug
RequestsInboxcould render stale approval rows under the Tasks tab while the task-list fetch was still in flight. Because the primary action was derived from the selected tab (kind), an approval row shown during this race exposed a "Done" button that would POSTaction=doneto/requests/{id}/respond.Fix
itemsimmediately whenkindchanges, before the new/requests/pending?kind=...fetch resolves.row.kindinstead of the selected tab, so an approval row can never be actioned as a task even if it somehow renders under the wrong tab.Test plan
npx vitest run src/components/concierge/__tests__/RequestsInbox.test.tsx— 13/13 pass.npx vitest run src/components/concierge— 18/18 pass.Added regression tests covering the tab-switch clear and row-kind-derived actions.
5-axis review on current head
1f25de7504: REQUEST_CHANGES.Correctness blocker: the delayed-fetch tab-switch race is still open.
load()clearsitemsbefore starting the currentapi.get, but it does not cancel or ignore stale responses from an earlierkind. If the user switches Approval → Task while the approval fetch is still pending, the task load clears the list, but the older approval promise can resolve afterwards and callsetItems(list), repopulating the Tasks tab with approval rows.Deriving the primary action from
row.kindcorrectly preventsaction="done"on an approval row if that stale row renders, so that part of #2766 is fixed. But the PR also claims stale/wrong-kind rows are cleared on tab switch and the delayed-fetch race is closed; the current implementation only clears currently rendered rows, not late old responses.Please gate
then/catchon the request's captured kind / sequence id, or abort/ignore stale loads in cleanup, and add a regression where the old approval fetch resolves after switching to the Tasks tab and must not render the approval row. Security/performance/readability are otherwise fine; scope is narrow and no new auth/secret surface found.REQUEST_CHANGES on head
1f25de75.The row-kind action derivation is the right direction:
RequestItemnow derives approval/task layout and primary action fromrow.kind, so an approval row that slips through no longer postsaction=done. However, the stale wrong-kind row race is not fully closed.load()clears the currently rendered rows before starting the new/requests/pending?kind=${kind}request (RequestsInbox.tsx:126-143), but it does not cancel or sequence-guard the previous tab's in-flight request. Repro shape: render Approvals with a delayed/requests/pending?kind=approval; switch to Tasks before that approval request resolves; the Tasksload()clears rows and starts the task fetch; then the old approval promise resolves and callssetItems(list)from its stale closure. That can still render an approval row under the Tasks tab until the task fetch wins. Because actions are now derived fromrow.kind, the row shows Approve rather than Done, but it is still a stale wrong-kind row visible/actionable under the wrong tab, which misses the "stale wrong-kind row can't be actioned" part of #2766.The new test covers the already-rendered-row case where the old approval fetch has resolved before the tab switch, then the new task fetch never resolves. It does not cover the inverse delayed-old-fetch case above. Please add a request generation/cancelled flag keyed by
kind(or equivalent) so stale responses from a prior kind cannot callsetItems, and add a delayed-old-fetch regression test that resolves the approval request after switching to Tasks and proves no approval row/action appears under Tasks.1f25de7504toa21e842f14Re-review on current head
a21e842f14: REQUEST_CHANGES.The commit message says this head adds a load-generation counter, but the actual diff/raw file at this exact SHA does not contain one.
RequestsInbox.load()still clearsitems, startsapi.get(/requests/pending?kind=...), and unconditionally callssetItems(list)in.then(...)for whatever request resolves.That means the #11478 sequence is still possible on this current head:
setItems(approvalRows)under the Tasks tab.The row.kind-derived action remains correct and still prevents
action=doneon an approval row, but the stale-render/delayed-fetch half of the fix is not present in code. Please push the actual generation/abort/stale-response guard and a regression where the old-tab fetch resolves after the new tab load starts and is ignored.a21e842f14to2dbcfc6c82APPROVED on head
2dbcfc6c.Re-reviewed the corrected head for the stale-delayed-fetch RC.
RequestsInboxnow increments a load generation for each request, clears rows on kind changes, and ignores stale/requests/pendingresponses whose generation no longer matches the current tab. That closes the old-tab-fetch-resolves-after-switch race without discarding the legitimate current fetch.The action path remains derived from
row.kind, so even a wrong-kind row cannot post the active-tab action. The regression test covers a delayed approval fetch resolving after switching to Tasks and verifies the stale approval row/action is not rendered.CI / Canvas (Next.js),CI / Platform (Go), andCI / all-requiredare green on this head; remaining red gates are review/checklist ceremony.SOP ACK: genuine independent re-review complete; no correctness, security, performance, test, or maintainability blockers found.