[BUG] Task store splits after crash + --resume: idle dispatcher replays stale tasks to subagents as synthetic "task-list" messages
Preflight Checklist
- [x] I have searched existing issues and this hasn't been reported yet
- [x] This is a single bug report (please file separate reports for different bugs)
- [x] I am using the latest version of Claude Code
What's Wrong?
After a session crash + claude --resume, the task subsystem splits across two store keys:
- The task TOOLS (TaskCreate/TaskList/TaskUpdate) bind to a fresh per-process key:
~/.claude/tasks/session-<8hex>/— numbering restarts at #1. - The built-in idle/join TASK DISPATCHER stays bound to the original conversation UUID:
~/.claude/tasks/<conversation-uuid>/— which still holds the entire pre-crash task list. Completions made after the resume go to the new store, so the old store's tasks stay "open" forever.
Consequence: every time a team subagent (spawned via the Agent tool) starts or goes idle, the dispatcher pulls the next non-completed task from the STALE store, marks it in_progress with that agent as owner (writing the stale store's JSON), and messages the agent as a synthetic sender named "task-list":
"Complete all open tasks. Start with task #NNN: <full stale task subject + description>"
The referenced IDs don't exist in the live task list (TaskGet → "Task not found"), and replies to "task-list" bounce ("No agent named 'task-list' is reachable") — it has no team inbox.
Impact observed: subagents received directives for tasks already completed and merged days earlier. One subagent accepted a replayed task and performed substantial unauthorized work (source edits + new files in the repo working tree) before intervention — the directive carried a complete, plausible task description (a real one, just stale), so the only detectable invalidity signal was the ID mismatch against the live TaskList.
What Should Happen?
- The dispatcher and the task tools should resolve the SAME task store (whichever key is chosen — per-conversation or per-process — just consistently).
- On
--resume, the conversation's existing task store should be adopted/migrated rather than a fresh key created alongside it. - The dispatcher should validate a task against the live task list before dispatching it to an agent, and identify itself with a real, replyable team identity.
- A stale queue should never silently convert tracking entries into executed work.
Error Messages/Logs
# Two stores for ONE conversation (conversation uuid matches transcript filename + scratchpad path):
~/.claude/tasks/<conversation-uuid>/ -> old numbering (#85-#111), stale pending/in_progress statuses
~/.claude/tasks/session-<8hex>/ -> live numbering (#1-#N); matches ~/.claude/teams/session-<8hex>/
# Dispatch message received by subagents (sender "task-list"):
"Complete all open tasks. Start with task #110: Fix Bug 2: ... <verbatim stale description>"
# Reply attempt:
SendMessage -> "No agent named 'task-list' is reachable."
# Smoking gun — stale-store file mtimes match team-agent lifecycle events TO THE SECOND
# (joinedAt values from ~/.claude/teams/session-<8hex>/config.json; idle timestamps from
# idle_notification messages). One dispatch write per spawn and per idle event:
101.json mtime 09:26:26 == agent A joinedAt 09:26:26 -> status in_progress, owner "agent A"
102.json mtime 09:26:55 == agent B joinedAt 09:26:55 -> in_progress, owner "agent B"
...
105.json mtime 09:34:37 == agent A idle notification 14:34:37.692Z (= 09:34:37 local)
106.json mtime 09:35:02 == agent B idle notification 14:35:02.859Z
110.json mtime 09:36:42 == agent D idle notification 14:36:42.331Z
Steps to Reproduce
- Start an interactive session in a project; create several tasks with TaskCreate (numbering reaches some #N > 1).
- Kill the CLI process mid-session (simulate a crash).
claude --resumeand continue the same conversation.- Create a new task — observe numbering restarted at #1 (tools now on a fresh
session-<8hex>store; the old<conversation-uuid>store still holds the pre-crash tasks, non-completed). - Spawn a named background subagent via the Agent tool (in-process team).
- Observe: at spawn — and again whenever the agent goes idle — it receives a "task-list" message instructing it to complete a stale old-numbered task, and the corresponding JSON in the OLD store is updated to
in_progresswith the agent as owner.
Workaround that stops it: back up ~/.claude/tasks/<conversation-uuid>/, then set every non-completed task's "status" to "completed". Dispatches cease immediately.
Claude Model
Other
Is this a regression?
I don't know
Last Working Version
_No response_
Claude Code Version
2.1.205
Platform
Anthropic API
Operating System
Ubuntu/Debian Linux
Terminal/Shell
Xterm
Additional Information
Environment detail: no user hooks (user + project settings.json hooks are {}), no dispatch-related plugins (superpowers / frontend-design / code-review only), no custom orchestration — the dispatcher behavior is stock.
Model during the session: Fable 5 (also reproduced context under Opus 4.7 pre-switch). Session ran inside tmux in a VNC X session; multiple subagents active as an in-process team.
Not attaching /bug session data (proprietary project content); happy to answer follow-ups or run diagnostics on the preserved stale-store backup.