Skip to content

feat(workflow): align history propagation API with go-sdk#1047

Open
nelson-parente wants to merge 2 commits into
dapr:mainfrom
nelson-parente:align-history-propagation-go-sdk
Open

feat(workflow): align history propagation API with go-sdk#1047
nelson-parente wants to merge 2 commits into
dapr:mainfrom
nelson-parente:align-history-propagation-go-sdk

Conversation

@nelson-parente
Copy link
Copy Markdown

@nelson-parente nelson-parente commented May 20, 2026

Summary

Aligns the workflow history propagation surface added in #1025 with the freshly-renamed go-sdk helpers (durabletask-go#105, merged 2026-05-19). That rename landed four days after #1025 merged, so this is drift, not a defect — Cassie flagged it post-merge while building the cross-SDK quickstarts.

This brings python-sdk back in line for cross-SDK parity before 1.18 ships.

Read API renames

Mirror durabletask-go's GetLast*ByName form to make it obvious these return the most-recent occurrence (with plural get_*s_by_name siblings for the full list):

Old New
PropagatedHistory.get_workflow_by_name get_last_workflow_by_name
WorkflowResult.get_activity_by_name get_last_activity_by_name
WorkflowResult.get_child_workflow_by_name get_last_child_workflow_by_name

Plural variants (get_workflows_by_name, get_activities_by_name, get_child_workflows_by_name) and chain-level helpers (get_app_ids, get_events_by_*) are unchanged.

Scheduling — use the enum directly

The propagation scope is passed via the existing propagation= kwarg, using PropagationScope members:

import dapr.ext.workflow as wf

yield ctx.call_child_workflow(
    process_payment,
    input=req,
    propagation=wf.PropagationScope.OWN_HISTORY,
)

yield ctx.call_activity(
    log_summary,
    input=None,
    propagation=wf.PropagationScope.LINEAGE,
)

An earlier revision of this PR shipped propagate_lineage() / propagate_own_history() factory functions mirroring the go-sdk shape. Per review feedback from @seherv these were dropped (commit 69a78d6) — Go uses factories to compensate for the lack of enums; Python has them, so the enum members are clearer and play better with type checkers.

Non-goals

  • Kwarg name unchanged (propagation=). Renaming to with_history_propagation= would mirror go-sdk's WithHistoryPropagation() more literally but isn't idiomatic Python.
  • No proto / runtime changes. This PR is purely surface-level: renamed query methods are direct renames.

Test plan

  • uv run ruff check --fix && uv run ruff format — clean
  • uv run pytest ext/dapr-ext-workflow/tests/durabletask/test_propagation.py ext/dapr-ext-workflow/tests/durabletask/test_propagation_wiring.py — 29 passed
  • uv run python -m unittest discover -v ./ext/dapr-ext-workflow/tests — 133 passed
  • uv run pytest tests/examples/test_workflow.py::test_history_propagation against a 1.18-RC sidecar (output-based test; print statements unchanged, should still pass)
  • CI green

References

cc @cicoyle @acroca

Cassie (durabletask-go author) flagged divergence between python-sdk dapr#1025
and the freshly-renamed go-sdk helpers in durabletask-go dapr#105 (merged
2026-05-19, after dapr#1025 landed). This brings python-sdk's surface back
in line for cross-SDK parity before 1.18 ships.

Read API renames (mirror durabletask-go GetLast*ByName):
- PropagatedHistory.get_workflow_by_name      -> get_last_workflow_by_name
- WorkflowResult.get_activity_by_name         -> get_last_activity_by_name
- WorkflowResult.get_child_workflow_by_name   -> get_last_child_workflow_by_name

Plural variants (get_workflows_by_name, get_activities_by_name,
get_child_workflows_by_name) and the chain-level helpers are unchanged.

Scheduling helpers (mirror go-sdk workflow.PropagateLineage /
workflow.PropagateOwnHistory):
- propagate_lineage()      -> PropagationScope.LINEAGE
- propagate_own_history()  -> PropagationScope.OWN_HISTORY

PropagationScope enum is kept as the underlying value, so both
`propagation=propagate_lineage()` and
`propagation=PropagationScope.LINEAGE` work.

Example, README snippet, and tests updated to use the renamed/new
surface. No runtime/proto changes.

Refs: dapr#1001, dapr/durabletask-go#105, dapr/go-sdk#823
Signed-off-by: Nelson Parente <nelson_parente@live.com.pt>
@nelson-parente nelson-parente requested review from a team as code owners May 20, 2026 21:08
@codecov
Copy link
Copy Markdown

codecov Bot commented May 20, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 82.46%. Comparing base (bffb749) to head (69a78d6).
⚠️ Report is 133 commits behind head on main.

Additional details and impacted files
@@            Coverage Diff             @@
##             main    #1047      +/-   ##
==========================================
- Coverage   86.63%   82.46%   -4.17%     
==========================================
  Files          84      146      +62     
  Lines        4473    14501   +10028     
==========================================
+ Hits         3875    11958    +8083     
- Misses        598     2543    +1945     

☔ View full report in Codecov by Sentry.
📢 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.

Copy link
Copy Markdown
Contributor

@seherv seherv left a comment

Choose a reason for hiding this comment

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

Thanks for opening the PR! Left you one comment

PD: I agree with keeping it as propagation=

Comment thread examples/workflow/README.md Outdated
Comment on lines 547 to 550
- `propagation=propagate_own_history()` on a child workflow call —
forwards the caller's events only.
- `propagation=PropagationScope.LINEAGE` on an activity call — forwards the
- `propagation=propagate_lineage()` on an activity call — forwards the
caller's events *plus* anything the caller itself received from its parent.
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.

The way this is implemented would work fine, but IMO this is not idiomatic Python.

It also obscures things a bit (and might even mess with type checkers) because the return type of these functions only tells us that propagation=OwnHistory|Lineage, while the plain enum value tells us exactly what the value is without a doubt.

If I understand correctly, this is the way Go makes up for a lack of enums and is not a pattern we should apply in Python.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

ty! Let me work around this feedback!

…ories

Per review feedback, Go-style factory helpers are not idiomatic Python: they
obscure the actual enum value at the call site and confuse static type
checkers (return annotation only shows PropagationScope, not the specific
member). Use PropagationScope.LINEAGE / PropagationScope.OWN_HISTORY
directly instead.

Signed-off-by: Nelson Parente <nelson_parente@live.com.pt>
Copilot AI pushed a commit to dapr/dotnet-sdk that referenced this pull request May 22, 2026
Cassie (durabletask-go author) flagged the .NET surface for cross-SDK
divergence post-merge of dotnet-sdk#1802 / #1818. This rewrites the
public history-propagation API to match the go-sdk shape — same one the
python-sdk just adopted (python-sdk#1047). Issue dotnet-sdk#1801 was
closed before her review; this PR delivers what the issue originally
described.

Three concrete gaps closed:

1. Activity-level opt-in (was missing entirely)
   - PropagationScope moved from ChildWorkflowTaskOptions to base
     WorkflowTaskOptions; ChildWorkflowTaskOptions inherits it.
   - WithHistoryPropagation() extension method added on the base record.
   - scheduleTaskAction.HistoryPropagationScope is now wired in
     WorkflowOrchestrationContext.CallActivityInternalAsync so activities
     can opt into propagation, matching CallChildWorkflowInternalAsync.
   - Without this, the Go SDK's reference example (SettlePayment activity
     using PropagateOwnHistory) literally cannot be ported to .NET.

2. Read API rewritten as high-level resolvers (was lossy FilterBy* + a
   PropagatedHistoryEvent record that dropped input/output/failure
   payloads)
   - PropagatedHistory.FilterByAppId/InstanceId/WorkflowName removed.
   - PropagatedHistory now exposes GetWorkflows(), GetWorkflowsByName(),
     GetLastWorkflowByName(), GetAppIds(), GetWorkflowsByAppId(),
     GetWorkflowsByInstanceId().
   - New WorkflowResult class with InstanceId/AppId/Name plus
     GetActivitiesByName(), GetLastActivityByName(),
     GetChildWorkflowsByName(), GetLastChildWorkflowByName() — mirrors
     durabletask-go's GetLastWorkflowByName / GetLastActivityByName /
     GetLastChildWorkflowByName renames from durabletask-go#105.
   - New ActivityResult record carries Name, Started, Completed, Failed,
     Input, Output, FailureDetails — matching the Go/Python equivalents
     so chain-of-custody patterns line up.
   - New ChildWorkflowResult record with the equivalent shape.

3. Event payload preserved internally (was discarded by ConvertChunk)
   - ConvertChunk in WorkflowOrchestrationContext now parses raw events,
     walks them to resolve TaskScheduled <-> TaskCompleted/Failed and
     ChildWorkflowInstanceCreated <-> ChildWorkflowInstanceCompleted/
     Failed by scheduleId, and produces fully-populated ActivityResult /
     ChildWorkflowResult instances. SDK retries reuse TaskExecutionId so
     matching is on scheduleId (matching Go/Python semantics).
   - Public API does not leak the proto HistoryEvent type — resolution
     happens at construction time inside Dapr.Workflow.

Additional surface additions:

- PropagationNotFoundException for missing-name lookups (mirrors
  Python's PropagationNotFoundError / Go's error returns).
- Static WorkflowHistory.PropagateLineage() / PropagateOwnHistory()
  factory helpers for go-sdk call-site parity.

Removed (clean break — 1.18 unreleased): PropagatedHistoryEntry,
PropagatedHistoryEvent, HistoryEventKind, FilterByAppId,
FilterByInstanceId, FilterByWorkflowName.

Tests:

- WorkflowHistoryPropagationTests.cs rewritten end-to-end to cover the
  new resolvers, query helpers, factory helpers, activity-level scope
  wiring, and child-workflow-level scope wiring.
- HistoryPropagationWorkflowTests.cs (integration) updated to use
  GetWorkflows().Count in place of Entries.Count.

Refs: #1801, dapr/durabletask-go#105, dapr/go-sdk#823,
dapr/python-sdk#1047

Signed-off-by: Nelson Parente <nelson_parente@live.com.pt>

Co-authored-by: nelson-parente <20144601+nelson-parente@users.noreply.github.com>
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.

3 participants