Skip to content

Commit 4bb484b

Browse files
Surface parent-perspective message for cancelled/terminated children
When a child workflow is cancelled or terminated, the child-side failure message (`Workflow cancelled.` / `Workflow cancelled: <reason>`) carried no useful context for the parent that catches the propagated exception. ChildRunHistory::exceptionForResolution now overrides the message to identify the child run and preserve any caller-supplied reason. Failed children continue to surface the user-thrown exception verbatim. Also fix V2RunDetailViewTest webhook fixture to tag commands with the canonical default codec instead of the abstract Serializer FQCN, which the codec registry no longer accepts. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent d080f9d commit 4bb484b

2 files changed

Lines changed: 41 additions & 2 deletions

File tree

src/V2/Support/ChildRunHistory.php

Lines changed: 40 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -468,11 +468,19 @@ public static function exceptionForResolution(
468468
$childRun,
469469
);
470470

471+
$parentMessage = self::parentPerspectiveMessage($resolutionEvent, $childRun);
472+
473+
if ($parentMessage !== null) {
474+
$payload = is_array($payload) ? $payload : [];
475+
$payload['message'] = $parentMessage;
476+
}
477+
471478
$fallbackClass = self::stringValue($resolutionEvent->payload['exception_class'] ?? null)
472479
?? self::stringValue(self::terminalEventForRun($childRun)?->payload['exception_class'] ?? null)
473480
?? self::failureRow($childRun)?->exception_class
474481
?? RuntimeException::class;
475-
$fallbackMessage = self::stringValue($resolutionEvent->payload['message'] ?? null)
482+
$fallbackMessage = $parentMessage
483+
?? self::stringValue($resolutionEvent->payload['message'] ?? null)
476484
?? self::stringValue(self::terminalEventForRun($childRun)?->payload['message'] ?? null)
477485
?? self::failureRow($childRun)?->message
478486
?? sprintf(
@@ -511,6 +519,37 @@ public static function exceptionForChildRun(?WorkflowRun $childRun): Throwable
511519
);
512520
}
513521

522+
/**
523+
* For cancelled/terminated child runs, the child-side message is just
524+
* "Workflow cancelled." or "Workflow cancelled: <reason>" — boring or
525+
* missing context from the parent's point of view. Build a parent-
526+
* perspective message that names the child run and preserves any reason
527+
* the canceller supplied. Failed children still surface the user-thrown
528+
* exception message verbatim, so we leave that path alone.
529+
*/
530+
private static function parentPerspectiveMessage(
531+
WorkflowHistoryEvent $resolutionEvent,
532+
?WorkflowRun $childRun,
533+
): ?string {
534+
$resolvedStatus = self::resolvedStatus($resolutionEvent, $childRun);
535+
536+
if ($resolvedStatus !== RunStatus::Cancelled && $resolvedStatus !== RunStatus::Terminated) {
537+
return null;
538+
}
539+
540+
$childIdentity = self::stringValue($resolutionEvent->payload['child_workflow_run_id'] ?? null)
541+
?? $childRun?->id
542+
?? 'unknown';
543+
$statusLabel = $resolvedStatus->value;
544+
$reason = self::stringValue(self::terminalEventForRun($childRun)?->payload['reason'] ?? null);
545+
546+
if ($reason !== null) {
547+
return sprintf('Child workflow %s closed as %s: %s', $childIdentity, $statusLabel, $reason);
548+
}
549+
550+
return sprintf('Child workflow %s closed as %s.', $childIdentity, $statusLabel);
551+
}
552+
514553
private static function terminalEventForRun(?WorkflowRun $childRun): ?WorkflowHistoryEvent
515554
{
516555
if (! $childRun instanceof WorkflowRun) {

tests/Feature/V2/V2RunDetailViewTest.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2169,7 +2169,7 @@ public function testRunDetailViewOmitsRawRequestContextForWebhookCommands(): voi
21692169
'fingerprint' => 'sha256:detail-command-context',
21702170
],
21712171
],
2172-
'payload_codec' => Serializer::class,
2172+
'payload_codec' => config('workflows.serializer'),
21732173
'payload' => Serializer::serialize([
21742174
'name' => 'name-provided',
21752175
'arguments' => ['Taylor'],

0 commit comments

Comments
 (0)