Skip to content

Agent reads can repopulate cleared or replacement-session state #3337

Description

@Chris0Jeky

Summary

frontend/taskdeck-web/src/store/agentStore.ts writes profile, run-list and run-detail responses into one shared slot per surface without request-generation or session-lifetime ownership.

This is a source-traced finding on main 307c3b8b50bec1cb0bfaea3e570a942bcb1d4451. Implementation must begin with failing real Pinia regressions.

Reachable races

A → B → A request identity reuse

AgentRunsView clears the run list and starts a new request when agentId changes. If an older request for agent A remains pending while the user visits B and then A again, the old A response can settle after the newer A response and overwrite it. Run detail has the same shape across {agentId, runId} navigation.

Clear/unmount is not an invalidation boundary

clearRuns() and clearRunDetail() clear visible state but do not retire an in-flight request or its finally. A late success can repopulate a route that was deliberately cleared; a late failure can install an error and toast after navigation; the old finally can clear loading owned by a newer request.

Session replacement

The store has no session watcher or reset epoch. A request begun under account A can settle after logout/login, including login as the same user ID, and populate the replacement account’s agent surfaces or emit stale error UI.

Profiles also accept every response, so overlapping profile reads are last-settlement-wins rather than latest-request-wins.

Expected contract

  • Give profiles, run-list and run-detail independent request-owner lanes.
  • A newer read retires only the previous owner in the same lane.
  • clearRuns() and clearRunDetail() synchronously invalidate their lane before clearing state.
  • Identity/auth/demo replacement advances a store epoch and clears every agent surface, error and loading owner.
  • Late stale success/failure/finally cannot patch state, emit a toast or clear a newer owner’s loading flag.
  • The original transport promise still resolves or rejects to its caller; the client must not pretend an already-sent server request was cancelled.
  • Legitimate independent profile/list/detail reads remain concurrent.
  • Preserve demo-mode no-network behavior.

Acceptance

  • Deferred Pinia regression for A-old → B → A-new run-list responses with A-old settling last.
  • Equivalent run-detail identity-reuse regression.
  • clearRuns() and clearRunDetail() followed by late success and late failure, including no stale toast/error.
  • Reverse-settling profile reads.
  • Old finally cannot clear loading for a newer same-lane request.
  • Logout/login as the same user ID while each representative lane is pending.
  • Independent lanes remain concurrent and their loading indicators remain truthful.
  • Existing agent-store and demo 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 add request cancellation, alter the agent API, or redesign the routes.

Audit provenance

Found during the 2026-09-21 async state-ownership pass after #3327/#3328. Searches found no open issue or PR owning agentStore.ts request/session lifetime.

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.

    Projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions