Subagent orchestration has no concurrency model: no join, no cancellation semantics, no quiescent stop, no signal ordering — and the semantics change silently between releases

Status Open
Maintainer reply None cached
Activity 0 comments · opened Aug 19, 2026

Skills and CLAUDE.md workflows are the mechanism Claude Code gives us for building repeatable processes. Most non-trivial workflows are built around subagents. But subagent orchestration has no defined concurrency model — the scheduling semantics change between releases with no changelog entry and no deprecation notice, and the primitives a concurrent system needs in order to be programmable are either missing or have changed meaning at least once.

The pattern, visible in the existing issue tracker

Several open issues look unrelated but are instances of one missing thing:

  • #78782 — a finished background task never resumes the in-process subagent waiting on it. That is the classic lost wakeup.
  • #77284 — the Agent tool's task-notification can report status:completed while the subagent's work is still running. That is a race between the completion signal and the state it signals; nothing guarantees the signal happens-after completion.
  • #74695 — a user interjection kills in-flight subagents and background tasks. There is no cancellation model, so an unrelated event propagates as cancellation.

A fourth instance, from a production workflow this week

A skill step said "isolate deep exploration in synchronous subagents." When it was written, spawning a subagent blocked the parent — that sentence was not an instruction, it was a description of how the tool worked, and it was self-enforcing.

The Agent tool now runs subagents in the background, which means:

  1. Join was removed from the API with no replacement. The parent has no way to actually wait for its children. It keeps getting turns while they run, the model fills those turns with "one cheap read" of the source — twelve times — and ends up doing the entire investigation inline, defeating the context isolation that was the whole point of the delegation. It paid for the subagents and the context bloat.
  2. There is no quiescent state. After the user explicitly said "approve and stop," the children's completion notifications arrived as incoming messages and woke the session. It treated their arrival as permission to resume, and edited an artifact the user had just approved. "Stopped" is not a state the system respects; it is just a moment when nothing has happened yet.

No individual release is the bug. The bug is the meta-level: words like "synchronous," "in parallel," "wait for," and "stop" are load-bearing in every skill in the wild, and their meaning is defined by harness behavior that changes silently. Every release is a potential silent regression for every skill, and users find out by watching a run go sideways.

Ask: define a concurrency model for agent orchestration

Commit to — and document — the standard primitives:

  1. Join. A way for a parent to genuinely wait for a child (or a set of children) without being handed turns it is expected to fill.
  2. Cancellation that propagates deliberately and only deliberately — user interjections and session lifecycle events should have defined, chosen effects on children, not incidental ones.
  3. A quiescent "stopped" state that child events cannot exit. Once the user ends the interaction, completion notifications queue; they do not wake the session without user action.
  4. Ordering guarantees: a completion signal happens-after the completion it reports.

Then treat these semantics as a stable contract: changes announced, deprecated, and listed in the changelog like API changes. With that model, #78782, #77284, #74695, and the skill-rot failure above all become instances of one specification gap instead of four unrelated complaints — and it becomes possible to write a skill that still works next month.

View original on GitHub ↗