Skip to content

Commit 6de1c6e

Browse files
committed
fix(copilot): prefer a conditioned variant over an unconditioned catch-all
An unconditioned same-id variant matches every set of values, so it would shadow a genuinely selected variant purely by being declared first. Prefer a variant that actually asserted something about the current values. No block in the registry currently declares a catch-all ahead of a conditioned variant on a field where it would change validation, so this is a guard against the pattern rather than a fix for a live case.
1 parent 5f1492b commit 6de1c6e

2 files changed

Lines changed: 35 additions & 1 deletion

File tree

apps/sim/lib/copilot/tools/server/workflow/edit-workflow/validation.test.ts

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -257,6 +257,22 @@ const multiVariantBlockConfig = {
257257
],
258258
condition: { field: 'provider', value: 'beta' },
259259
},
260+
// Catch-all declared first: it matches everything, so it must not shadow
261+
// the conditioned variant below purely by declaration order.
262+
{
263+
id: 'mode',
264+
type: 'dropdown',
265+
options: [{ label: 'default', id: 'default' }],
266+
},
267+
{
268+
id: 'mode',
269+
type: 'dropdown',
270+
options: [
271+
{ label: 'fast', id: 'fast' },
272+
{ label: 'slow', id: 'slow' },
273+
],
274+
condition: { field: 'provider', value: 'beta' },
275+
},
260276
],
261277
tools: { access: ['multi_variant_tool'], config: { tool: () => 'multi_variant_tool' } },
262278
}
@@ -403,6 +419,18 @@ describe('validateInputsForBlock', () => {
403419
expect(result.errors[0].field).toBe('model')
404420
})
405421

422+
it('prefers a conditioned variant over an unconditioned catch-all', () => {
423+
// `mode` declares the catch-all first; it must not shadow the beta variant.
424+
const result = validateInputsForBlock(
425+
'multi_variant_block',
426+
{ provider: 'beta', mode: 'fast' },
427+
'mv-7'
428+
)
429+
430+
expect(result.errors).toHaveLength(0)
431+
expect(result.validInputs.mode).toBe('fast')
432+
})
433+
406434
it('falls back to the union when no variant condition matches', () => {
407435
// Without a provider nothing resolves, so widen rather than guess.
408436
const result = validateInputsForBlock('multi_variant_block', { model: 'b1' }, 'mv-6')

apps/sim/lib/copilot/tools/server/workflow/edit-workflow/validation.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -326,7 +326,13 @@ function resolveActiveSubBlock(
326326
const active = candidates.filter((candidate) =>
327327
evaluateSubBlockCondition(candidate.condition, effectiveValues)
328328
)
329-
return active.length > 0 ? active[0] : null
329+
if (active.length === 0) return null
330+
/**
331+
* An unconditioned variant matches everything, so it would shadow a genuinely
332+
* selected one purely by being declared earlier. Prefer a variant that
333+
* actually asserted something about the current values.
334+
*/
335+
return active.find((candidate) => candidate.condition) ?? active[0]
330336
}
331337

332338
/**

0 commit comments

Comments
 (0)