Motivation
When an orchestrator session uses launch_session to kick off a child session (e.g. running /implement on a ticket), there's no clean way to know when the child finishes. An MCP server can't push a turn into the orchestrator's conversation — the Claude Code harness owns model re-invocation, so a launched session never notifies the orchestrator the way a native background task does. Today the only option is repeatedly polling get_active_sessions until the child's status flips to idle, which is clunky and burns turns.
Proposed primitive
A blocking / long-poll MCP tool:
wait_for_session(session, until = "idle", timeoutSeconds) ->
{ sessionId, status, timedOut, lastActivity, tail? }
It blocks until the target session transitions to the requested state (or the timeout elapses), then returns its final status (optionally a short transcript tail). This turns "poll every N minutes" into a single await, giving launched sessions roughly background-task ergonomics for an orchestrator.
cldctrl already derives active/idle from transcript activity (see get_active_sessions), so the signal exists — this is mostly a long-poll wrapper with a timeout and a sane cap.
Why this is the right first piece
- Smallest, highest-leverage addition. It unlocks a supervised auto-orchestration loop: launch ticket N →
await idle → read_session to check the result → launch N+1 or send_to_session a correction.
- Composes with the tools that already exist (
read_session to inspect, send_to_session to steer) — no new concepts.
Adjacent ideas (out of scope for this issue, noting for later)
- cldctrl-native orchestration loop: cldctrl itself walks a dependency list and launches the next ticket when one finishes — planner lives in chat, runtime lives in cldctrl. Could reuse the existing task model (
read_tasks / upsert_task).
- Operator-facing push: a desktop/dashboard notification when a tile goes idle. Doesn't help the orchestrator model, but helps the human not babysit.
Where this came up
Orchestrating the physiometrics-web marker-surface build (parent issue #64 → 12 build tickets #65–#76). I launched /mattpocock-skills:implement on the frontier ticket #65 via launch_session, then had to poll get_active_sessions several times over ~50 s to confirm the new session had registered and received its kickoff. A wait_for_session await would make that launch → verify → chain loop clean, and would let a supervising session gate at ticket boundaries without a hand-rolled poll loop.
Motivation
When an orchestrator session uses
launch_sessionto kick off a child session (e.g. running/implementon a ticket), there's no clean way to know when the child finishes. An MCP server can't push a turn into the orchestrator's conversation — the Claude Code harness owns model re-invocation, so a launched session never notifies the orchestrator the way a native background task does. Today the only option is repeatedly pollingget_active_sessionsuntil the child's status flips toidle, which is clunky and burns turns.Proposed primitive
A blocking / long-poll MCP tool:
It blocks until the target session transitions to the requested state (or the timeout elapses), then returns its final status (optionally a short transcript tail). This turns "poll every N minutes" into a single
await, giving launched sessions roughly background-task ergonomics for an orchestrator.cldctrl already derives
active/idlefrom transcript activity (seeget_active_sessions), so the signal exists — this is mostly a long-poll wrapper with a timeout and a sane cap.Why this is the right first piece
awaitidle →read_sessionto check the result → launch N+1 orsend_to_sessiona correction.read_sessionto inspect,send_to_sessionto steer) — no new concepts.Adjacent ideas (out of scope for this issue, noting for later)
read_tasks/upsert_task).Where this came up
Orchestrating the physiometrics-web marker-surface build (parent issue #64 → 12 build tickets #65–#76). I launched
/mattpocock-skills:implementon the frontier ticket #65 vialaunch_session, then had to pollget_active_sessionsseveral times over ~50 s to confirm the new session had registered and received its kickoff. Await_for_sessionawait would make that launch → verify → chain loop clean, and would let a supervising session gate at ticket boundaries without a hand-rolled poll loop.