Problem/Motivation

A node's timeout names an action, and the author then writes a flow condition on whatever outcome that action resumes the step with. Nothing tells them what that outcome is. Only resume surfaces it, because it takes the value as a timeout_outcome setting; every other action resumes indirectly - through an event a consumer announces, or through a call of its own - so the editor shows an action select and no hint of what it will produce.

So the author reads the plugin's source, or guesses. Guessing is quiet: a flow condition naming an outcome nothing announces is not a validation error, it is a branch that is simply never taken, and the step dead-ends the first time the timeout actually fires. That is not hypothetical - it is exactly how a shipped workflow came to route its payment step on timeout while the payment layer announced expired, which nothing caught until a real checkout lapsed.

The plugin type already has the right precedent one seam over: #[VariableProvider] takes a provides property naming the variables it writes, so an editor can show what selecting it contributes. #[TimeoutAction] has no equivalent.

Proposed resolution

Give the attribute a property naming the outcomes the action can resume a step with, declared the same way and for the same reason:

#[TimeoutAction(id: 'payment_lapsed', label: ..., resumesWith: ['expired'])]

Then the timeout editor can say what the chosen action produces, underneath the action select. The bundled actions describe themselves honestly: resume answers with whatever timeout_outcome is set to, notify and unclaim answer with nothing because they leave the task parked, and an action resuming through a consumer's event names what that event carries.

The larger win is the flow-condition editor being able to offer those values rather than leaving the author to type a string, which is where the mistake above was actually made. That can follow separately; declaring the outcomes is what unblocks it.

Declarative documentation, like provides: nothing needs to reject an action that resumes with something it did not declare, and an action that declares nothing is simply an action nobody can be told about.

Remaining tasks

  • The attribute property, and the bundled actions declaring theirs.
  • Showing it in the timeout editor, and in a timer row's action.
  • Documentation and tests.

Issue fork orchestra-3615651

Command icon Show commands

Start within a Git clone of the project using the version control instructions.

Or, if you do not have SSH keys set up on git.drupalcode.org:

Comments

mably created an issue. See original summary.

mably’s picture

Implemented in !397, and green.

Two things were considered and deliberately left out, recorded here so they are not rediscovered from scratch.

Offering the outcomes when writing a flow condition. This was the original motivation: the failure was an author typing a value nothing announces, and a hint only helps someone who reads it. It was dropped for now because a step can be resumed with any string - resumeWithPayload() is reachable from a custom task type, an external signal or a consumer's subscriber - so any list can only ever be suggestions, never a closed set. That makes the widget the cheap part and the plumbing the whole cost: the condition editor composes each condition from its plugin and settings alone and never learns which node the flow leaves, so the source node would have to be threaded through the condition tree to reach a plugin's subform. A lot of machinery for a suggestion. The declaration this issue adds is the prerequisite whenever that is worth doing.

Showing the whole condition rather than the outcome. The timeout editor holds both halves already - the node, so its payload variable, and the action, so its declared outcome - and could therefore render the condition to write, e.g. payment_outcome == expired, with no new context threading at all. Left out as more than is needed for now; noted because it is cheap and lives entirely in the timeout feature.

Consumers still to declare. yoyaku_basket_lapsed resumes a step with expired by way of its reconciler rather than resuming it itself, so it declares nothing today and shows no hint. Declaring it is a yoyaku change.

  • mably committed b9e5663d on 1.x
    feat: #3615651 Let a timeout action declare the outcomes it resumes a...
mably’s picture

Status: Active » Fixed

Now that this issue is closed, review the contribution record.

As a contributor, attribute any organization that helped you, or if you volunteered your own time.

Maintainers, credit people who helped resolve this issue.

Status: Fixed » Closed (fixed)

Automatically closed - issue fixed for 2 weeks with no activity.