From 0123716173e0f4df9dc0e59200ab313266ce985c Mon Sep 17 00:00:00 2001 From: Richard McDaniel <1130888+rmcdaniel@users.noreply.github.com> Date: Tue, 27 Oct 2026 19:08:40 +0000 Subject: [PATCH] Wait for the embedded pending timer before cancelling it The embedded cancellation probe worked one queue job and then cancelled or terminated a pending_timer run. When that one job returned before the workflow task was available, the command landed before the timer was scheduled, and the embedded 2.11.4 reference recorded no TimerScheduled or TimerCancelled for terminate-pending-timer (#816's merge-group run 40561089085, MySQL existing-php corpus job 122392116528). The embedded path now works single jobs until the run has recorded TimerScheduled, for at most 15 seconds, and fails clearly otherwise, as the HTTP path already waits for the declared phase. Five embedded recordings of terminate-pending-timer and cancel-pending-timer on SQLite pass. Co-Authored-By: Claude Opus 5.5 --- .../server-parity/cancellation-probe.php | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/scripts/conformance/server-parity/cancellation-probe.php b/scripts/conformance/server-parity/cancellation-probe.php index d2f13e175..df76cfece 100644 --- a/scripts/conformance/server-parity/cancellation-probe.php +++ b/scripts/conformance/server-parity/cancellation-probe.php @@ -193,7 +193,20 @@ function embeddedCancellationObservation(array $fixture, string $workflowId, str if ($phase === 'before_claim') { cancelEmbeddedRun($stub->runId()); } elseif ($phase === 'pending_timer') { - Artisan::call('queue:work', ['connection' => 'database', '--queue' => $queue, '--once' => true, '--sleep' => 0, '--tries' => 1]); + // The declared phase is a scheduled timer. One queue job can return + // before the workflow task is available, so work single jobs until + // the run has recorded TimerScheduled (bounded), then cancel. + $deadline = microtime(true) + 15; + do { + Artisan::call('queue:work', ['connection' => 'database', '--queue' => $queue, '--once' => true, '--sleep' => 0, '--tries' => 1]); + $scheduled = in_array('TimerScheduled', array_column(embeddedCancellationHistory($stub->runId()), 'event_type'), true); + if (! $scheduled) { + usleep(100_000); + } + } while (! $scheduled && microtime(true) < $deadline); + if (! $scheduled) { + throw new \RuntimeException('The embedded run did not reach its pending timer before the cancellation phase.'); + } cancelEmbeddedRun($stub->runId()); } Artisan::call('queue:work', ['connection' => 'database', '--queue' => $queue, '--stop-when-empty' => true,