A cancelled run is announced like any other terminal outcome
Cancellation leaves the run through its own path, so it is easy to forget to announce. Anything watching for a run to finish must see a cancelled run finish.
The rule
- Every path that cancels a run announces the run's completion carrying the status cancelled, exactly once per cancellation, with the run's identity and its duration.
- Consumers of that announcement must treat cancelled as its own outcome and not as a completed run.
- A cancelled sub-workflow resolves to its caller with status cancelled and empty outputs.
What it means
Anything watching for a run to finish must not mistake a cancelled run for one still in progress: cancellation ends the run through its own path, and that path is easy to forget to announce, so this rule pins the announcement as mandatory rather than incidental. A listener that only checks for completion, and treats silence as "still running," is exactly the bug this closes.
A cancelled sub-workflow resolving to its caller is the same rule seen from the other side. Whatever the resolved response looked like — no response at all, a bare status, or an outputs field that is not itself a set of values — none of it is treated as partial results. The caller sees an empty result, never a fabricated one assembled from whatever happened to be present.
Example
A sub-workflow's resolved response, in shapes a cancellation or an outputless resume can leave behind:
{ "status": "cancelled" }{}