Add ctx.schedule_new_workflow for detached workflows - #1177
Draft
acroca wants to merge 1 commit into
Draft
Conversation
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## main #1177 +/- ##
==========================================
+ Coverage 82.84% 82.86% +0.02%
==========================================
Files 123 123
Lines 10130 10191 +61
==========================================
+ Hits 8392 8445 +53
- Misses 1738 1746 +8 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
Signed-off-by: Albert Callarisa <albert@diagrid.io>
acroca
force-pushed
the
detached-workflows
branch
from
July 27, 2026 13:57
9010aa9 to
acad112
Compare
Contributor
There was a problem hiding this comment.
Pull request overview
Adds first-class detached workflow spawning to the synchronous workflow context.
Changes:
- Adds public and durable-task context APIs.
- Implements action generation, routing, replay reconciliation, and deterministic IDs.
- Adds unit tests, documentation, and an example.
Reviewed changes
Copilot reviewed 9 out of 9 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
dapr/ext/workflow/workflow_context.py |
Defines the public context API. |
dapr/ext/workflow/dapr_workflow_context.py |
Delegates detached scheduling to durable-task. |
dapr/ext/workflow/_durabletask/task.py |
Defines the engine context contract. |
dapr/ext/workflow/_durabletask/worker.py |
Implements scheduling and replay handling. |
dapr/ext/workflow/_durabletask/internal/helpers.py |
Constructs detached actions and events. |
dapr/ext/workflow/AGENTS.md |
Documents detached workflow semantics. |
tests/ext/workflow/test_dapr_workflow_context.py |
Tests the public wrapper. |
tests/ext/workflow/durabletask/test_detached_workflow.py |
Tests engine behavior and replay. |
examples/workflow/detached.py |
Demonstrates detached fan-out. |
Suppressed comments (3)
dapr/ext/workflow/_durabletask/worker.py:1490
- Reject invalid detached options before consuming an action sequence. An explicit empty
instance_idcurrently emits an action the Dapr runtime rejects, whileapp_namespacewithoutapp_idcreates an unusable routing envelope; both cases are rejected by the upstream context API.
id = self.next_sequence_number()
workflow_name = workflow if isinstance(workflow, str) else task.get_name(workflow)
if instance_id is None:
self._detached_counter += 1
instance_id = f'{self.instance_id}-{self._detached_counter}'
tests/ext/workflow/durabletask/test_detached_workflow.py:34
- The helper leaves both
encoded_inputand its return value untyped, despite the repository's strong-typing requirement. Annotate the optional encoded payload and executor result explicitly.
def _run(registry: worker._Registry, entry_name: str, encoded_input=None):
dapr/ext/workflow/_durabletask/worker.py:2039
- A historical detached event can align with a current
completeWorkflowaction when updated workflow code now returns before this spawn. In that case_get_wrong_action_type_error()calls_get_method_name_for_action(), which has nocompleteWorkflowcase and raisesNotImplementedError, masking the intendedNonDeterminismError. Make the action-name lookup handle completion actions or safely fall back to the action type.
elif not action.HasField('createDetachedWorkflow'):
expected_method_name = task.get_name(ctx.schedule_new_workflow)
raise _get_wrong_action_type_error(task_id, expected_method_name, action)
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
Comment on lines
+195
to
+199
| Unlike ``call_sub_orchestrator``, the spawned workflow is fully | ||
| decoupled from the caller: no parent linkage is recorded on the new | ||
| instance and no completion or failure flows back. There is no | ||
| awaitable task — the call resolves as soon as the runtime accepts the | ||
| action. |
Comment on lines
+58
to
+59
| if __name__ == '__main__': | ||
| wfr.start() |
| import dapr.ext.workflow._durabletask.internal.protos as pb | ||
| from dapr.ext.workflow._durabletask import task, worker | ||
|
|
||
| logging.basicConfig(level=logging.DEBUG) |
Comment on lines
+1245
to
+1247
| # Cancel any pending actions except detached-workflow spawns, which are | ||
| # fire-and-forget: the action is effective the moment schedule_new_workflow | ||
| # returns, so it must survive the caller's completion. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Ref: dapr/dapr#9261
Summary
Adds first-class support for spawning fully decoupled workflows from within another workflow in the Python SDK, mirroring the API introduced upstream in dapr/durabletask-go#100 and the runtime support in dapr/dapr#9902. Users no longer need to wrap
DaprWorkflowClient.schedule_new_workflowinside an activity to start an independent workflow — it's now a first-class context method.Notes
9d3681cb…already includesCreateDetachedWorkflowActionandDetachedWorkflowInstanceCreatedEvent.dapr.ext.workflow.aio) is unchanged: the workflow context API is sync (generator-based) in this SDK; only the client has an async variant, and detached spawning is a context operation.