Skip to content

Commit 579d240

Browse files
fix(parallel): remove broken node-counting completion + resolver claim cross-block (#4045)
* fix(parallel): remove broken node-counting completion in parallel blocks * fix resolver claim --------- Co-authored-by: Vikhyath Mondreti <vikhyath@simstudio.ai>
1 parent 04c9057 commit 579d240

File tree

8 files changed

+177
-73
lines changed

8 files changed

+177
-73
lines changed

apps/sim/executor/execution/state.ts

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,6 @@ export interface ParallelScope {
2222
parallelId: string
2323
totalBranches: number
2424
branchOutputs: Map<number, NormalizedBlockOutput[]>
25-
completedCount: number
26-
totalExpectedNodes: number
2725
items?: any[]
2826
/** Error message if parallel validation failed (e.g., exceeded max branches) */
2927
validationError?: string

apps/sim/executor/orchestrators/node.ts

Lines changed: 4 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -58,9 +58,7 @@ export class NodeExecutionOrchestrator {
5858

5959
const parallelId = node.metadata.parallelId
6060
if (parallelId && !this.parallelOrchestrator.getParallelScope(ctx, parallelId)) {
61-
const parallelConfig = this.dag.parallelConfigs.get(parallelId)
62-
const nodesInParallel = parallelConfig?.nodes?.length || 1
63-
await this.parallelOrchestrator.initializeParallelScope(ctx, parallelId, nodesInParallel)
61+
await this.parallelOrchestrator.initializeParallelScope(ctx, parallelId)
6462
}
6563

6664
if (node.metadata.isSentinel) {
@@ -157,8 +155,7 @@ export class NodeExecutionOrchestrator {
157155
if (!this.parallelOrchestrator.getParallelScope(ctx, parallelId)) {
158156
const parallelConfig = this.dag.parallelConfigs.get(parallelId)
159157
if (parallelConfig) {
160-
const nodesInParallel = parallelConfig.nodes?.length || 1
161-
await this.parallelOrchestrator.initializeParallelScope(ctx, parallelId, nodesInParallel)
158+
await this.parallelOrchestrator.initializeParallelScope(ctx, parallelId)
162159
}
163160
}
164161

@@ -237,20 +234,9 @@ export class NodeExecutionOrchestrator {
237234
): Promise<void> {
238235
const scope = this.parallelOrchestrator.getParallelScope(ctx, parallelId)
239236
if (!scope) {
240-
const parallelConfig = this.dag.parallelConfigs.get(parallelId)
241-
const nodesInParallel = parallelConfig?.nodes?.length || 1
242-
await this.parallelOrchestrator.initializeParallelScope(ctx, parallelId, nodesInParallel)
237+
await this.parallelOrchestrator.initializeParallelScope(ctx, parallelId)
243238
}
244-
const allComplete = this.parallelOrchestrator.handleParallelBranchCompletion(
245-
ctx,
246-
parallelId,
247-
node.id,
248-
output
249-
)
250-
if (allComplete) {
251-
await this.parallelOrchestrator.aggregateParallelResults(ctx, parallelId)
252-
}
253-
239+
this.parallelOrchestrator.handleParallelBranchCompletion(ctx, parallelId, node.id, output)
254240
this.state.setBlockOutput(node.id, output)
255241
}
256242

apps/sim/executor/orchestrators/parallel.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,7 @@ describe('ParallelOrchestrator', () => {
107107
)
108108
const ctx = createContext()
109109

110-
const initializePromise = orchestrator.initializeParallelScope(ctx, 'parallel-1', 1)
110+
const initializePromise = orchestrator.initializeParallelScope(ctx, 'parallel-1')
111111
await Promise.resolve()
112112

113113
expect(onBlockStart).toHaveBeenCalledTimes(1)

apps/sim/executor/orchestrators/parallel.ts

Lines changed: 11 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -47,17 +47,13 @@ export class ParallelOrchestrator {
4747
private contextExtensions: ContextExtensions | null = null
4848
) {}
4949

50-
async initializeParallelScope(
51-
ctx: ExecutionContext,
52-
parallelId: string,
53-
terminalNodesCount = 1
54-
): Promise<ParallelScope> {
50+
async initializeParallelScope(ctx: ExecutionContext, parallelId: string): Promise<ParallelScope> {
5551
const parallelConfig = this.dag.parallelConfigs.get(parallelId)
5652
if (!parallelConfig) {
5753
throw new Error(`Parallel config not found: ${parallelId}`)
5854
}
5955

60-
if (terminalNodesCount === 0 || parallelConfig.nodes.length === 0) {
56+
if (parallelConfig.nodes.length === 0) {
6157
const errorMessage =
6258
'Parallel has no executable blocks inside. Add or enable at least one block in the parallel.'
6359
logger.error(errorMessage, { parallelId })
@@ -108,8 +104,6 @@ export class ParallelOrchestrator {
108104
parallelId,
109105
totalBranches: 0,
110106
branchOutputs: new Map(),
111-
completedCount: 0,
112-
totalExpectedNodes: 0,
113107
items: [],
114108
isEmpty: true,
115109
}
@@ -186,8 +180,6 @@ export class ParallelOrchestrator {
186180
parallelId,
187181
totalBranches: branchCount,
188182
branchOutputs: new Map(),
189-
completedCount: 0,
190-
totalExpectedNodes: branchCount * terminalNodesCount,
191183
items,
192184
}
193185

@@ -253,8 +245,6 @@ export class ParallelOrchestrator {
253245
parallelId,
254246
totalBranches: 0,
255247
branchOutputs: new Map(),
256-
completedCount: 0,
257-
totalExpectedNodes: 0,
258248
items: [],
259249
validationError: errorMessage,
260250
}
@@ -277,32 +267,34 @@ export class ParallelOrchestrator {
277267
return resolveArrayInput(ctx, config.distribution, this.resolver)
278268
}
279269

270+
/**
271+
* Stores a node's output in the branch outputs for later aggregation.
272+
* Aggregation is triggered by the sentinel-end node via the edge mechanism,
273+
* not by counting individual node completions. This avoids incorrect completion
274+
* detection when branches have conditional paths (error edges, conditions).
275+
*/
280276
handleParallelBranchCompletion(
281277
ctx: ExecutionContext,
282278
parallelId: string,
283279
nodeId: string,
284280
output: NormalizedBlockOutput
285-
): boolean {
281+
): void {
286282
const scope = ctx.parallelExecutions?.get(parallelId)
287283
if (!scope) {
288284
logger.warn('Parallel scope not found for branch completion', { parallelId, nodeId })
289-
return false
285+
return
290286
}
291287

292288
const branchIndex = extractBranchIndex(nodeId)
293289
if (branchIndex === null) {
294290
logger.warn('Could not extract branch index from node ID', { nodeId })
295-
return false
291+
return
296292
}
297293

298294
if (!scope.branchOutputs.has(branchIndex)) {
299295
scope.branchOutputs.set(branchIndex, [])
300296
}
301297
scope.branchOutputs.get(branchIndex)!.push(output)
302-
scope.completedCount++
303-
304-
const allComplete = scope.completedCount >= scope.totalExpectedNodes
305-
return allComplete
306298
}
307299

308300
async aggregateParallelResults(

apps/sim/executor/types.ts

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -228,8 +228,6 @@ export interface ExecutionContext {
228228
parallelId: string
229229
totalBranches: number
230230
branchOutputs: Map<number, any[]>
231-
completedCount: number
232-
totalExpectedNodes: number
233231
parallelType?: 'count' | 'collection'
234232
items?: any[]
235233
}

apps/sim/executor/utils/iteration-context.test.ts

Lines changed: 0 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -43,8 +43,6 @@ describe('getIterationContext', () => {
4343
parallelId: 'p1',
4444
totalBranches: 3,
4545
branchOutputs: new Map(),
46-
completedCount: 0,
47-
totalExpectedNodes: 3,
4846
},
4947
],
5048
]),
@@ -135,8 +133,6 @@ describe('buildUnifiedParentIterations', () => {
135133
parallelId: 'outer-p',
136134
totalBranches: 4,
137135
branchOutputs: new Map(),
138-
completedCount: 0,
139-
totalExpectedNodes: 4,
140136
},
141137
],
142138
]),
@@ -164,8 +160,6 @@ describe('buildUnifiedParentIterations', () => {
164160
parallelId: 'parallel-1',
165161
totalBranches: 5,
166162
branchOutputs: new Map(),
167-
completedCount: 0,
168-
totalExpectedNodes: 5,
169163
},
170164
],
171165
]),
@@ -232,8 +226,6 @@ describe('buildUnifiedParentIterations', () => {
232226
parallelId: 'outer-p',
233227
totalBranches: 3,
234228
branchOutputs: new Map(),
235-
completedCount: 0,
236-
totalExpectedNodes: 3,
237229
},
238230
],
239231
]),
@@ -275,8 +267,6 @@ describe('buildUnifiedParentIterations', () => {
275267
parallelId: 'P1',
276268
totalBranches: 2,
277269
branchOutputs: new Map(),
278-
completedCount: 0,
279-
totalExpectedNodes: 2,
280270
},
281271
],
282272
[
@@ -285,8 +275,6 @@ describe('buildUnifiedParentIterations', () => {
285275
parallelId: 'P2',
286276
totalBranches: 2,
287277
branchOutputs: new Map(),
288-
completedCount: 0,
289-
totalExpectedNodes: 2,
290278
},
291279
],
292280
[
@@ -295,8 +283,6 @@ describe('buildUnifiedParentIterations', () => {
295283
parallelId: 'P2__obranch-1',
296284
totalBranches: 2,
297285
branchOutputs: new Map(),
298-
completedCount: 0,
299-
totalExpectedNodes: 2,
300286
},
301287
],
302288
]),
@@ -363,8 +349,6 @@ describe('buildUnifiedParentIterations', () => {
363349
parallelId: 'parallel-1',
364350
totalBranches: 5,
365351
branchOutputs: new Map(),
366-
completedCount: 0,
367-
totalExpectedNodes: 5,
368352
},
369353
],
370354
]),
@@ -423,8 +407,6 @@ describe('buildUnifiedParentIterations', () => {
423407
parallelId: 'parallel-1',
424408
totalBranches: 3,
425409
branchOutputs: new Map(),
426-
completedCount: 0,
427-
totalExpectedNodes: 3,
428410
},
429411
],
430412
]),
@@ -478,8 +460,6 @@ describe('buildContainerIterationContext', () => {
478460
parallelId: 'parallel-1',
479461
totalBranches: 5,
480462
branchOutputs: new Map(),
481-
completedCount: 0,
482-
totalExpectedNodes: 5,
483463
},
484464
],
485465
]),
@@ -541,8 +521,6 @@ describe('buildContainerIterationContext', () => {
541521
parallelId: 'P2__obranch-1',
542522
totalBranches: 5,
543523
branchOutputs: new Map(),
544-
completedCount: 0,
545-
totalExpectedNodes: 5,
546524
},
547525
],
548526
]),
@@ -568,8 +546,6 @@ describe('buildContainerIterationContext', () => {
568546
parallelId: 'outer-parallel',
569547
totalBranches: 3,
570548
branchOutputs: new Map(),
571-
completedCount: 0,
572-
totalExpectedNodes: 3,
573549
},
574550
],
575551
]),

0 commit comments

Comments
 (0)