Extract plan-authored Durable retry execution - #193
Extract plan-authored Durable retry execution#193Tsuyoshi Ushio (TsuyoshiUshio) merged 12 commits into
Conversation
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Persist bounded execution.retry policies, dispatch static and dynamic tasks through Durable native retry, classify policy-aware failures, expose stable idempotency context, and preserve policy-free history behavior. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Boot the sample through a real Functions host and cover both retry-to-completion and sanitized exhaustion without workflow_tool retry metadata. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Keep synchronous Activity handlers off the event loop, retain sanitized error codes at the message limit, and cover untrusted envelopes, fan-out retry, and Sub Agent persistence. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Describe bounded execution.retry, failure classification, idempotency, replay compatibility, and the independently runnable order-recovery sample. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Avoid Durable's wall-clock retry timeout during orchestration replay while retaining the submission-time delay-sum bound. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Cover native retry policy and display tag propagation for tool, Sub Agent, dynamic, and fan-out Activity dispatch. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 7018a524-6dd1-4132-ada9-ba5f9cf47f26
72bb80d to
400f6f4
Compare
There was a problem hiding this comment.
🟡 Changes recommended
The E2E exhaustion assertion can pass from earlier cumulative logs, and several finalized retry-contract descriptions do not match the implementation.
Once you've addressed the issues Copilot identified, you can request another Copilot review.
Pull request overview
Adds plan-authored Durable retry execution while preserving replay compatibility for policy-free workflows.
Changes:
- Adds bounded retry policies, failure classification, and idempotency context.
- Integrates native retry across static, dynamic, fan-out, tool, and Sub Agent execution.
- Adds tests, a runnable sample, and architecture/user documentation.
File summaries
| File | Description |
|---|---|
src/azure_functions_agents/workflows/schema.py |
Defines retry policies and persisted execution contracts. |
src/azure_functions_agents/workflows/activity.py |
Implements policy-aware Activity execution and outcomes. |
src/azure_functions_agents/workflows/native_retry.py |
Maps policies and handles retry failures. |
src/azure_functions_agents/workflows/engine.py |
Dispatches Activities with persisted retry policies. |
src/azure_functions_agents/workflows/context.py |
Adds task context and idempotency keys. |
src/azure_functions_agents/workflows/tools.py |
Exposes retry authoring and freezes policies. |
src/azure_functions_agents/__init__.py |
Exports the new public API. |
tests/test_workflow_native_retry.py |
Covers retry contracts and scheduling. |
tests/test_workflow_engine.py |
Covers policy-aware Sub Agent execution. |
tests/test_workflow_registry.py |
Adapts registry tests for async Activities. |
tests/test_workflow_retry_policy_sample.py |
Validates the sample contract. |
tests/test_package_imports.py |
Verifies public exports. |
tests/endtoend/test_workflow_native_retry_e2e.py |
Exercises retry with a real Functions host. |
samples/workflow-retry-policy/src/tools/order_tools.py |
Implements the transient-failure sample tools. |
samples/workflow-retry-policy/src/main.agent.md |
Instructs plan-authored retry. |
samples/workflow-retry-policy/src/function_app.py |
Creates the sample app. |
samples/workflow-retry-policy/src/requirements.txt |
Installs the local runtime package. |
samples/workflow-retry-policy/src/host.json |
Configures the Functions host. |
samples/workflow-retry-policy/src/local.settings.template.json |
Provides local settings. |
samples/workflow-retry-policy/src/.funcignore |
Configures sample packaging exclusions. |
samples/workflow-retry-policy/README.md |
Documents the sample. |
samples/README.md |
Lists the new sample. |
README.md |
Documents retry authoring and the sample. |
docs/workflows.md |
Documents the retry contract. |
docs/frds/0004-dynamic-workflows.md |
Records design and delivery decisions. |
docs/architecture.md |
Updates the workflow module map. |
Review details
Suppressed comments (2)
docs/frds/0004-dynamic-workflows.md:177
- These are not the authored field names implemented by this PR. The limits apply to the nested
backoff.initial,backoff.multiplier, andbackoff.maxfields.
`first_interval`, `backoff_coefficient`, and `max_interval` have fixed limits,
docs/frds/0004-dynamic-workflows.md:1272
- The idempotency key is not persisted at submission;
invoke_policy_handlerderives it when the Activity starts from the persisted workflow ID and node-instance ID. This test-plan item should describe the actual invariant.
- persist the effective policy and stable idempotency key at submission;
- Files reviewed: 26/26 changed files
- Comments generated: 5
- Review effort level: Balanced
💡 Configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
Scope exhausted-workflow log assertions to new host output and align retry documentation with the implemented plan-authored contract. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 7018a524-6dd1-4132-ada9-ba5f9cf47f26
Correlate the authoritative final orchestration failure and require the exact decoded task error, with a regression guard that rejects marker-bearing Activity wrappers. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 7018a524-6dd1-4132-ada9-ba5f9cf47f26
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 8c76b5ac-7baa-4c3f-aebf-49d44f0788fe
Laveesh Rohra (larohra)
left a comment
There was a problem hiding this comment.
Approved with non-blocking suggestions for alignment with the Python source conventions on feature/aca-sandboxes. These are NITs/low-priority follow-ups rather than merge requirements.
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 8c76b5ac-7baa-4c3f-aebf-49d44f0788fe
|
Laveesh Rohra (@larohra) Thank you for the detailed standards-alignment review. I addressed the three correctness/security items in commit
The remaining non-blocking refactoring and documentation suggestions are tracked in #205 so they can be handled as a focused follow-up without expanding this already large PR. That issue covers Pydantic outcome boundary models, a The full gate passes ( |
Summary
Extract the plan-authored Durable native retry execution foundation from #185 as an independently usable slice.
execution.retryauthoring for tool and Sub Agent tasks.RetryPolicyfor static, dynamic, and fan-out scheduling.This is the execution-foundation extraction from #185. PR #185 remains open and its remote branch is unchanged. After this PR merges, #185 will be rebased and reduced to retain only the
@workflow_tool(retry=...)authoring/discovery/catalog integration.Scope boundary
This PR intentionally does not add a retry argument or retry metadata to
@workflow_tool, propagate retry through discovery/registry/catalog paths, or implement decorator-over-plan precedence.Upstream dependency
durabletask1.10.0 drops Activity tags when reconstructing a schedule after a native retry timer. The initial attempt keepsdurabletask.displayName, but retry attempts currently fall back to the registered Activity function name in DTS.The runtime supplies tags correctly on the initial retry-aware Activity schedule. Consuming a future
durabletaskrelease containing the upstream fix is tracked separately and is not worked around in this PR.Validation
python -m ruff check src testspython -m mypy src— 44 source files cleanpython -m pytest --cache-clear --cov=./src/azure_functions_agents --cov-report=xml --cov-branch tests— 1166 passed, 55 deselectedpython -m pytest -q -m e2e tests/endtoend/test_workflow_native_retry_e2e.py— 2 passed in 25.31s against a real Functions host and Azurite, using task hubprstatusreportsFor the Windows host run, the sample was installed into its ignored
.python_packages/lib/site-packagesdirectory to match CI's packaged-dependency layout and avoid duplicate native gRPC loading. No machine-specific settings or package artifacts are committed.NOTE
I found the tag label issue when we had a retry. I created the upstream fix on

durabletask-python.