Closed (fixed)
Project:
Orchestra
Version:
1.x-dev
Component:
Code
Priority:
Normal
Category:
Feature request
Assigned:
Unassigned
Reporter:
Created:
29 Jun 2026 at 16:46 UTC
Updated:
14 Jul 2026 at 16:35 UTC
Jump to comment: Most recent
An external interaction and a user task are, at their core, the same thing: a parked token waiting for someone to respond, after which the engine resumes with an outcome. They had separate presentation layers. This unifies them so a single interaction plugin drives either doorway.
interaction_task inbox task (identity), via a continuation handle the doorway resolves.users_variable / roles_variable.orchestra_interaction_notification) and a default mail handler (orchestra_interaction_mail)..result.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
Comment #2
mably commentedComment #3
mably commentedLocking the model after discussion (this rewrites the earlier sketch). Goal: an interaction plugin works transparently regardless of who performs the step, an anonymous link-holder, an authenticated user, or an assigned operator, with no per-plugin special casing.
Core insight
Resuming a parked step is a single action. What varies is only who is authorized to do it. The bearer capability token and the task assignment check are the same kind of thing: two answers to one question, may this actor resume this step. A token is not tied to anonymity, an authenticated user can present one too. So the model is one resume, pluggable authorization, and an optional work-item lifecycle.
One resume
Resume the parked step with an outcome or result, write it to the node result variable, call
WorkflowEngine::signal(), advance. This is the shared core that bothresumeWithResult()andWorkItemManager::complete()already bottom out in.Pluggable authorization
Interchangeable strategies that all answer may this actor resume this step:
userCanAct().They are interchangeable in mechanism but NOT in security semantics: a bearer token is transferable and leakable (whoever holds the link can act), assignment is identity-bound (only the named users can act). Authorization is therefore a conscious per-step choice.
The work item is orthogonal lifecycle, not authorization
A work item adds claim and reassign, the inbox listing, and outcome validation against the configured set. It is the tracking and lifecycle layer, driven when a step is assignment-gated. A user task is therefore assignment authorization plus the work-item lifecycle; the resume itself is the same shared action.
The seam in code
InteractionContextalready abstracts the step URLs behindInteractionUrlsInterface. Generalize that into a gateway that is primarily an authorization strategy over the shared resume, owning:returnUrl(),signalUrl($outcome),branchReturnUrl()), already present;resume($outcome, $result)that performs the shared resume, and when assignment-gated also drives the work-item lifecycle;continuationHandle(), an opaque token an off-site surface such as a webform carries to return. In the token strategy it is the capability token (self authorizing); in the session or assignment strategies it is a plain reference re-checked against the authenticated user on return.Plugin
respond()code never changes: it only talks to the context. The webform stops embedding the capability token and embedscontinuationHandle();OrchestraResumeHandlerresolves the handle to a gateway and callsresume()instead ofresumeWithResult()directly. That makes every plugin, including the webform, transparent across all authorization modes.Consequence for ExternalInteraction
The current
ExternalInteractionfeature is misnamed: it is not external-specific, it is just an interaction on a parked step. Who performs it is the gateway concern layered on. Concretely the dispatcher (InteractionController) gains a tokenless, permission-gated authenticated path alongside the capability-token one, both resuming through the engine. Renaming the feature toInteractionis worth a small separate issue.Phases
InteractionControllerandReviewHandlerFormto resume through it. No behavior change.continuationHandle()andOrchestraResumeHandlerthrough the gateway, so the form and webform interactions are transparent across modes too.Invariant
Do not expose a bearer path on a step gated by assignment, or the point of assigning it is undone. A leaked link must be inert under the session or assignment strategies: the gateway re-checks the authenticated user, and the handle is not a credential.
Tests
Open decisions
ExternalInteractiontoInteraction(separate issue?).usertask type or a new node type (recommend a feature, for composition).InteractionUrlsInterface.Comment #7
mably commentedComment #9
mably commentedComment #11
mably commentedFiled #3607206: Let the workflow own a webform interaction's post-submit behavior (chain steps) as a follow-up for the one remaining cell, the bearer plus view combination: a view-mode webform interaction is not yet rendered read-only through the public dispatcher (following the link redirects back to the collect form). Read-only view mode already works under the task doorway and the bearer doorway works for collect, so this is a coverage gap rather than a regression.