Skip to content

[Frontend][Inbox] Session-epoch follow-ups: list snapshot path, dropped-read reporting, peekDetail wording, spec mock hygiene #2640

Description

@Chris0Jeky

Follow-up to PR #2631 (#2571), from the read-only verification pass on its round-2 fixes. None blocks that merge; all are post-logout-only edges of the new session epoch, tracked here for one small follow-up PR.

1. MEDIUM: the map clear inverts the summary-side guard the same way it inverted the detail guard, and the epoch does not cover the list path. applyBackgroundListSnapshot (captureStore.ts ~184-193) keeps a locally newer row only when latestSummaryGenerationById.get(id) ?? 0 exceeds the snapshot's observed generation; after resetForLogout() clears that map the read is 0, so an older in-flight background list snapshot wins. Scenario: a batch triage poll is active; a detail read moves an item to a terminal status via upsertSummary (generation N above the in-flight snapshot's observed generation); the user logs out; between the shell watcher's resetForLogout() and the Inbox unmount's cancelBatchTriagePolling(), the pre-logout list response lands with isCurrent() still true and items regresses the row. The window is sub-millisecond, the route is already leaving, and the next Inbox mount replaces items through the scope-replacement seam, so it is not user-visible today; but the pre-existing spec at captureStore.spec.ts ~2763-2796 asserts the regressed behaviour as intended ("nothing stale-high is left to pin the row"), so the two halves of one guard now carry opposite post-logout contracts. Fix: the list snapshot path captures the session epoch like the detail path and drops a snapshot issued before the reset; retarget that spec.

2. LOW: onRefreshed fires for reads the epoch dropped (captureStore.ts ~651), so refreshedDetailIds in the batch poll can claim an id was reconciled while detailById still holds pre-batch state; post-logout only.

3. LOW: fetchDetail's drop is unreported to callers (three drop paths, no boolean, unlike fetchItems); selectItemById returns true with an empty detail and the Legacy panel shows "Unable to load capture detail." for a read that succeeded; the new path is post-logout only, the other two are pre-existing.

4. LOW: the docstring says every detail read captures the epoch, but peekDetail writes detailById through cacheDetail with no epoch and no generation compare (its only guard is the hash re-check); cacheSummary: false keeps it out of items. Qualify the wording or add the capture.

5. LOW: captureStore.spec.ts ~2560 installs a persistent mockImplementation returning never-resolving promises; the suite's only reset is vi.clearAllMocks(), so a test added below it that triggers an unconfigured detail read hangs instead of receiving undefined. Replace with per-test mockReturnValueOnce or restore the implementation in that test's teardown.

Refs #2571, PR #2631, PR #2567.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingfrontendPrimary implementation impact in Vue/TypeScript UI and client runtime.tech-debtDebt cleanup or deferred engineering quality work with limited product-surface change.

    Projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions