Problem

A human step whose audience matches nobody still becomes a work item. Nobody can see it, nobody can claim it, and the run stops there. The only signal is a log line, and the only way out is an operator holding the reassign permission who happens to go looking for it.

Orchestra already has the object for a run that needs a person: an incident. It halts the token, appears in the incidents list, and carries retry, resume with variables, skip, cancel and fail. Retry re-queues the token so its node runs again, which for a human step means the audience is resolved afresh, so correcting the audience and retrying is the repair. Resume writes corrected variables first, which is exactly the fix for a variable-named audience. None of that reaches a phantom task.

Proposed

  • AssignmentMatcher::getCandidates() returns NULL for a step an audience opened by restricting nobody, the resolved tokens, or an empty array when every audience the node named found nobody. The empty array is the invalid one: it never reaches storage, because that is what the incident is raised on. NULL is what a stored open step reads back as, since it keeps no candidate rows.
  • An audience says it restricts nobody by answering NULL, the same value the step it opens reads back as from storage. Not by a marker interface: a marker only moves the problem, because a caller asking a plugin directly would have to check for it too, and the reassign timeout action does exactly that.
  • The engine resolves the audience before it parks. On NULL it halts the token and raises an incident, and emits no park event at all, so no work item is created and no notification subscriber fires for a step that produced nothing.
  • The two sentinel candidate values go. Nothing is stored that is not a real audience token, and the only work item without candidates is one the Everyone audience opened.

Why the resolution moves before the park

The work item is minted by WorkItemCreationSubscriber reacting to TokenParkedEvent, at priority 100 so that it exists before any notification subscriber on the same event. By then the token is already PARKED, and the decision sits in a subscriber that has no business halting a token. Deciding earlier keeps the halt in the engine, where the one existing halt path already lives, and stops a doomed park from being announced to everything downstream.

Tests

  • An audience matching nobody leaves the token in ERROR with an open incident, and creates no work item.
  • Retrying after the audience is corrected produces a real task.
  • Resuming with variables does the same for a variable-named audience.
  • Retrying while the audience still matches nobody raises a fresh incident rather than a silent task.
  • A step the Everyone audience opened still creates a task, with no candidates.

These belong in EngineIncidentTest, which already covers the five resolutions for the dead-letter raise path.

Follows #3620619, which stopped the matcher inferring "everyone" from an audience that matched nobody, and added the Everyone audience so an open step can say so. This finishes that change.

AI-Generated: Yes (Claude Code was used to help draft this issue summary. I reviewed it before posting; there is no code on this issue yet.)

Issue fork orchestra-3621013

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

Status: Active » Needs review
mably’s picture

Issue summary: View changes
mably’s picture

Issue summary: View changes

  • mably committed c23561a2 on 1.x
    task: #3621013 Resolve a step's audience before parking, and raise an...
mably’s picture

Status: Needs review » 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.