Summary
frontend/taskdeck-web/src/store/notificationStore.ts has two shared data surfaces, inbox notifications and notification preferences, but no request identity, mutation generation, operation-owned loading, or credential/session epoch.
This is a source-traced audit finding on main 307c3b8b50bec1cb0bfaea3e570a942bcb1d4451. Implementation must begin with failing real Pinia regressions.
Reachable races
Out-of-order reads
Two inbox reads for different filters, or two preference reads, may settle in reverse order. Every response commits, so the older request can replace the latest requested view.
Confirmed writes overwritten by older snapshots
An inbox read can begin before markAsRead or markAllRead, then resolve with its pre-mutation snapshot and make confirmed read notifications look unread again. A preference read can similarly resolve after updatePreferences and restore the pre-save values.
Shared loading ownership
Inbox reads, preference reads, and preference writes assign one Boolean directly. If independent operations overlap, the first settlement clears loading while another operation is still pending.
Credential/session replacement
The store has no session watcher or reset epoch. Requests or mutations begun under one token/account can settle after logout, login, or token refresh and populate the replacement notification surfaces or emit stale error/toast UI. This includes token rotation where user ID and authenticated state remain unchanged.
Expected contract
- Inbox and preferences have independent latest-read owners.
- Successful notification mutations invalidate older inbox reads; successful preference writes invalidate older preference reads.
- Loading is derived from current operation tokens rather than whichever call settles first.
- Identity, token, authentication, or demo replacement synchronously advances an epoch, retires old owners, and clears notification/preference state, error, and loading.
- Stale success/failure/finally settlement cannot patch replacement state, emit a toast, or clear newer loading ownership.
- Original transport promises still resolve or reject to their callers; the client does not pretend a sent request was cancelled.
- Existing demo-mode no-network behavior and legitimate inbox/preferences concurrency remain intact.
Acceptance
- Deferred Pinia cases for reverse-settling inbox reads and preference reads.
- Old inbox snapshot after confirmed
markAsRead and markAllRead cannot restore unread state.
- Old preference snapshot after successful
updatePreferences cannot restore old values.
- Overlapping inbox/preference operations retain truthful loading until the final owner settles.
- Logout/login and same-user token rotation clear both surfaces and suppress late success, failure, error, and toast settlement.
- Stale mutation success cannot patch replacement-session state.
- Existing notification store, realtime, integration, demo, inbox-view, and preference-view tests remain green.
- Frontend lint, typecheck, build, full Vitest, and exact-head hosted CI pass.
Boundaries
This is client-state ownership, not a demonstrated server authorization bypass. It does not serialize overlapping mutations, redesign realtime delivery, change the notification API, or implement the reminder/email work in #2010. Realtime arrival versus authoritative refresh and same-target mutation ordering should be reviewed as separate follow-ups.
Audit provenance
Found during the 2026-09-21 async state-ownership pass after #3327, #3328, and #3337. The only open issue naming notificationStore.ts is broad feature epic #2010; no open PR owns this request/session lifecycle defect.
Summary
frontend/taskdeck-web/src/store/notificationStore.tshas two shared data surfaces, inbox notifications and notification preferences, but no request identity, mutation generation, operation-owned loading, or credential/session epoch.This is a source-traced audit finding on
main307c3b8b50bec1cb0bfaea3e570a942bcb1d4451. Implementation must begin with failing real Pinia regressions.Reachable races
Out-of-order reads
Two inbox reads for different filters, or two preference reads, may settle in reverse order. Every response commits, so the older request can replace the latest requested view.
Confirmed writes overwritten by older snapshots
An inbox read can begin before
markAsReadormarkAllRead, then resolve with its pre-mutation snapshot and make confirmed read notifications look unread again. A preference read can similarly resolve afterupdatePreferencesand restore the pre-save values.Shared loading ownership
Inbox reads, preference reads, and preference writes assign one Boolean directly. If independent operations overlap, the first settlement clears
loadingwhile another operation is still pending.Credential/session replacement
The store has no session watcher or reset epoch. Requests or mutations begun under one token/account can settle after logout, login, or token refresh and populate the replacement notification surfaces or emit stale error/toast UI. This includes token rotation where user ID and authenticated state remain unchanged.
Expected contract
Acceptance
markAsReadandmarkAllReadcannot restore unread state.updatePreferencescannot restore old values.Boundaries
This is client-state ownership, not a demonstrated server authorization bypass. It does not serialize overlapping mutations, redesign realtime delivery, change the notification API, or implement the reminder/email work in #2010. Realtime arrival versus authoritative refresh and same-target mutation ordering should be reviewed as separate follow-ups.
Audit provenance
Found during the 2026-09-21 async state-ownership pass after #3327, #3328, and #3337. The only open issue naming
notificationStore.tsis broad feature epic #2010; no open PR owns this request/session lifecycle defect.