Issue #3615651: Let a timeout action declare the outcomes it resumes a step with, so an author does not have to read the plugin

Adds a resumesWith property to #[TimeoutAction], naming the outcomes an action can resume a task with, and shows it under the action select in both places an author picks one: a node's single timeout, and a rung of a timer ladder.

The problem it solves is quiet. Only resume surfaced its outcome, because the author types it into timeout_outcome; every other action resumes indirectly, so the editor offered a select and no hint of what it would produce. Guessing wrong is not a validation error - it is a flow condition on a value nothing announces, which is a branch never taken, and it only shows up the first time the timeout fires for real. That is exactly how a shipped workflow came to route its payment step on timeout while the payment layer announced expired.

Same shape as #[VariableProvider(provides: [...])], and the same contract: declarative documentation, nothing rejects an action that resumes with an outcome it did not declare.

Declared on the one bundled action that resumes with an outcome of its own, payment_lapsed (expired). The rest declare nothing, and that is meaningful rather than missing: notify, unclaim, reassign and spawn leave the task parked, and resume's outcome is the author's setting, which already says what it will be. The editor renders nothing for them rather than a line saying "resumes with nothing" under every select.

PluginSequenceFeatureBase gains a pluginHint() hook so a row's select can carry a note; the base stays ignorant of what any inner plugin type means, and Timers fills it in because its inner plugins are timeout actions.

Tests. ResumeOutcomeDeclarationTest drives each installed action through a real timeout and compares what the task was resumed with against what the plugin claims - because a declaration nothing enforces is exactly the kind of thing that drifts. It asserts both halves: an action that declares an outcome resumes with it, and one that declares nothing leaves the task parked. It also asserts the declaring set is non-empty first, since a loop over an empty list agrees with anything. A test action (declared_outcome) supplies a declaring case without pulling a payment stack into the fixture.

Docs and the French translation updated.

Follow-on, deliberately not here: the flow-condition editor offering these values rather than leaving the author to type a string. That is where the mistake was actually made; declaring the outcomes is what unblocks it.

Merge request reports

Loading
Loading