* fix(core): a throwing FaultSignal handler must not escape the middleware (#7911)
The signal is sent from inside the catch whose whole job is to stop exceptions
escaping the activity pipeline. A handler that threw went straight through it:
no incident, no strategy, and the original fault lost along with it.
The send is now guarded. A handler that throws is treated as not having handled
the fault, so the incident strategy runs exactly as it would with no handler
present. That is the conservative direction: a handler that failed part way
through may have left the faulted activity in any state, and an incident is a
better answer than silence. Its exception is logged at error level, because a
broken fault handler is a defect in its own right rather than a workflow
outcome.
Covered both ways, since a handler that already claimed the fault before
throwing is the case that could plausibly have been mistaken for success.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* fix(core): let cancellation from a fault handler propagate
The handler guard caught OperationCanceledException along with everything else,
so a cancellation raised while an ancestor was being offered a fault was logged
as a broken handler and handed to the incident strategy. A deliberately
cancelled run reported itself faulted.
Cancellation is excluded now, matching how this repository already keeps the two
apart: the workflow-level exception middleware cancels and rethrows before its
general catch, and WorkflowRunner declines to record cancellation as the
workflow's exception.
Caught by review on #7924.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* test(core): pin that a handled fault stays in the execution log
The justification for dropping the incident is that the journal keeps the
evidence. That was asserted in several places and guarded nowhere.
It holds because ExecutionLogMiddleware writes the Faulted entry from a catch
that rethrows, and it is registered inside ExceptionHandlingMiddleware, so the
entry lands before the fault is ever offered to an ancestor. Swapping those two
registrations would make a handled failure disappear from the record with
nothing failing, which is what this test now prevents.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>