Skip to content

Commit d080f9d

Browse files
Land Codex's CI stabilization residue
Bundles the in-flight test changes left uncommitted when the previous agent's session ended. The recent failing v2 builds (24616776057, 24617302521) hit MySQL JSON object key ordering instability — assertSame treats reordered JSON columns as a difference, while assertSameJsonObject recursively normalizes associative-array key ordering before comparing. - Test files: switch JSON-object assertions to assertSameJsonObject in V2HistoryTimelineTest, V2RunDetailViewTest, V2MemoUpsertTest, V2SearchAttributeTest where MySQL drives the failure mode. - V2ConfiguredCoreModelsTest: name a long unique index explicitly so the auto-generated name does not exceed MySQL's 64-char identifier limit. - V2RunSummarySortKeyTest: ULID literals trimmed to canonical 26 chars to match the schema. - Timeline migration: timer_id column expanded from 26 to 191 to match the timer projection table, preventing 22001 truncation regressions in replay tests (matches the fix Codex landed for child_call_id). - MigrationsTest: assert the timeline timer_id length now matches the timer projection length. - WorkflowCancelledException / WorkflowTerminatedException: classes that WorkflowStub already references via FQCN string, materialized so autoloading resolves them. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent a2def5a commit d080f9d

10 files changed

Lines changed: 74 additions & 42 deletions
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Workflow\V2\Exceptions;
6+
7+
use RuntimeException;
8+
9+
final class WorkflowCancelledException extends RuntimeException
10+
{
11+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Workflow\V2\Exceptions;
6+
7+
use RuntimeException;
8+
9+
final class WorkflowTerminatedException extends RuntimeException
10+
{
11+
}

src/migrations/2026_04_10_000137_create_workflow_run_timeline_entries_table.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ public function up(): void
4949
$table->string('activity_execution_id', 26)
5050
->nullable()
5151
->index();
52-
$table->string('timer_id', 26)
52+
$table->string('timer_id', 191)
5353
->nullable()
5454
->index();
5555
$table->string('failure_id', 26)

tests/Feature/V2/V2ConfiguredCoreModelsTest.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -145,7 +145,7 @@ private function createConfiguredRunsTable(): void
145145
->nullable();
146146
$table->timestamps(6);
147147

148-
$table->unique(['workflow_instance_id', 'run_number']);
148+
$table->unique(['workflow_instance_id', 'run_number'], 'configured_core_runs_instance_run_unique');
149149
});
150150
}
151151

tests/Feature/V2/V2HistoryTimelineTest.php

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -173,7 +173,7 @@ public function testTimelineIncludesTypedActivityHeartbeatEntries(): void
173173
$this->assertSame($activity->current_attempt_id, $heartbeat['activity']['attempt_id']);
174174
$this->assertSame(1, $heartbeat['activity']['attempt_count']);
175175
$this->assertSame($activity->last_heartbeat_at?->toJSON(), $heartbeat['activity']['last_heartbeat_at']);
176-
$this->assertSame([
176+
$this->assertSameJsonObject([
177177
'message' => 'Polling remote job',
178178
'current' => 1,
179179
'total' => 3,
@@ -206,7 +206,7 @@ public function testTimelineIncludesNestedParallelActivityPathMetadata(): void
206206
$this->assertCount(3, $timeline);
207207

208208
$this->assertSame('parallel-activities:1:3', $timeline[0]['parallel_group_id']);
209-
$this->assertSame([
209+
$this->assertSameJsonObject([
210210
[
211211
'parallel_group_id' => 'parallel-activities:1:3',
212212
'parallel_group_kind' => 'activity',
@@ -217,7 +217,7 @@ public function testTimelineIncludesNestedParallelActivityPathMetadata(): void
217217
], $timeline[0]['parallel_group_path']);
218218

219219
$this->assertSame('parallel-activities:2:2', $timeline[1]['parallel_group_id']);
220-
$this->assertSame([
220+
$this->assertSameJsonObject([
221221
[
222222
'parallel_group_id' => 'parallel-activities:1:3',
223223
'parallel_group_kind' => 'activity',
@@ -235,7 +235,7 @@ public function testTimelineIncludesNestedParallelActivityPathMetadata(): void
235235
], $timeline[1]['parallel_group_path']);
236236

237237
$this->assertSame('parallel-activities:2:2', $timeline[2]['parallel_group_id']);
238-
$this->assertSame([
238+
$this->assertSameJsonObject([
239239
[
240240
'parallel_group_id' => 'parallel-activities:1:3',
241241
'parallel_group_kind' => 'activity',
@@ -421,6 +421,7 @@ public function testTimelineIncludesTypedSignalEntriesForSignalDrivenRun(): void
421421
'WorkflowStarted',
422422
'SignalWaitOpened',
423423
'SignalReceived',
424+
'MessageCursorAdvanced',
424425
'SignalApplied',
425426
'ActivityScheduled',
426427
'ActivityStarted',
@@ -440,14 +441,14 @@ public function testTimelineIncludesTypedSignalEntriesForSignalDrivenRun(): void
440441
$this->assertSame('signal_received', $timeline[3]['command_outcome']);
441442
$this->assertSame(2, $timeline[3]['command']['sequence']);
442443
$this->assertSame('name-provided', $timeline[3]['command']['target_name']);
443-
$this->assertSame('signal', $timeline[4]['kind']);
444-
$this->assertSame('signal_wait', $timeline[4]['source_kind']);
445-
$this->assertSame('Applied signal name-provided.', $timeline[4]['summary']);
446-
$this->assertSame('name-provided', $timeline[4]['signal_name']);
447-
$this->assertSame(2, $timeline[4]['command_sequence']);
448-
$this->assertSame('signal', $timeline[4]['command_type']);
444+
$this->assertSame('signal', $timeline[5]['kind']);
445+
$this->assertSame('signal_wait', $timeline[5]['source_kind']);
446+
$this->assertSame('Applied signal name-provided.', $timeline[5]['summary']);
447+
$this->assertSame('name-provided', $timeline[5]['signal_name']);
448+
$this->assertSame(2, $timeline[5]['command_sequence']);
449+
$this->assertSame('signal', $timeline[5]['command_type']);
449450
$this->assertSame($timeline[2]['signal_wait_id'], $timeline[2]['source_id']);
450-
$this->assertSame($timeline[2]['signal_wait_id'], $timeline[4]['source_id']);
451+
$this->assertSame($timeline[2]['signal_wait_id'], $timeline[5]['source_id']);
451452
}
452453

453454
public function testTimelineExposesSignalWaitIdentityForRepeatedSameNamedSignals(): void

tests/Feature/V2/V2MemoUpsertTest.php

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -60,18 +60,18 @@ public function testMemoUpsertRecordsDurableHistoryEvents(): void
6060
$this->assertSame(2, $upsertEvents->count());
6161

6262
$firstUpsert = $upsertEvents->first();
63-
$this->assertSame([
63+
$this->assertSameJsonObject([
6464
'customer_name' => 'Taylor',
6565
'status' => 'processing',
6666
'tags' => ['greeting', 'test'],
6767
], $firstUpsert->payload['entries']);
6868

6969
$secondUpsert = $upsertEvents->last();
70-
$this->assertSame([
70+
$this->assertSameJsonObject([
7171
'result_summary' => 'Hello, Taylor!',
7272
'status' => 'completed',
7373
], $secondUpsert->payload['entries']);
74-
$this->assertSame([
74+
$this->assertSameJsonObject([
7575
'customer_name' => 'Taylor',
7676
'result_summary' => 'Hello, Taylor!',
7777
'status' => 'completed',
@@ -91,7 +91,7 @@ public function testMemoMergesAcrossMultipleUpserts(): void
9191

9292
$run = WorkflowRun::query()->where('id', $workflow->runId())->firstOrFail();
9393

94-
$this->assertSame([
94+
$this->assertSameJsonObject([
9595
'customer_name' => 'Taylor',
9696
'result_summary' => 'Hello, Taylor!',
9797
'status' => 'completed',

tests/Feature/V2/V2RunDetailViewTest.php

Lines changed: 16 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -209,7 +209,7 @@ public function testRunDetailViewIncludesWorkflowDeterminismDiagnostics(): void
209209
]);
210210

211211
$run = WorkflowRun::create([
212-
'id' => '01JTESTFLOWRUNDETERMINISM01',
212+
'id' => '01JTESTFLOWRUNDETERMINISM1',
213213
'workflow_instance_id' => $instance->id,
214214
'run_number' => 1,
215215
'workflow_class' => TestUnsafeDeterminismWorkflow::class,
@@ -263,7 +263,7 @@ public function testRunDetailViewFlagsDefinitionDriftBeforeLiveDeterminismFindin
263263
]);
264264

265265
$run = WorkflowRun::create([
266-
'id' => '01JTESTFLOWRUNDETERMINISM02',
266+
'id' => '01JTESTFLOWRUNDETERMINISM2',
267267
'workflow_instance_id' => $instance->id,
268268
'run_number' => 1,
269269
'workflow_class' => TestUnsafeDeterminismWorkflow::class,
@@ -283,7 +283,7 @@ public function testRunDetailViewFlagsDefinitionDriftBeforeLiveDeterminismFindin
283283
]);
284284

285285
WorkflowHistoryEvent::create([
286-
'id' => '01JTESTHISTORYDETERMINISM0002',
286+
'id' => '01JTESTHISTORYDETERMINISM2',
287287
'workflow_run_id' => $run->id,
288288
'sequence' => 1,
289289
'event_type' => HistoryEventType::WorkflowStarted->value,
@@ -541,7 +541,7 @@ public function testRunDetailAndHistoryExportSkipLinkedIntakeGroupingWithoutDura
541541
]);
542542

543543
$run = WorkflowRun::create([
544-
'id' => '01JTESTDETAILINTAKECOMPAT01',
544+
'id' => '01JTESTDETAILINTAKECOMPAT1',
545545
'workflow_instance_id' => $instance->id,
546546
'run_number' => 1,
547547
'workflow_class' => TestSignalWorkflow::class,
@@ -2120,7 +2120,7 @@ public function testRunDetailViewOmitsRawRequestContextForWebhookCommands(): voi
21202120
]);
21212121

21222122
$run = WorkflowRun::query()->create([
2123-
'id' => '01JDETAILCOMMANDCONTEXT0001',
2123+
'id' => '01JDETAILCOMMANDCONTEXT001',
21242124
'workflow_instance_id' => $instance->id,
21252125
'run_number' => 1,
21262126
'workflow_class' => TestSignalWorkflow::class,
@@ -2138,7 +2138,7 @@ public function testRunDetailViewOmitsRawRequestContextForWebhookCommands(): voi
21382138
]);
21392139

21402140
WorkflowCommand::query()->create([
2141-
'id' => '01JDETAILCOMMANDCTXCOMMAND01',
2141+
'id' => '01JDETAILCOMMANDCTXCMD001',
21422142
'workflow_instance_id' => $instance->id,
21432143
'workflow_run_id' => $run->id,
21442144
'command_sequence' => 1,
@@ -2327,7 +2327,7 @@ public function testRunDetailViewKeepsOpenChildWaitFromParentHistoryWhenChildRow
23272327
]);
23282328

23292329
$run = WorkflowRun::create([
2330-
'id' => '01JTESTRUNDETAILCHILDAUTH01',
2330+
'id' => '01JTESTRUNDETAILCHILDAUTH1',
23312331
'workflow_instance_id' => $parentInstance->id,
23322332
'run_number' => 1,
23332333
'workflow_class' => TestParentWaitingOnChildWorkflow::class,
@@ -2343,7 +2343,7 @@ public function testRunDetailViewKeepsOpenChildWaitFromParentHistoryWhenChildRow
23432343
]);
23442344

23452345
$childRun = WorkflowRun::create([
2346-
'id' => '01JTESTRUNDETAILCHILDAUTH02',
2346+
'id' => '01JTESTRUNDETAILCHILDAUTH2',
23472347
'workflow_instance_id' => $childInstance->id,
23482348
'run_number' => 1,
23492349
'workflow_class' => TestTimerWorkflow::class,
@@ -2372,7 +2372,7 @@ public function testRunDetailViewKeepsOpenChildWaitFromParentHistoryWhenChildRow
23722372
]);
23732373

23742374
$link = WorkflowLink::create([
2375-
'id' => '01JTESTRUNDETAILCHILDLINK01',
2375+
'id' => '01JTESTRUNDETAILCHILDLINK1',
23762376
'link_type' => 'child_workflow',
23772377
'sequence' => 1,
23782378
'parent_workflow_instance_id' => $parentInstance->id,
@@ -2387,7 +2387,7 @@ public function testRunDetailViewKeepsOpenChildWaitFromParentHistoryWhenChildRow
23872387
]);
23882388

23892389
WorkflowHistoryEvent::create([
2390-
'id' => '01JTESTRUNDETAILCHILDEVENT1',
2390+
'id' => '01JTESTRUNDETAILCHILDEVNT1',
23912391
'workflow_run_id' => $run->id,
23922392
'sequence' => 1,
23932393
'event_type' => HistoryEventType::ChildWorkflowScheduled->value,
@@ -2673,7 +2673,7 @@ public function testRunDetailViewExposesNestedParallelActivityPathsForOpenWaits(
26732673
$this->assertCount(3, $activityWaits);
26742674

26752675
$this->assertSame('parallel-activities:1:3', $activityWaits[0]['parallel_group_id']);
2676-
$this->assertSame([
2676+
$this->assertSameJsonObject([
26772677
[
26782678
'parallel_group_id' => 'parallel-activities:1:3',
26792679
'parallel_group_kind' => 'activity',
@@ -2684,7 +2684,7 @@ public function testRunDetailViewExposesNestedParallelActivityPathsForOpenWaits(
26842684
], $activityWaits[0]['parallel_group_path']);
26852685

26862686
$this->assertSame('parallel-activities:2:2', $activityWaits[1]['parallel_group_id']);
2687-
$this->assertSame([
2687+
$this->assertSameJsonObject([
26882688
[
26892689
'parallel_group_id' => 'parallel-activities:1:3',
26902690
'parallel_group_kind' => 'activity',
@@ -2702,7 +2702,7 @@ public function testRunDetailViewExposesNestedParallelActivityPathsForOpenWaits(
27022702
], $activityWaits[1]['parallel_group_path']);
27032703

27042704
$this->assertSame('parallel-activities:2:2', $activityWaits[2]['parallel_group_id']);
2705-
$this->assertSame([
2705+
$this->assertSameJsonObject([
27062706
[
27072707
'parallel_group_id' => 'parallel-activities:1:3',
27082708
'parallel_group_kind' => 'activity',
@@ -3690,8 +3690,8 @@ public function testRunDetailViewRebuildsActivityAttemptsFromTypedHistoryWhenAtt
36903690
$heartbeatAt->toJSON(),
36913691
$detail['activities'][0]['attempts'][1]['last_heartbeat_at']?->toJSON()
36923692
);
3693-
$this->assertSame($progress, $detail['activities'][0]['last_heartbeat_progress']);
3694-
$this->assertSame($progress, $detail['activities'][0]['attempts'][1]['last_heartbeat_progress']);
3693+
$this->assertSameJsonObject($progress, $detail['activities'][0]['last_heartbeat_progress']);
3694+
$this->assertSameJsonObject($progress, $detail['activities'][0]['attempts'][1]['last_heartbeat_progress']);
36953695
$this->assertSame(
36963696
$leaseExpiresAt->toJSON(),
36973697
$detail['activities'][0]['attempts'][1]['lease_expires_at']?->toJSON()
@@ -3706,7 +3706,7 @@ public function testRunDetailViewRebuildsActivityAttemptsFromTypedHistoryWhenAtt
37063706
],
37073707
array_column($detail['timeline'], 'type')
37083708
);
3709-
$this->assertSame($progress, $detail['timeline'][4]['activity']['last_heartbeat_progress']);
3709+
$this->assertSameJsonObject($progress, $detail['timeline'][4]['activity']['last_heartbeat_progress']);
37103710
$this->assertNull($this->findTaskOrNull($detail['tasks'], 'activity'));
37113711
}
37123712

tests/Feature/V2/V2RunSummarySortKeyTest.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,8 @@ public function testDescendingQueryOrderUsesDurableSortTimestampThenRunId(): voi
1717
{
1818
$sortTimestamp = Carbon::parse('2026-04-08 12:00:00');
1919

20-
$olderRunId = '01JTESTSORTKEY00000000000001';
21-
$newerRunId = '01JTESTSORTKEY00000000000002';
20+
$olderRunId = '01JTESTSORTKEY000000000001';
21+
$newerRunId = '01JTESTSORTKEY000000000002';
2222

2323
$older = $this->createRunningSummary(
2424
'sort-contract-older',

tests/Feature/V2/V2SearchAttributeTest.php

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -61,17 +61,17 @@ public function testSearchAttributeUpsertRecordsDurableHistoryEvents(): void
6161
$this->assertSame(2, $upsertEvents->count());
6262

6363
$firstUpsert = $upsertEvents->first();
64-
$this->assertSame([
64+
$this->assertSameJsonObject([
6565
'customer' => 'Taylor',
6666
'status' => 'processing',
6767
], $firstUpsert->payload['attributes']);
6868

6969
$secondUpsert = $upsertEvents->last();
70-
$this->assertSame([
70+
$this->assertSameJsonObject([
7171
'result' => 'success',
7272
'status' => 'completed',
7373
], $secondUpsert->payload['attributes']);
74-
$this->assertSame([
74+
$this->assertSameJsonObject([
7575
'customer' => 'Taylor',
7676
'result' => 'success',
7777
'status' => 'completed',
@@ -90,7 +90,7 @@ public function testSearchAttributesMergeAcrossMultipleUpserts(): void
9090

9191
$run = WorkflowRun::query()->where('id', $workflow->runId())->firstOrFail();
9292

93-
$this->assertSame([
93+
$this->assertSameJsonObject([
9494
'customer' => 'Taylor',
9595
'result' => 'success',
9696
'status' => 'completed',
@@ -109,7 +109,7 @@ public function testSearchAttributesAppearInRunSummary(): void
109109

110110
$summary = WorkflowRunSummary::query()->where('id', $workflow->runId())->firstOrFail();
111111

112-
$this->assertSame([
112+
$this->assertSameJsonObject([
113113
'customer' => 'Taylor',
114114
'result' => 'success',
115115
'status' => 'completed',
@@ -174,8 +174,8 @@ public function testVisibilityFiltersDefinitionIncludesSearchAttributes(): void
174174

175175
public function testVisibilityFilterVersionIsUpdated(): void
176176
{
177-
$this->assertSame(4, VisibilityFilters::VERSION);
178-
$this->assertContains(4, VisibilityFilters::supportedVersions());
177+
$this->assertSame(5, VisibilityFilters::VERSION);
178+
$this->assertContains(5, VisibilityFilters::supportedVersions());
179179
$this->assertContains(3, VisibilityFilters::supportedVersions());
180180
}
181181
}

tests/Unit/migrations/MigrationsTest.php

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,15 @@ public function testRunSummaryWorkflowInstanceIdSupportsServerWorkflowIds(): voi
114114
$this->assertGreaterThanOrEqual(128, $summaryLength);
115115
}
116116

117+
public function testTimelineTimerIdMatchesTimerProjectionLength(): void
118+
{
119+
$timelineLength = $this->stringColumnLength('workflow_run_timeline_entries', 'timer_id');
120+
$timerProjectionLength = $this->stringColumnLength('workflow_run_timer_entries', 'timer_id');
121+
122+
$this->assertSame($timerProjectionLength, $timelineLength);
123+
$this->assertGreaterThanOrEqual(128, $timelineLength);
124+
}
125+
117126
private function stringColumnLength(string $table, string $column): int
118127
{
119128
$driver = DB::connection()->getDriverName();

0 commit comments

Comments
 (0)