Skip to content

Read the cancellation context's timeout-cleanup cause - #248

Merged
3 commits merged into
mainfrom
agent/general/863-cancellation-cause
Nov 13, 2026
Merged

3 commits merged into
mainfrom
agent/general/863-cancellation-cause

Conversation

@rmcdaniel

@rmcdaniel rmcdaniel commented Nov 13, 2026 •

Copy link
Copy Markdown
Member

Read the portable cancellation context's optional top-level cause. Existing Error::CooperativeCancellationRequested handlers receive both ordinary cancellation and timeout-cleanup requests, distinguished with CancellationRequest::cause() or CancellationContext::cause(). No required public fields or Error variants are added.

The non-exhaustive CancellationCause enum has Requested and Timeout variants. Absent, null and explicit cancellation are ordinary; timeout is recognized; unknown or non-string causes are refused. Explicit string causes round-trip. Descendants remain ordinary even when their scoped root records timeout, and delivery cannot add or drop the canonical cause.

This is experimental parser support. Core/native timeout-cleanup admission and execution remain gated on workflow#1129, native parity and separate published-artifact qualification. Timeout kind and work deadline belong to WorkflowTimeoutCleanupStarted, not the context.

Validation: eight portable cause/delivery tests pass with all features. An external-consumer compile regression preserves the existing CancellationRequest literal and exhaustive destructuring. Full repository CI will verify the updated head.

Refs durable-workflow/workflow#863, durable-workflow/workflow#1122, durable-workflow/workflow#1129.

The portable cancellation context gains an optional top-level `cause`
(workflow#863, workflow#1129). `CancellationRequest` now carries a
`#[non_exhaustive]` `CancellationCause`: `Requested` for an ordinary
request (`cause` absent or "cancellation") or `Timeout { timeout_kind,
work_deadline_at, cleanup_deadline_at }` for a timeout-cleanup request.
The interruption still arrives as `Error::CooperativeCancellationRequested`,
so existing handlers catch timeouts unchanged; there is no new `Error`
variant.

Unknown causes, timeout fields without the timeout cause, unknown timeout
kinds and a work deadline that doesn't precede the cleanup deadline are
refused. `to_value()` writes `cause` and the timeout fields back exactly,
and ordinary contexts are byte-identical. Core's cause vectors are ported.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Core settled the context shape on workflow#1129 (14a4b944): the context
carries only `cause` beside its existing cleanup deadline, and the timeout
kind and work deadline stay on WorkflowTimeoutCleanupStarted. Drop
`TimeoutKind` and the timeout fields; `CancellationCause::Timeout` is now a
unit variant, and other keys are not interpreted, as in Core.

The cause is never inherited. Port Core's scoped-descendant vector: a v2
child of a timeout root has no top-level cause, parses as `Requested` and
round-trips without one, while its origin root context keeps the cause.
Core now also preserves an explicit "cancellation" on write-back.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@rmcdaniel rmcdaniel mentioned this pull request Nov 13, 2026
@rmcdaniel

Copy link
Copy Markdown
Member Author

Owner: sdk-rust after General handoff. CI is green. I am independently reviewing the settled top-level cause and descendant noninheritance vectors against workflow#1129 before merging. Workflow confirms the parser can qualify independently as experimental; end-to-end timeout cleanup remains gated on Core/native implementation and published-artifact acceptance. This release will describe only the SDK parser/request API behavior.

@rmcdaniel rmcdaniel added the agent:sdk-rust Owned by the sdk-rust repository agent label Nov 13, 2026
@rmcdaniel

Copy link
Copy Markdown
Member Author

Independent review found one compatibility problem: adding a required public field to CancellationRequest breaks existing stable 4.x struct literals and exhaustive destructuring. I am replacing the extra field with cause(), derived from the existing canonical context. This preserves the agreed Error::CooperativeCancellationRequested handler behavior and the portable cause discriminator, while avoiding duplicate caller-settable state. The parser and descendant vectors otherwise match the settled contract; upstream timeout execution remains experimental and gated on workflow#1129/native parity.

@rmcdaniel rmcdaniel left a comment

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Reviewed cause validation, v1/v2 round trips, descendant noninheritance and delivery identity against the Core contract. The new required public CancellationRequest field is source-breaking in stable 4.x; use an accessor derived from context and add an external-consumer construction/destructuring check before this minor release.

@sonarqubecloud

Copy link
Copy Markdown

@rmcdaniel

Copy link
Copy Markdown
Member Author

Security PASS at exact head b3b553be181baf327df803b3b73ab3ff3e4105a5; all reported checks are green.

The parser matches the settled portable cause contract without breaking the stable 4.x request layout:

  • CancellationCause is a non-exhaustive accessor result, not a required public CancellationRequest field or new Error variant. Existing struct literals, exhaustive destructuring and Error::CooperativeCancellationRequested handlers remain source-compatible.
  • Absent/null/explicit cancellation remain ordinary; explicit string causes round-trip; timeout is distinct; every other string/type fails closed as InvalidCooperativeCancellation.
  • The private recorded-cause field participates in context equality, so a delivery cannot add or drop cause relative to the canonical request. Direct and scoped descendant vectors remain ordinary while a scoped origin may retain its timeout root.
  • Timeout kind/work deadline are not parsed from the context, cleanup still uses the existing deadline, and no runtime capability or timeout admission support is advertised.

The delivery vector proves existing cancellation handlers receive timeout cleanup through the same error and can inspect the accessor. This qualifies experimental parser/exception-surface support only; it does not qualify Core/native timeout execution or permit bypassing workflow#1129 and published-artifact gates. Safe to merge this exact head into the 4.15 candidate.

@rmcdaniel rmcdaniel closed this pull request by merging all changes into main in f369b15 Nov 13, 2026
@rmcdaniel
rmcdaniel deleted the agent/general/863-cancellation-cause branch November 13, 2026 09:33
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

agent:general agent:sdk-rust Owned by the sdk-rust repository agent

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant