Forward unmatched external events to running sub-orchestrations
Problem
Client::raise_event(instance_id, name, data) only delivers the event to the orchestration whose ID is instance_id. If a schedule_wait(name) is awaiting inside a sub-orchestration spawned by schedule_sub_orchestration (e.g. parallel branches via join / select), an event raised on the parent never reaches those waiters.
From a caller's perspective the sub-orchestration is an implementation detail — the auto-generated IDs ({parent}::sub::{event_id}) aren't part of the public contract and aren't known to external signalers, so there's no practical way for a client to target the child directly.
Repro sketch
// orchestration
ctx.schedule_sub_orchestration("child", input).await?; // child awaits schedule_wait("approve")
// caller
client.raise_event(parent_id, "approve", data).await?; // never observed by child
Workarounds today
Callers can walk get_instance_tree(parent) and raise_event on each running descendant, but this has races:
- Descendant may be
Pending (not yet Running) when enumerated and gets skipped.
- Event may arrive before the sub-orchestration is even scheduled; it then lands on the parent's pending queue with no consumer.
- Broadcasts to every descendant regardless of whether they actually subscribe to that event name.
Proposed direction
Some options worth considering — happy to discuss which fits the model best:
- Inheritance on miss: an unmatched
raise_event on a parent is buffered and offered to sub-orchestrations as they reach a matching schedule_wait. Symmetric to how schedule_wait already buffers events that arrive before the wait is registered.
- Explicit broadcast API:
Client::raise_event_tree(root, name, data) (or a flag on raise_event) that durably fans the event out to the root and all current/future descendants until they complete.
- Subscription registration: sub-orchestrations register a "forward
name from parent" subscription when they call schedule_wait, and the runtime routes parent events accordingly.
Option 1 is the most ergonomic for callers since they don't need to know the topology. Option 2 is the smallest API change but pushes timing responsibility back to the client.
Why this matters
Any orchestration pattern that fans out work into sub-orchestrations and then expects a human-in-the-loop or external system to signal one of those branches is currently unreachable without out-of-band coordination. Approval flows inside parallel branches, cancel-style signals targeting a specific competitor in a race, etc.
Forward unmatched external events to running sub-orchestrations
Problem
Client::raise_event(instance_id, name, data)only delivers the event to the orchestration whose ID isinstance_id. If aschedule_wait(name)is awaiting inside a sub-orchestration spawned byschedule_sub_orchestration(e.g. parallel branches viajoin/select), an event raised on the parent never reaches those waiters.From a caller's perspective the sub-orchestration is an implementation detail — the auto-generated IDs (
{parent}::sub::{event_id}) aren't part of the public contract and aren't known to external signalers, so there's no practical way for a client to target the child directly.Repro sketch
Workarounds today
Callers can walk
get_instance_tree(parent)andraise_eventon each running descendant, but this has races:Pending(not yetRunning) when enumerated and gets skipped.Proposed direction
Some options worth considering — happy to discuss which fits the model best:
raise_eventon a parent is buffered and offered to sub-orchestrations as they reach a matchingschedule_wait. Symmetric to howschedule_waitalready buffers events that arrive before the wait is registered.Client::raise_event_tree(root, name, data)(or a flag onraise_event) that durably fans the event out to the root and all current/future descendants until they complete.namefrom parent" subscription when they callschedule_wait, and the runtime routes parent events accordingly.Option 1 is the most ergonomic for callers since they don't need to know the topology. Option 2 is the smallest API change but pushes timing responsibility back to the client.
Why this matters
Any orchestration pattern that fans out work into sub-orchestrations and then expects a human-in-the-loop or external system to signal one of those branches is currently unreachable without out-of-band coordination. Approval flows inside parallel branches, cancel-style signals targeting a specific competitor in a race, etc.