MultiAgentV2 followup_task can run a new assignment under a stale canonical task name
Nobody has claimed this yet.
- Dominant language
- Rust
- Stars
- 125k
- Forks
- 19.4k
- PR merge metrics
- PR metrics pending
Description
What happens
followup_task can reactivate a completed child agent for an unrelated logical task while retaining the original canonical task name and context. The task tree and UI then show the old name even though the agent is doing different work, so ownership and scope are no longer observable from the agent identity.
Reproduction
On Codex 0.153.1:
- Spawn a child with
task_name: old_scopeand let it complete. - Call
followup_taskonold_scopewith an unrelated task. - Observe that the call succeeds and the child runs again under
old_scope; there is no declared current-scope field to validate or display.
This happened in a live multi-agent session: a completed agent named for one narrow task was reused for two unrelated design/review assignments, but list_agents and the UI continued to expose only the original name.
Source boundary
FollowupTaskArgs currently contains only target and message. The message is marked encrypted in the model-facing tool schema, so a local PreToolUse hook cannot reliably compare the new task semantics with the target's currently registered task name. The handler resolves the existing target and sends the communication without any declared-scope check.
Expected contract
Treat the user-visible unit of work as an assignment, independently of whether the runtime allocates a new resident thread.
- Every assignment has a stable assignment identity/generation and canonical task name for its lifetime. The current effective assignment identity is exposed consistently by
list_agents, the UI, activity events, and audit output. followup_taskis same-assignment continuation. It carries the expected assignment identity/generation, and dispatch atomically verifies that it is still current. A stale or mismatched request is rejected with a machine-readable receipt before resident/thread load, message delivery, wake-up, or assignment/context mutation.- Unrelated work starts a new assignment with a fresh validated task name and a new identity/generation. It does not inherit context from the previous child assignment; parent history inheritance or explicit handoff is determined by the new spawn/retask request. A new thread is valid, but an implementation may instead atomically retask a fully quiescent terminal resident; it must not retain or display the previous assignment's name or implicit context.
- No turn or queued communication from the previous assignment may cross the assignment-generation boundary, and concurrent follow-up/retask attempts must be linearizable.
The runtime cannot infer semantic relatedness from the encrypted free-text message. The enforceable boundary is the caller's explicit choice between continuing the current assignment and starting a new one, plus an atomic generation check.
Suggested regression tests:
- A matching same-assignment follow-up succeeds, including after completion when continuation is intended.
- A stale or mismatched assignment generation rejects without resident/thread load, delivery, wake-up, or assignment/context mutation.
- Starting unrelated work creates a new visible assignment name and generation without previous child-assignment context, whether backed by a fresh thread or an explicit retask operation; configured parent-history inheritance or explicit handoff remains allowed.
- Concurrent follow-up and retask calls cannot deliver old-assignment work into the new generation.
- The invariants survive unload/reload and terminal outcomes including completed, failed, and interrupted.
A blanket local ban on all follow-ups to terminal agents would hide this bug but would also break valid same-task continuation. Semantic inspection of message is not a viable hook workaround because that field is encrypted before hooks receive the tool call.
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start with FollowupTaskArgs and the followup_task handler, then trace target resolution, delivery, resident loading, and assignment identity handling. Add regression coverage for matching, stale, unrelated, concurrent, and unload/reload cases, verifying stale requests are rejected before side effects and visible assignment names and generations remain consistent.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- rust
- Domain
- api, backend, distributed-systems
- Issue type
- Bug
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Active
- Clarity
- Mostly clear
- Newbie friendliness
- 35/100