Problem/Motivation
A subprocess node always starts its child workflow as a new process instance. When the child is just a piece of the same request (a validation circuit shared by several request types), the visitor's journey then spans two runs, and every visitor-facing reader has to cross between them: the interaction_chain node feature, the parent_chain instance column, and the chain readers that look in several runs. A final message reached in the child is lost once the parent resumes, because the parent answers for both links.
Proposed resolution
Add an embedded mode to the subprocess node: the child's steps run as tokens of the parent instance, read against a pinned version of the child workflow. Nothing is copied into the parent's definition.
- Scope. Each token gets a
scope: the embedded subprocess token it runs under, NULL at the top level. A token's node is read from its scope's child version, soDefinitionResolver::getTokenNode()answers for every caller that already goes through it. The readers that walk a run's nodes also walk its open scopes, and the token queries that filter onnode_idalso filter on scope, as a parent and its child may share node ids. - Child version. The authoring config names the child workflow only. Cutting a parent version resolves the child's current version and records it in the parent's version, inside its hash. Running instances keep the pair they started on.
- Updating the parents. In auto versioning mode, saving the child invalidates the version of every parent embedding it (found as
WorkflowUsagefinds them), so their next start pins the new child. In manual mode, publishing the child lists the parents still embedding an older version, with "Update all": each ticked parent gets a new published version identical to its current published one except for the child reference, so unpublished edits to the parent are not published with it. The parent's subprocess node shows when a newer child version is published. - Variables. Shared with the instance, as in a BPMN embedded subprocess. "Update all" warns about a variable the new child version reads that a parent never sets.
- Completion. When the last token of a scope ends, the engine resumes the embedded subprocess token, as a finished child run resumes it today.
- Guard. Saving a workflow that embeds itself, directly or through its children, is refused.
The separate-instance mode stays for what only a run of its own can do: relaunch attempts, child status and label, correlation, completion scope, variable mapping and initiator source. The interaction_chain feature and the parent_chain column are removed, along with the cross-run chain reads they need.
Remaining tasks
- Agree on the design.
- Implement the scope, the version recording and the engine changes.
- "Update all" and the newer-version notice.
- Follow-up: migrating a running instance across a child version change, which needs a node map per scope.
User interface changes
A mode selector on the subprocess node, the "Update all" list on the child after publishing in manual mode, and a newer-version notice on the parent's subprocess node.
API changes
A scope-aware node lookup. InteractionResolver::getParentChain(), findChainChild(), canShareChains() and the cross-run chain reads are removed.
Data model changes
A scope field on the token and the child versions recorded in a workflow version. The parent_chain instance column is removed.
AI-Generated: Yes (Claude Code was used to help draft this issue summary from a design I worked out with it.)
Issue fork orchestra-3628680
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
Comment #3
mably commentedComment #5
mably commented