feat(workflows): per-attempt task timeout and continue-on-error - #212
Draft
Tsuyoshi Ushio (TsuyoshiUshio) wants to merge 1 commit into
Draft
feat(workflows): per-attempt task timeout and continue-on-error#212Tsuyoshi Ushio (TsuyoshiUshio) wants to merge 1 commit into
Tsuyoshi Ushio (TsuyoshiUshio) wants to merge 1 commit into
Conversation
The retry foundation this slice depended on landed through PRs #193 and #207 rather than through the stale PR #186 branch, whose versions of `schema.py`, `activity.py`, and `engine.py` now conflict with the shipped contracts wholesale. Record the remaining timeout and continue-on-error slice as a clean reimplementation against current `main` instead of a rebase. The design makes all three `execution` fields optional so a deadline can be declared alone, resolves `@workflow_tool` precedence per field, bounds one attempt at the Activity boundary under a new retryable `timeout` classification, and lets a declared-continuation node commit a bounded controlled-failure result so its dependents run. Fail-fast is preserved for everything that cannot be continued: the wave selection loop keeps awaiting only when the completed instance both declared continuation and failed continuably, so a non-continuable failure never waits behind a slow sibling or a long timer. Both persisted keys are written only when authored, so a task that declares neither freezes and replays the payload the retry-only runtime wrote. An independent architecture review raised four blocking findings — whole-wave collection, unaddressed downgrade, an underspecified continuation contract, and missing coverage of mixed waves, cancellation races, and `for_each` failure shapes. Decisions 88 and 92-96 and the expanded test plan resolve them. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: f32b40a3-603f-48d9-ad29-43179afe265b
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.
Stack
This is PR 1 of 2 replacing the stale #170 decomposition stack.
continue_on_errortsuyoshiushio-workflow-task-timeout-continuationmaincustom_statusv3)The retry foundation these slices depended on landed through #193 and #207, not
through the original #185/#186 branches. #185 is closed; #186 and #187 are stale
and will be closed in favor of this stack.
Why this is a reimplementation, not a rebase
#186's branch still carries its own versions of
native_retry.py,activity.py,engine.py, andschema.pyfrom before #189/#193/#207 landed,so every file it touches conflicts with the shipped contracts wholesale.
Rebasing would reintroduce already-merged foundation work and enlarge review
scope, so this stack reimplements only the residual slices against current
main. FRD Decision 82 records that.What this PR contains
Design only. This is the FRD slice for review before implementation
(
AGENTS.mdlifecycle phase 2). No product code changes yet — implementationcommits follow on this branch once the design is signed off.
docs/frds/0004-dynamic-workflows.mdgains:Attempt timeout and continuation— the full design: all threeexecutionfields become optional so a deadline may be declared alone;per-field
@workflow_toolprecedence; the attempt deadline at the Activityboundary under a new retryable
timeoutclassification; and thecontinuation contract.
coverage.
review record.
Design highlights
Fail-fast is preserved. The reviewed alternative — collecting every
instance outcome whenever any node in the wave opted into continuation — would
let a non-continuable failure wait behind a slow sibling or a 24-hour timer and
could report a different node as the cause. Instead the selection loop decides
per completed instance: it keeps awaiting only when that instance both declared
continuation and failed continuably. Everything else raises immediately, as
today. Both inputs are persisted or derived from the outcome, so the choice is
replay-stable (Decision 88).
Continuation is a closed contract. Continuability is derived from the
persisted failure
kindthrough a runtime-owned map rather than added to theoutcome envelope, whose exact key set must keep validating failures written by
the previous runtime.
timeout,handler_transient,handler_terminal, andexecution_unknownare continuable;authorizationandhandler_contractnever are, and an opaque Durable failure with no classification still fails the
workflow (Decisions 87, 93).
A continued node commits a bounded object.
{"failed": true, "error_code", "error", "kind"}and nothing else — reusing the workflow-level failureenvelope would embed the aggregate
resultsmap in every continued instanceand let a fully failed
for_eachexpansion grow quadratically (Decision 94).The node stays
completed, so no scheduler state or persisted aggregatecontract changes.
The deadline bounds the wait, not the worker. A workflow tool handler runs
on a worker thread through
asyncio.to_threadand cannot be cancelled fromoutside, so a timed-out attempt is reported while it may still run; a Sub Agent
delivery is asynchronous and is cancelled. Both are the at-least-once exposure
the stable
idempotency_keyalready exists for. A Sub Agent's own resolvedspecialist timeout stays independent and keeps its
handler_transientclassification — the first bound to expire wins (Decisions 85, 95).
Compatibility
NotRequiredand written only when authored, so atask that declares neither freezes a payload byte-identical to the one the
retry-only runtime wrote and replays through exactly the same path
(Decision 91).
current reported cause.
workflow_execution_policy_invalidcoversonly what this slice adds.
are in flight: an older runtime's closed retryability map rejects the
timeoutkind and ignores both new keys. Drain or terminate first(Decision 92).
Reviews
An independent architecture review raised four blocking findings — whole-wave
collection deferring non-continuable failures, unaddressed downgrade, an
underspecified continuation contract, and missing coverage of mixed waves,
cancellation races, and
for_eachfailure shapes. All are resolved above andrecorded in §8.
Out of scope
Telemetry,
custom_statusschema v3, and the UI — those are PR 2 of thisstack. No
config/schema.pychange, sofront-matter-reference.mdisunaffected.