Skip to content

Delegation

Someone is away and their work should not wait for them. A delegation says that, for a period, another person acts for them: the delegate sees the delegator's tasks in their own inbox, acts on them as themselves, and the record says the work was done on the delegator's behalf.

It ships in the optional orchestra_delegation submodule. Without it, Orchestra behaves exactly as before and pays nothing for the feature.

Delegation is not reassignment

Orchestra can hand work to another person in two quite different ways, and picking the wrong one is the usual mistake:

What moves How long What comes back
Reassignment Ownership. The task's assignee is rewritten. Permanent. Nothing.
Delegation Nothing on the task. Who may reach it widens. A period, revocable. Everything, when it lapses.

Reassignment is right when the work really is someone else's now. Delegation is right when it stays the delegator's work and somebody is covering it. Because a delegation stores nothing on the task, it applies at once to tasks that already exist, and stops applying the moment it lapses, with nothing to unwind.

Declaring cover

A user with the delegate own orchestra tasks permission arranges their own cover at /orchestra/delegations, next to the inbox. A delegation names the delegate, an optional start and end (leave either empty for "already" and "until revoked"), and carries an on/off switch so a recurring absence is set up once rather than re-entered.

A user with administer orchestra delegations manages anyone's, from Structure > Orchestra > Delegations, beside the running instances rather than in the configuration screens: a delegation is something somebody declared, not site configuration. That permission exists for the case self-service cannot reach: arranging cover for someone who is already unreachable, or ending cover that outlived its reason.

The form refuses a delegation that would quietly do nothing: to yourself, to a blocked account, to someone without the process orchestra tasks permission, or a second one for the same pair over an overlapping period.

The two directions

Cover has two sides, and the page shows them as two tabs:

  • My delegations, the cover you asked for. Yours to add, change and revoke.
  • Delegated to me, the cover you provide. Read-only: revoking is the delegator's to do, since a stand-in withdrawing cover themselves would leave the delegator believing they were still covered.

The received side lists only cover that is still in force or still to come. Cover that ended, and cover switched off, are dropped: they ask nothing of the stand-in, and a delegator who keeps old rows around would otherwise clutter somebody else's page. Cover that has not started stays, because "you are covering for Alice from Monday" is worth knowing before Monday, and it is why the Status column is worth having on this tab at all. The delegator's own tab keeps everything, since a lapsed or switched-off row is theirs to edit or re-enable.

The second tab exists because a stand-in otherwise cannot answer the two questions they will actually have: why are someone else's tasks in my inbox, and until when? The per-row wording on a task says who it is covered for, but not when the arrangement ends.

It carries its own menu entry rather than relying on the tab alone. The tab hangs off My delegations, which needs delegate own orchestra tasks, and somebody who only ever stands in for other people has no reason to hold that permission. Menu links the viewer cannot use are hidden, so each user sees the side or sides that apply to them.

How many at once

Both directions are many, and neither is capped:

  • One person may be covered by several others. Being away is not a single hand-off. The only thing refused is a second delegation to the same person over overlapping dates, because two rows saying the identical thing cannot be reasoned about: revoking one would leave the cover silently in place. Two different stand-ins over the same period is a legitimate arrangement, and it is why dropping the delegator's own notifications takes unanimity.
  • One person may cover for several people. A delegate carries the audience tokens of everybody they cover for, added to their own, so each of their pools reaches them. When such a delegate claims a pooled task, the person whose audience matched is the one recorded on it.

What is not many is the chain: cover never passes through a delegate to their own stand-in. See one hop below.

sequenceDiagram
  actor Alice
  actor Bob
  participant D as Delegation
  participant Inbox
  Alice->>D: "Bob covers for me, 12-26 Aug"
  Note over D: nothing is written to any task
  Bob->>Inbox: open /orchestra/tasks
  Inbox-->>Bob: their own work, plus Alice's
  Bob->>Inbox: complete "Approve invoice 42"
  Note over Inbox: completer = Bob, on behalf of Alice
  Note over D: 26 Aug passes
  Bob->>Inbox: open /orchestra/tasks
  Inbox-->>Bob: their own work only

What a delegate reaches

Two kinds of task arrive, by two different routes:

  • Work still pooled to an audience the delegator belongs to. The delegate carries the delegator's audience tokens on top of their own, so the pool is offered to them as well.
  • Work the delegator has already claimed. This is the one that matters most, and the one a token-only rule would miss: a claimed task names no audience at all, so the delegate is instead allowed to act for the delegator as an assignee.

Everything built on those two rules follows for free: the inbox, the pending-actions list, the Views filters, bulk operations, interaction tasks and content-bound tasks. A delegate may also reassign or return to the pool the work they cover, since covering includes handing a piece of it on.

That is every task of the delegator's in the tenant: the ones they already hold, the ones offered to a pool they belong to, and any that arrive during the window, including tasks a workflow assigns to them while they are away.

Cover is read at the moment of asking, not written to the task

A step assigned to one named person is claimed for that person the moment its token parks, and that does not change during an absence: the task is still assigned to the delegator, and the delegate reaches it because the question "may I act on this?" is answered through the delegation every time it is asked.

This matters more than it looks. If assignment resolved through cover instead, a task created while somebody was away would belong to their stand-in for good, and returning would not get it back: that is a reassignment with extra steps. Because nothing is written to any task:

  • declaring cover applies at once to work that is already waiting;
  • revoking it takes the work back at once, with nothing to unwind;
  • the assignee always answers "whose task is this", and never has to be read as "whose task this was before somebody went on holiday".

The one thing a delegation does write is on completion, and it is a record rather than a redirection: see what gets recorded.

A delegation widens which tasks a person reaches, never whether they may act: the delegate still needs process orchestra tasks in their own right, and a delegation grants no permission.

It follows that the delegator needs that permission too, because cover passes on their reach and adds nothing of its own. A delegator who is blocked, or who does not hold the permission, has nothing to pass on, and their cover is inert until that changes: the delegation row is read, never rewritten, so it works again the moment the permission comes back. The forms refuse both sides of a cover that would do nothing, rather than let the work sit unseen.

What gets recorded

Completing a delegated task stamps the work item's on behalf of field. It sits alongside, and is deliberately distinct from, the other two people a task knows about:

  • the assignee is whoever holds the task, which a delegation never moves;
  • the completer is whoever acted, which is the delegate;
  • on behalf of is who the delegate was acting for.

It is stamped in the two places a delegation is what brought the work within reach: when a delegate completes a task assigned to the person they cover for, and when a delegate claims a pooled task that only their cover made visible. It stays empty for ordinary work, and for a manager reaching a task through the reassign orchestra tasks permission, who is not standing in for anyone.

The two paths carry different strengths, and the field is deliberately not described as ownership because of it. On a task its assignee already held, the person named is unambiguously whose work it was. On one taken from a pool it is not: a pool is nobody's in particular, so what the field records is the capacity the actor was acting in, which is what explains their access.

It is never a guess. A value appears only when exactly one delegation accounts for the reach:

Situation Recorded
Task assigned to Alice, completed by her stand-in Alice
Pool reached only through Alice's cover Alice
Pool reached through Alice's and Erin's cover at once nothing
Claimer is in the audience in their own right nothing

The third row is the important one: with two people covered in the same audience, either could equally explain the access, so naming one would be a guess decided by row order. An audit field is better silent than confidently wrong, so it stays empty, exactly as it does when no delegation was involved.

A step may of course be offered to several audiences at once (two roles, a role and a named user). Those resolve into one candidate set before any of this runs, and the question asked is always "how many of the people I cover for are in that set?" So a multi-audience step needs no rule of its own: one covered person in it, whichever token matched, is recorded; two, whether they matched the same token or different ones, is not.

Every covered row explains itself

A stand-in should never have to wonder why a task is in front of them, so the holder column always gives the reason:

What the row is Reads
Work its assignee already held alice
Claimed by the stand-in under cover bob (for alice)
Still pooled, reached only through cover Offered to: role:reviewer (covering for alice)
Still pooled, reached through two people's cover Offered to: role:reviewer (covering for another user)
Reached in the viewer's own right Offered to: role:reviewer

The third and fourth rows are the ones that need the reader's identity: nothing is stored on an unclaimed pooled task about who can see it, so the wording is worked out for whoever is looking. Without it a stand-in reads a row addressed to a group they do not belong to, with nothing to explain it. The fourth still gives the reason without a name, because naming one of several would be the guess described above.

Because that wording differs per reader, the Views column varies by user, and a caller that wants viewer-independent phrasing (a digest, a log line) simply leaves the viewer out.

Reading it back

Beyond the inbox, the work is queryable. on_behalf_of is exposed to Views as On behalf of, filterable by username, alongside a personal Handled on the current user's behalf filter: the list to read on returning from an absence. It is the delegator's side of the same rows a stand-in sees in their own task history, and both are true at once, since one records who acted and the other whose work it was. See Views and dashboards.

Notifications

The delegate is added to the audience of the tasks they cover, so cover does not depend on anyone watching an inbox. The delegator stays in it too, unless the delegation's Keep notifying me checkbox is cleared, which is the point of declaring an absence.

With more than one delegation active at once, the delegator is dropped only when every one of them asks to be left alone. This is the only place Orchestra ever removes a recipient, so it takes unanimity rather than a majority.

The rules worth knowing

  • One hop. If A delegates to B and B delegates to C, C does not act for A. Cover does not chain, which is also why no cycle is possible.
  • One tenant. A delegation covers a single tenant: the one it was declared in, which it keeps for life. Someone covering in one realm is a stranger in another.
  • Not per workflow. A delegation covers every task in its tenant. This is structural rather than a simplification: the widening happens where a viewer's identity is resolved, which knows nothing of a particular task, and that is exactly why it reaches every surface at once.
  • One way. Covering for someone gives them nothing in return.
  • Live, not copied. Nothing is written to a task when a delegation is declared or revoked, so both take effect immediately, including on work that was already waiting.

Extending it

Orchestra core carries only the question, as DelegationResolverInterface: who does this account act for, who acts for this account, and does this account still want its own notifications. The shipped implementation answers "nobody" to all three; orchestra_delegation decorates the orchestra.delegation_resolver service and answers from its stored delegations.

A site whose absences already live somewhere else, an HR system, an LDAP attribute, a group module, can decorate that service instead and keep its own source of truth, with no change to any task, surface or query. The two contract rules above, one hop and per tenant, are the resolver's to honor.