Skip to content

Extract plan-authored Durable retry execution - #193

Merged
Tsuyoshi Ushio (TsuyoshiUshio) merged 12 commits into
mainfrom
tsuyoshiushio-retry-execution-foundation
Sep 8, 2026
Merged

Extract plan-authored Durable retry execution#193
Tsuyoshi Ushio (TsuyoshiUshio) merged 12 commits into
mainfrom
tsuyoshiushio-retry-execution-foundation

Conversation

@TsuyoshiUshio

@TsuyoshiUshio Tsuyoshi Ushio (TsuyoshiUshio) commented Sep 2, 2026

Copy link
Copy Markdown
Contributor

Summary

Extract the plan-authored Durable native retry execution foundation from #185 as an independently usable slice.

  • Adds bounded execution.retry authoring for tool and Sub Agent tasks.
  • Freezes the effective policy into orchestration input for replay-safe dispatch.
  • Maps persisted policies to Durable RetryPolicy for static, dynamic, and fan-out scheduling.
  • Adds policy-aware Activity envelopes, runtime-owned failure classification, stable idempotency context, and sanitized exhaustion errors.
  • Preserves policy-free legacy histories and their existing Activity/result envelopes.
  • Preserves latest-main DTS display tags on orchestration start and policy-free/retry-aware tool and Sub Agent Activity calls.
  • Adds a plan-authored retry sample plus a real Functions-host E2E.
  • Updates finalized FRD 0004 to record the approved delivery split and replay-safety decisions.

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

durabletask 1.10.0 drops Activity tags when reconstructing a schedule after a native retry timer. The initial attempt keeps durabletask.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 durabletask release containing the upstream fix is tracked separately and is not worked around in this PR.

Validation

  • python -m ruff check src tests
  • python -m mypy src — 44 source files clean
  • python -m pytest --cache-clear --cov=./src/azure_functions_agents --cov-report=xml --cov-branch tests1166 passed, 55 deselected
  • python -m pytest -q -m e2e tests/endtoend/test_workflow_native_retry_e2e.py2 passed in 25.31s against a real Functions host and Azurite, using task hub prstatusreports

For the Windows host run, the sample was installed into its ignored .python_packages/lib/site-packages directory 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.
image

Tsuyoshi Ushio and others added 7 commits September 3, 2026 16:04
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

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.

🟡 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, and backoff.max fields.
`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_handler derives 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.

Comment thread tests/endtoend/test_workflow_native_retry_e2e.py Outdated
Comment thread docs/frds/0004-dynamic-workflows.md Outdated
Comment thread docs/frds/0004-dynamic-workflows.md Outdated
Comment thread docs/workflows.md Outdated
Comment thread src/azure_functions_agents/workflows/schema.py Outdated
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
Comment thread tests/endtoend/test_workflow_native_retry_e2e.py Outdated
Tsuyoshi Ushio added 2 commits September 4, 2026 14:54
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

@larohra Laveesh Rohra (larohra) 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.

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.

Comment thread src/azure_functions_agents/workflows/schema.py Outdated
Comment thread src/azure_functions_agents/workflows/native_retry.py Outdated
Comment thread src/azure_functions_agents/workflows/activity.py
Comment thread src/azure_functions_agents/workflows/activity.py
Comment thread src/azure_functions_agents/workflows/native_retry.py
Comment thread src/azure_functions_agents/workflows/activity.py
Comment thread docs/frds/0004-dynamic-workflows.md
Comment thread tests/endtoend/test_workflow_native_retry_e2e.py Outdated
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 8c76b5ac-7baa-4c3f-aebf-49d44f0788fe
@TsuyoshiUshio

Copy link
Copy Markdown
Contributor Author

Laveesh Rohra (@larohra) Thank you for the detailed standards-alignment review.

I addressed the three correctness/security items in commit 1edce829:

  • plan-schema validation errors no longer reflect caller-authored input values;
  • retry marker JSON rejects duplicate keys at every object level;
  • the E2E assertion inspects each nested failure message directly and requires the decoded terminal failure to have no innerFailure.

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 StrEnum for failure kinds, source/test basename alignment, concise source docstrings, and applying the newer minimal-decisions guidance prospectively while preserving the existing append-only FRD history.

The full gate passes (1174 passed, 55 deselected), and the real Functions Host sample E2E passes both scenarios with no skips.

@TsuyoshiUshio
Tsuyoshi Ushio (TsuyoshiUshio) merged commit 08b9d76 into main Sep 8, 2026
16 checks passed
@larohra
Laveesh Rohra (larohra) deleted the tsuyoshiushio-retry-execution-foundation branch September 8, 2026 23:05
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