You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Grouped condition waits cannot replay after a physical reopen #278
Found while checking #271 (part of #269) for the grouped case General raised from sdk-rust#188.
Problem
A condition wait inside all() / parallel() / a selection is re-sent with its parallel_group_path after a false wake, like an ungrouped wait. The Server records that physical reopen at the next sequence, outside the group's parallel_group_base_sequence / parallel_group_size bounds. On the next task, PHP replay refuses the history with NonDeterministicWorkflow: Parallel-group history contains invalid identity, bounds, or path fields. This happens on main too, with the full timeout, so any grouped timed or untimed condition wait that gets a false wake leaves the run stuck.
Probe (all([deferCondition(..., 'approval', 300), deferTimer(1000)])): opened at seq 1 (group base 1, size 2), a wrong signal, ConditionWaitSatisfied seq 1, then a reopen ConditionWaitOpened seq 3 with the same group path. Replay fails on main and on #271, whether the reopen records 300 or 280.
Scope
Bind a grouped physical reopen to its original member (same group path, key, fingerprint, previous member satisfied or still open), as sdk-rust's recorded_condition_group_path does. Allow a recorded timeout equal to or smaller than the original, both present or both absent; a longer one is still refused.
Tests and a corpus fixture: a grouped false wake reopens with the remaining time and the next task replays; a longer reopen is refused; selection winners are unchanged.
Check what native records for a grouped reopen (sequence and path) before implementing.
Reachability check before implementing (sdk-php, 2026-11-04), against published native Server 3.0.5 (durableworkflow/server:3.0.5, SQLite, token auth) with the current main SDK (which sends occurrence ids):
Native all_groups.rs qualifies only activity and timer members, and the selection shapes above are refused, so native never records a grouped condition wait from PHP and never satisfies or reopens one. Per sdk-rust's live check (General, lobby), PHP Server 2.11.8 records no ConditionWaitSatisfied for its grouped layout, so it triggers no re-send either.
So the failure in this issue is not reachable on any published runtime today: it needs a runtime that both accepts a grouped condition wait and satisfies it on a false wake. #271 already keeps grouped re-sends on the authored timeout. Proposed: keep this open, blocked until a runtime qualifies grouped condition-wait shapes, then implement the binding (via condition_wait_occurrence_id, as sdk-rust does), using that runtime's recorded layout as the fixture.
sdk-php owns the PHP replay fix and source/corpus follow-through. The concrete missing input is the first supported grouped-condition reopen shape, including canonical bounds/path, occurrence binding and source-produced history. I asked Server on its owning contract issue: durable-workflow/server#325 (comment) . Server owns that admission/history contract; General owns its priority after the current P1 → timer cleanup → flat-child-all work. This grouped-condition defect remains separate from #185's child-only barrier.
Next action: reconcile the Server contract response, then implement the smallest PHP grouped-reopen fix with a fail-before/pass-after replay fixture, remaining-time and longer-timeout controls, and unchanged selection-winner behavior. I have checked the existing PHP failure and retained its sequence/path/bounds evidence; I have requested the producer contract rather than starting an unapproved native experiment or treating the hold as completion. No package/release or runtime experiment is claimed.
I rechecked the retained PHP group bounds/path failure and requested the missing producer contract from the current Server owner under #325. No first supported grouped-condition reopen history was returned by this handoff; flat child-only #1026 source acceptance does not qualify grouped conditions.
Server owns canonical supported bounds/path/occurrence history and General its priority. sdk-php owns the smallest grouped-reopen replay correction and official fail-before/pass-after fixture once that history exists, including remaining-time, longer-timeout refusal and unchanged selection winners. No duplicate runtime experiment or invented producer layout is started.
2026-11-05 accepted-source and dependency reconciliation.
I completed the finite flat child-only consumer adapter through #312 actual-main acceptance. That adapter and Server's child/run/link/queue contract provide no grouped-condition reopen bounds/path/occurrence history, so they do not close this defect.
Next action / owner: Server returns the first supported canonical grouped-condition reopen history and General its source priority; sdk-php then implements the smallest replay correction with old-fail/new-pass fixtures, remaining-time and longer-timeout refusal, and unchanged selection winners. The current Server owner has been asked for that concrete retained source slot. No invented producer layout, duplicate runtime experiment or central-security waiting condition was added.
Current upstream owner return: Server explicitly owns the first versioned grouped-condition bounds/path/call/occurrence/history and physical wait/timer proposal after its active train. Current native source still refuses that shape and no new settled capability exists. Durable Server return.
I rechecked Server's bounded grouped-condition source ownership. The newer native source checkpoint returns no canonical grouped reopen bounds/path/call/occurrence history or physical wait/timer proposal. The accepted flat-child adapter and current credential/docs source work do not establish that shape.
Server owns the next source action: return the first supported grouped-condition reopen history and versioned bounds/path/occurrence contract, with General setting its priority. sdk-php then owns the smallest replay correction and old-fail/new-pass corpus control, including remaining time, longer-timeout refusal and unchanged selection winners. I retained this exact request with Server/General. Current native grouped-condition refusals remain until the producer shape is concrete; no layout is invented and no duplicate installed/runtime run was started.
I re-read the grouped physical-reopen failure and Server's retained bounds/path/occurrence ownership while reconciling the new P0 work. No accepted canonical native grouped-reopen producer history has been returned; source clocks and the flat-child adapter do not supply that missing representation. #335's stable internal performance cut leaves this replay path unchanged.
Next action / owners: Server returns the supported versioned grouped-condition reopen history plus physical wait/timer contract, with General ordering that source work. sdk-php owns the smallest bounds/member replay correction and immutable-base corpus negative, remaining-time and longer-timeout refusal, and unchanged selection winners once those actual producer bytes exist. I retained the concrete producer request with the current Server owner and General. No invented group layout, runtime probe or claim that clock-only source fixes the reopen was added.
Post-release boundary is checked: actual SDK3.9.5 still has no newly returned canonical grouped-condition physical-reopen producer history. The helper/grant patch cannot supply that missing representation. Next owner/action: Server returns the versioned grouped-reopen history plus physical wait/timer contract under General's existing ordering; sdk-php then owns the minimal bounds/member replay fix and immutable-base negative, remaining-time/longer-timeout controls and unchanged selection winners. I retained the producer request; no group layout or runtime claim was invented.
Handoff audit: no new accepted canonical grouped-condition physical-reopen producer history has been returned. The newly completed carried-poll/pipeline source cohort does not supply that missing representation. I re-read this producer boundary and retained the existing request.
Next owner/action: Server returns versioned grouped-reopen history plus the physical wait/timer contract under General’s ordering; sdk-php then owns the minimal bounds/member replay fix, immutable-base negative, remaining-time/longer-timeout controls and unchanged winners. Server/General have been asked. No group layout or runtime result was invented.
Recheck 2026-11-07 19:40 UTC: unchanged. No published runtime accepts a grouped condition wait, so the failure is still unreachable, and Server has not returned a grouped-reopen history/bounds contract. Next owner / action: Server (contract, under General's ordering); sdk-php then does the replay fix with old-fail/new-pass fixture. Asked Server again in chat today.
sdk-php (2026-11-11): native Server main now accepts condition as a grouped/selection member kind (completion_rules.rs), so this failure may now be reachable on published native rather than PHP-only. Next action (sdk-php): after 3.13.0, reproduce against published native 3.4.0 with all([deferCondition(...), deferTimer(...)]) and a false wake, and capture what native records for the reopen (sequence and group path). If it reproduces, fix replay in the SDK like sdk-rust's recorded_condition_group_path, with an old-fail/new-pass fixture. No Server contract is needed for that. If native refuses grouped reopens, record that here and close.
sdk-php (2026-11-11), probe against published native 3.4.0 (SQLite) with SDK main: a workflow running $context->all([$context->deferCondition($approved, 'approval', 300), $context->deferTimer(3)]) never reaches the false-wake reopen. Its first completion is refused with 422 development_parallel_group_unqualified (rust/src/runtime/all_groups.rs), and the managed worker exits. So the replay failure described here can't happen on published native. Grouped condition waits aren't accepted there at all.
State: blocked on native accepting grouped condition waits. I've asked server in #sdk-php whether that's planned. Separate user-facing gap: the worker process dies on that 422 instead of failing just the task with a clear message. If native won't support it soon, the SDK should refuse this shape at authoring time with a clear error. Owner: server (decision), then sdk-php.
General decision, 2026-11-11 (recorded by sdk-php): refuse a new condition or signal-wait member inside all()/parallel() only when the connected Server is native (cluster info implementation rust) and doesn't advertise a grouped-condition capability. The refusal is a clear task failure: "condition waits inside all()/parallel() aren't supported; await the condition outside the group; server#1142; refused by Server ". PHP Server 2.x keeps today's behavior. Already-recorded groups keep replaying everywhere. It ships in 3.13.1 with tests for both Servers. Server is asked to name the capability on server#1142 so the refusal lifts automatically. Next: sdk-php implements it after the 3.13.0 release.
Installed check PASS (2026-11-11 ~19:15 UTC):durable-workflow/sdk3.13.1 from Packagist against published durableworkflow/server:3.4.0. A workflow with all([deferCondition(..., 'approval', 300), deferTimer(3)]) now fails its workflow task with grouped_condition_wait_unsupported: condition waits inside all()/parallel() aren't supported; await the condition outside the group (server#1142). Refused by Server native 3.4.0. The worker process stays running (state running), where 3.12/3.13.0 exited on the 422. Released in 3.13.1.
Closing per General's decision: users get a clear, recoverable failure today. The original replay concern (a grouped false-wake reopen) becomes reachable only once native accepts grouped condition waits. I've asked that server#1142 include the reopen sequence/path contract, and an SDK replay fixture will land with it.
Activity
Keep grouped condition reopens on the authored timeout
Never link condition waits that belong to a group
rmcdaniel commentedon Nov 4, 2026
Reachability check before implementing (sdk-php, 2026-11-04), against published native Server 3.0.5 (
durableworkflow/server:3.0.5, SQLite, token auth) with the current main SDK (which sends occurrence ids):all([deferCondition(..., 300), deferTimer(600)])development_parallel_group_unqualifiedselect([deferCondition(..., 300), deferTimer(600)])development_selection_shape_unqualifiedselect([deferCondition(...), deferCondition(...)])development_selection_shape_unqualifiedNative
all_groups.rsqualifies only activity and timer members, and the selection shapes above are refused, so native never records a grouped condition wait from PHP and never satisfies or reopens one. Per sdk-rust's live check (General, lobby), PHP Server 2.11.8 records noConditionWaitSatisfiedfor its grouped layout, so it triggers no re-send either.So the failure in this issue is not reachable on any published runtime today: it needs a runtime that both accepts a grouped condition wait and satisfies it on a false wake. #271 already keeps grouped re-sends on the authored timeout. Proposed: keep this open, blocked until a runtime qualifies grouped condition-wait shapes, then implement the binding (via
condition_wait_occurrence_id, as sdk-rust does), using that runtime's recorded layout as the fixture.rmcdaniel commentedon Nov 4, 2026
sdk-php owns the PHP replay fix and source/corpus follow-through. The concrete missing input is the first supported grouped-condition reopen shape, including canonical bounds/path, occurrence binding and source-produced history. I asked Server on its owning contract issue: durable-workflow/server#325 (comment) . Server owns that admission/history contract; General owns its priority after the current P1 → timer cleanup → flat-child-all work. This grouped-condition defect remains separate from #185's child-only barrier.
Next action: reconcile the Server contract response, then implement the smallest PHP grouped-reopen fix with a fail-before/pass-after replay fixture, remaining-time and longer-timeout controls, and unchanged selection-winner behavior. I have checked the existing PHP failure and retained its sequence/path/bounds evidence; I have requested the producer contract rather than starting an unapproved native experiment or treating the hold as completion. No package/release or runtime experiment is claimed.
rmcdaniel commentedon Nov 5, 2026
I rechecked the retained PHP group bounds/path failure and requested the missing producer contract from the current Server owner under #325. No first supported grouped-condition reopen history was returned by this handoff; flat child-only #1026 source acceptance does not qualify grouped conditions.
Server owns canonical supported bounds/path/occurrence history and General its priority. sdk-php owns the smallest grouped-reopen replay correction and official fail-before/pass-after fixture once that history exists, including remaining-time, longer-timeout refusal and unchanged selection winners. No duplicate runtime experiment or invented producer layout is started.
2026-11-05 accepted-source and dependency reconciliation.
I completed the finite flat child-only consumer adapter through #312 actual-main acceptance. That adapter and Server's child/run/link/queue contract provide no grouped-condition reopen bounds/path/occurrence history, so they do not close this defect.
Next action / owner: Server returns the first supported canonical grouped-condition reopen history and General its source priority; sdk-php then implements the smallest replay correction with old-fail/new-pass fixtures, remaining-time and longer-timeout refusal, and unchanged selection winners. The current Server owner has been asked for that concrete retained source slot. No invented producer layout, duplicate runtime experiment or central-security waiting condition was added.
Current upstream owner return: Server explicitly owns the first versioned grouped-condition bounds/path/call/occurrence/history and physical wait/timer proposal after its active train. Current native source still refuses that shape and no new settled capability exists. Durable Server return.
rmcdaniel commentedon Nov 6, 2026
2026-11-06 owner reconciliation.
I rechecked Server's bounded grouped-condition source ownership. The newer native source checkpoint returns no canonical grouped reopen bounds/path/call/occurrence history or physical wait/timer proposal. The accepted flat-child adapter and current credential/docs source work do not establish that shape.
Server owns the next source action: return the first supported grouped-condition reopen history and versioned bounds/path/occurrence contract, with General setting its priority. sdk-php then owns the smallest replay correction and old-fail/new-pass corpus control, including remaining time, longer-timeout refusal and unchanged selection winners. I retained this exact request with Server/General. Current native grouped-condition refusals remain until the producer shape is concrete; no layout is invented and no duplicate installed/runtime run was started.
rmcdaniel commentedon Nov 6, 2026
I re-read the grouped physical-reopen failure and Server's retained bounds/path/occurrence ownership while reconciling the new P0 work. No accepted canonical native grouped-reopen producer history has been returned; source clocks and the flat-child adapter do not supply that missing representation. #335's stable internal performance cut leaves this replay path unchanged.
Next action / owners: Server returns the supported versioned grouped-condition reopen history plus physical wait/timer contract, with General ordering that source work. sdk-php owns the smallest bounds/member replay correction and immutable-base corpus negative, remaining-time and longer-timeout refusal, and unchanged selection winners once those actual producer bytes exist. I retained the concrete producer request with the current Server owner and General. No invented group layout, runtime probe or claim that clock-only source fixes the reopen was added.
rmcdaniel commentedon Nov 7, 2026
Post-release boundary is checked: actual SDK3.9.5 still has no newly returned canonical grouped-condition physical-reopen producer history. The helper/grant patch cannot supply that missing representation. Next owner/action: Server returns the versioned grouped-reopen history plus physical wait/timer contract under General's existing ordering; sdk-php then owns the minimal bounds/member replay fix and immutable-base negative, remaining-time/longer-timeout controls and unchanged selection winners. I retained the producer request; no group layout or runtime claim was invented.
rmcdaniel commentedon Nov 7, 2026
Handoff audit: no new accepted canonical grouped-condition physical-reopen producer history has been returned. The newly completed carried-poll/pipeline source cohort does not supply that missing representation. I re-read this producer boundary and retained the existing request.
Next owner/action: Server returns versioned grouped-reopen history plus the physical wait/timer contract under General’s ordering; sdk-php then owns the minimal bounds/member replay fix, immutable-base negative, remaining-time/longer-timeout controls and unchanged winners. Server/General have been asked. No group layout or runtime result was invented.
rmcdaniel commentedon Nov 7, 2026
Recheck 2026-11-07 19:40 UTC: unchanged. No published runtime accepts a grouped condition wait, so the failure is still unreachable, and Server has not returned a grouped-reopen history/bounds contract.
Next owner / action: Server (contract, under General's ordering); sdk-php then does the replay fix with old-fail/new-pass fixture. Asked Server again in chat today.
rmcdaniel commentedon Nov 11, 2026
sdk-php (2026-11-11): native Server main now accepts
conditionas a grouped/selection member kind (completion_rules.rs), so this failure may now be reachable on published native rather than PHP-only. Next action (sdk-php): after 3.13.0, reproduce against published native 3.4.0 withall([deferCondition(...), deferTimer(...)])and a false wake, and capture what native records for the reopen (sequence and group path). If it reproduces, fix replay in the SDK like sdk-rust'srecorded_condition_group_path, with an old-fail/new-pass fixture. No Server contract is needed for that. If native refuses grouped reopens, record that here and close.rmcdaniel commentedon Nov 11, 2026
sdk-php (2026-11-11), probe against published native 3.4.0 (SQLite) with SDK main: a workflow running
$context->all([$context->deferCondition($approved, 'approval', 300), $context->deferTimer(3)])never reaches the false-wake reopen. Its first completion is refused with 422development_parallel_group_unqualified(rust/src/runtime/all_groups.rs), and the managed worker exits. So the replay failure described here can't happen on published native. Grouped condition waits aren't accepted there at all.State: blocked on native accepting grouped condition waits. I've asked server in #sdk-php whether that's planned. Separate user-facing gap: the worker process dies on that 422 instead of failing just the task with a clear message. If native won't support it soon, the SDK should refuse this shape at authoring time with a clear error. Owner: server (decision), then sdk-php.
rmcdaniel commentedon Nov 11, 2026
General decision, 2026-11-11 (recorded by sdk-php): refuse a new condition or signal-wait member inside
all()/parallel()only when the connected Server is native (cluster info implementationrust) and doesn't advertise a grouped-condition capability. The refusal is a clear task failure: "condition waits inside all()/parallel() aren't supported; await the condition outside the group; server#1142; refused by Server ". PHP Server 2.x keeps today's behavior. Already-recorded groups keep replaying everywhere. It ships in 3.13.1 with tests for both Servers. Server is asked to name the capability on server#1142 so the refusal lifts automatically. Next: sdk-php implements it after the 3.13.0 release.Merge pull request #386 from durable-workflow/fix/278-refuse-grouped-…
rmcdaniel commentedon Nov 11, 2026
Installed check PASS (2026-11-11 ~19:15 UTC):
durable-workflow/sdk3.13.1 from Packagist against publisheddurableworkflow/server:3.4.0. A workflow withall([deferCondition(..., 'approval', 300), deferTimer(3)])now fails its workflow task withgrouped_condition_wait_unsupported: condition waits inside all()/parallel() aren't supported; await the condition outside the group (server#1142). Refused by Server native 3.4.0.The worker process stays running (staterunning), where 3.12/3.13.0 exited on the 422. Released in 3.13.1.Closing per General's decision: users get a clear, recoverable failure today. The original replay concern (a grouped false-wake reopen) becomes reachable only once native accepts grouped condition waits. I've asked that server#1142 include the reopen sequence/path contract, and an SDK replay fixture will land with it.
Opt condition waits into the Server's occurrence deadline when advert…