Problem

A task claimed by an account that is later blocked or deleted stays claimed to that account forever. Its holder cannot act on it, because they cannot sign in; nobody else can, because the task is claimed. Nothing detects it, nothing logs it, and no sweep recovers it. The run stops there.

Orchestra reacts to none of this today: there is no hook_user_update, no hook_user_cancel and no hook_user_predelete anywhere in the module. The Unclaim timeout action is time-based, released after a configured period, not a response to the holder going away.

The code already knows the state exists. getHolderLabel() renders "(unknown)" when the assignee's account will not load, so a list shows the problem to whoever is looking and nothing acts on it.

There is an asymmetry worth naming. reassign() refuses to hand a task to a blocked or deleted account, and the users audience filters inactive accounts out at resolution, so the door is guarded carefully. The same account going bad after it already holds the task is unhandled.

Proposed

  • React to a user being blocked (hook_user_update, an active account going inactive) and to a user being deleted (hook_user_predelete), for the tasks that account holds and that are not yet terminal.
  • Release each one back to its audience, which is the existing release() and needs no new mechanism: the task returns to OPEN and anyone the audience admits can claim it.
  • Where there is no audience to return it to, raise an incident instead. That covers a task assigned to that one person, and one whose audience would now resolve to nobody. It is the same answer #3621013 gives a step that could never be offered, and it reaches the same operator actions: retry, resume with variables, skip, cancel, fail.

Tests

  • Blocking the holder of a pooled task releases it, and another member of the audience can claim it.
  • Deleting the holder does the same.
  • A task assigned to that person alone raises an incident rather than being released to nobody.
  • A terminal task is left alone: completed and canceled work is history, not something to reopen.
  • Blocking somebody who holds nothing changes nothing, so the hook costs nothing on a site where this never happens.

Not this issue

A task whose audience empties without anybody being blocked, for example the last member losing a role the task names, is a different shape: there is no holder to release and no single trigger to hang a hook on. Detecting that needs a periodic sweep and a per-dimension question ("could anything match this token today"), which each audience plugin would have to answer for itself. Worth its own issue.

Follows #3621013, which raises an incident for a step that could never be offered to anybody. This is the same problem arriving later: a step that could be offered, to somebody who has since gone.

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-3621033

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 committed 284181db on 1.x
    fix: #3621033 Release a task whose holder can no longer act on it
    
    By:...
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.