Summary
DynamicFork's companion JoinTask is constructed with zero arguments, producing empty joinOn: []. When added to a workflow, the JOIN task completes immediately — before any dynamic fork branches finish.
Tested against: Conductor OSS 3.32.0-rc.9
Root Cause
// Conductor/Definition/TaskType/DynamicFork.cs:32
public DynamicFork(string taskReferenceName, ...) : base(...)
{
this.Join = new JoinTask(taskReferenceName + "_join"); // no joinOn args
...
}
JoinTask(string taskReferenceName, params WorkflowTask[] joinOn) — called with zero WorkflowTask args → JoinOn = new List<string>() on the companion join task.
When the user adds dynamicFork.Join to the workflow definition, the JOIN task has joinOn: []. The server's Join executor evaluates joinOn.stream().allMatch(...) — on an empty stream, allMatch returns true immediately — the JOIN transitions to COMPLETED without waiting for any dynamic fork branches.
Impact
Any workflow that uses DynamicFork.Join to obtain the join task will have a join that completes immediately. Dynamic tasks spawned by the fork run in the background while the rest of the workflow proceeds as if the fork were done.
Cross-SDK Confirmation
Same root cause as:
The server behavior (empty joinOn → immediate completion) was live-confirmed against Conductor OSS 3.32.0-rc.9 in those audits.
Fix
Require callers to supply the join-on tasks, or expose it as a settable property that must be populated before use:
public DynamicFork(string taskReferenceName, string forkTasksParameter,
string forkTasksInputsParameter, params WorkflowTask[] joinOn)
{
this.Join = new JoinTask(taskReferenceName + "_join", joinOn);
...
}
For dynamic forks, the tasks are determined at runtime, so the client cannot auto-infer joinOn. The user must explicitly pass the tasks to wait for (typically the dynamic task reference names they configure at workflow design time).
Summary
DynamicFork's companionJoinTaskis constructed with zero arguments, producing emptyjoinOn: []. When added to a workflow, the JOIN task completes immediately — before any dynamic fork branches finish.Tested against: Conductor OSS 3.32.0-rc.9
Root Cause
JoinTask(string taskReferenceName, params WorkflowTask[] joinOn)— called with zeroWorkflowTaskargs →JoinOn = new List<string>()on the companion join task.When the user adds
dynamicFork.Jointo the workflow definition, the JOIN task hasjoinOn: []. The server's Join executor evaluatesjoinOn.stream().allMatch(...)— on an empty stream,allMatchreturnstrueimmediately — the JOIN transitions toCOMPLETEDwithout waiting for any dynamic fork branches.Impact
Any workflow that uses
DynamicFork.Jointo obtain the join task will have a join that completes immediately. Dynamic tasks spawned by the fork run in the background while the rest of the workflow proceeds as if the fork were done.Cross-SDK Confirmation
Same root cause as:
The server behavior (empty
joinOn→ immediate completion) was live-confirmed against Conductor OSS 3.32.0-rc.9 in those audits.Fix
Require callers to supply the join-on tasks, or expose it as a settable property that must be populated before use:
For dynamic forks, the tasks are determined at runtime, so the client cannot auto-infer
joinOn. The user must explicitly pass the tasks to wait for (typically the dynamic task reference names they configure at workflow design time).