diff --git a/apps/sim/lib/workflows/sanitization/json-sanitizer.test.ts b/apps/sim/lib/workflows/sanitization/json-sanitizer.test.ts index 2e8ff1b59d6..667b67963d5 100644 --- a/apps/sim/lib/workflows/sanitization/json-sanitizer.test.ts +++ b/apps/sim/lib/workflows/sanitization/json-sanitizer.test.ts @@ -184,6 +184,58 @@ describe('sanitizeForCopilot product-gated block inputs', () => { }) }) +describe('sanitizeForCopilot subflow config', () => { + /** + * The model's read view has to use the same field names as the write contract it is + * given, or an echoed-back edit is silently dropped. `components/blocks/parallel.json` + * declares `count`; `components/blocks/loop.json` declares `iterations`. + */ + it("names a count-parallel's branch count `count`, matching the parallel write contract", () => { + const state = makeSingleBlockWorkflow('parallel-1', { + type: 'parallel', + name: 'Parallel 1', + enabled: true, + subBlocks: {}, + data: { parallelType: 'count', count: 5 }, + }) + + expect(sanitizeForCopilot(state).blocks['parallel-1'].inputs).toEqual({ + parallelType: 'count', + count: 5, + }) + }) + + it("names a for-loop's trip count `iterations`, matching the loop write contract", () => { + const state = makeSingleBlockWorkflow('loop-1', { + type: 'loop', + name: 'Loop 1', + enabled: true, + subBlocks: {}, + data: { loopType: 'for', count: 3 }, + }) + + expect(sanitizeForCopilot(state).blocks['loop-1'].inputs).toEqual({ + loopType: 'for', + iterations: 3, + }) + }) + + it('exports a collection parallel without a branch count', () => { + const state = makeSingleBlockWorkflow('parallel-2', { + type: 'parallel', + name: 'Parallel 2', + enabled: true, + subBlocks: {}, + data: { parallelType: 'collection', collection: '' }, + }) + + expect(sanitizeForCopilot(state).blocks['parallel-2'].inputs).toEqual({ + parallelType: 'collection', + collection: '', + }) + }) +}) + /** Builds a one-block workflow for webhook-URL synthesis tests. */ function makeSingleBlockWorkflow(blockId: string, block: Record): WorkflowState { return { diff --git a/apps/sim/lib/workflows/sanitization/json-sanitizer.ts b/apps/sim/lib/workflows/sanitization/json-sanitizer.ts index bd4f2377b12..a25be379e7d 100644 --- a/apps/sim/lib/workflows/sanitization/json-sanitizer.ts +++ b/apps/sim/lib/workflows/sanitization/json-sanitizer.ts @@ -591,7 +591,11 @@ export function sanitizeForCopilot( loopInputs.parallelType = parallelType // Only export fields relevant to the current parallelType if (parallelType === 'count' && block.data?.count !== undefined) { - loopInputs.iterations = block.data.count + // `count`, not `iterations`: the parallel schema the model is given names this + // field `count` and the edit path reads it back under that name. A loop's + // equivalent field really is called `iterations` on both sides — copying that + // line here made the model's read view disagree with its own write contract. + loopInputs.count = block.data.count } if (parallelType === 'collection' && block.data?.collection !== undefined) { loopInputs.collection = block.data.collection