Repository navigation
Reopen a condition wait with its remaining timeout - #246
Merged
Merged
Conversation
A signal that left a wait_condition predicate false reopened the wait with the full timeout_seconds, so each such signal moved the timeout later. The deadline is now fixed at the first open: a reopen carries the whole seconds left (rounded up, never below 0) measured at the latest recorded history time, matching embedded semantics and the sdk-php#269 decision. Histories without recorded times keep the authored timeout, and histories written with full reopen timeouts still replay. Fixes #245. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The reopen's remaining time is now ceil(authored - (latest history time - first open time)) clamped to 0..authored, the rule sdk-php#271 uses, and sdk-php's falseWakeDeadlines vectors (180, 61, 0) are tests here. Refs #245. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Member
Author
|
General interim review (Security unavailable): exact head Determinism
Parity with sdk-php#271 (cd346723)
Server behaviour
Edge cases
Back-compat and tests
Non-blocking
The CHANGELOG merges cleanly with main. Merge only this SHA, or refresh it and send the head for a tree check if strict mode requires it. |
rmcdaniel
marked this pull request as ready for review
November 4, 2026 03:19
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Member
Author
|
Merged at General's verification of refreshed head
|
This was referenced Nov 4, 2026
rmcdaniel
added a commit
that referenced
this pull request
Nov 4, 2026
Resolve two additive conflicts: keep both new workflow.py helpers (_history_event_time from #246 and _typed_activity_result) and both corpus WORKFLOWS registrations. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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.



Fixes #245. This is the Python counterpart of sdk-php#269 (a), following General's ruling: the deadline is fixed at the first open, and a reopen carries the remaining time.
Bug
ctx.wait_condition(predicate, key=..., timeout=300)opens a wait withtimeout_seconds: 300. A signal that leaves the predicate false re-drives the workflow, and the SDK reopened the wait with the full 300 seconds again. Each such signal pushed the timeout later. A wait opened at 00:00 and signalled at 00:02 timed out at 00:07.Fix (
src/durable_workflow/workflow.py)ConditionWaitOpenedrow's time (timestamp, elserecorded_at), and the latest recorded time in the history.pendinggets a copy of theWaitCondition. The copy'stimeout_secondsisclamp(ceil(authored - (latest history time - first open time)), 0, authored). This is the same rule as sdk-php#271.WaitConditionunchanged.ctx.now()stays at the workflow start. The Server records the reopen at its completion time, which is at most one task latency later.timeout_secondson a recorded open is not compared on replay. So histories written with full reopen timeouts still replay; a control covers this.wait_conditiondocstring now says the deadline is fixed at the first open.Not mirrored from sdk-php#271: refusing a recorded reopen whose
timeout_secondsexceeds the authored timeout. Python does not compare a recorded open's timeout today, and this PR doesn't add that check.Not in this PR: sdk-php#269 (b)
The duplicate
open_condition_waiton a non-signal task is not changed here. sdk-php's analysis applies to Python too. A worker can't tell a signal-resume task from another task, and if it doesn't re-send on a signal-resume task, the wait is satisfied with nothing reopened. The accepted fix iscondition_wait_occurrence_idplus the Server's idempotent reopen. Python can adopt that once the Server issue lands, as a separate change.Evidence
tests/test_wait_condition.py(TestReopenedWaitRemainingTimeout):falseWakeDeadlinesvectors: a 300 s wait opened at 02:36:05.295 gives 180, 61 and 0 for signals at 02:38:05.345, 02:40:05 and 02:41:06.main's source the 7 reopen tests fail (they get 300), and the 3 controls pass.condition-wait-reopen-remaining-timeoutexpectsopen_condition_waitwithtimeout_seconds: 180. It passes at this head and fails onmain's source through the official corpus test.RemainingTimeoutApprovalis registered inWORKFLOWS.Local runs at 6e54e5d (lab2, Python 3.13, this worktree's source)
ruff check src tests: clean.mypy src/durable_workflow: no issues.tests, excludingtests/integration): 4804 passed, 4 skipped atbae15122. At this head, the wait-condition and corpus tests give 138 passed.validate-regression-corpus.py --base-ref origin/main:status: pass.Not merged or released.
🤖 Generated with Claude Code