Skip to content

Add ctx.schedule_new_workflow for detached workflows - #1177

Draft
acroca wants to merge 1 commit into
dapr:mainfrom
acroca:detached-workflows
Draft

Add ctx.schedule_new_workflow for detached workflows#1177
acroca wants to merge 1 commit into
dapr:mainfrom
acroca:detached-workflows

Conversation

@acroca

@acroca acroca commented Jul 27, 2026

Copy link
Copy Markdown
Member

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_workflow inside an activity to start an independent workflow — it's now a first-class context method.

Notes

  • No proto regeneration on this branch, commit 9d3681cb… already includes CreateDetachedWorkflowAction and DetachedWorkflowInstanceCreatedEvent.
  • Async client wrapper (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.

@codecov

codecov Bot commented Jul 27, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 87.09677% with 8 lines in your changes missing coverage. Please review.
✅ Project coverage is 82.86%. Comparing base (866ee5a) to head (acad112).

Files with missing lines Patch % Lines
dapr/ext/workflow/_durabletask/worker.py 84.61% 6 Missing ⚠️
dapr/ext/workflow/_durabletask/task.py 66.66% 1 Missing ⚠️
dapr/ext/workflow/workflow_context.py 66.66% 1 Missing ⚠️
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.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Signed-off-by: Albert Callarisa <albert@diagrid.io>
@acroca
acroca force-pushed the detached-workflows branch from 9010aa9 to acad112 Compare July 27, 2026 13:57
@acroca
acroca requested a balanced review from Copilot August 17, 2026 11:11

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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_id currently emits an action the Dapr runtime rejects, while app_namespace without app_id creates 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_input and 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 completeWorkflow action 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 no completeWorkflow case and raises NotImplementedError, masking the intended NonDeterminismError. 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.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants