From 76c432c53f5c3387bbc406198296364822c8eded Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Braulio=20Gonz=C3=A1lez=20Valido?= Date: Wed, 27 May 2026 08:42:54 +0100 Subject: [PATCH] fix(ai-builder): Default Switch to case-insensitive in builder hints (#31044) Co-authored-by: Claude Opus 4.7 (1M context) --- .../data/workflows/notification-router.json | 8 ++++---- .../node-guidance/parameter-guides/switch-node.ts | 13 +++++++------ .../validators/filter-node-validator.test.ts | 2 +- .../plugins/validators/filter-node-validator.ts | 2 +- .../nodes-base/nodes/Switch/V3/SwitchV3.node.ts | 6 +++--- 5 files changed, 16 insertions(+), 15 deletions(-) diff --git a/packages/@n8n/instance-ai/evaluations/data/workflows/notification-router.json b/packages/@n8n/instance-ai/evaluations/data/workflows/notification-router.json index 4395357e2ef..a02f229a749 100644 --- a/packages/@n8n/instance-ai/evaluations/data/workflows/notification-router.json +++ b/packages/@n8n/instance-ai/evaluations/data/workflows/notification-router.json @@ -10,7 +10,7 @@ }, { "role": "user", - "text": "JSON with `level` (high, medium, or low), `title`, and `message`. Each notification should be forwarded with title + message intact." + "text": "JSON with `level` (\"High\", \"Medium\" or \"Low\"), `title`, and `message`. Each notification should be forwarded with title + message intact." }, { "role": "assistant", @@ -29,19 +29,19 @@ { "name": "high-priority", "description": "A high-priority notification is routed to Microsoft Teams", - "dataSetup": "The webhook receives { \"level\": \"high\", \"title\": \"Server Down\", \"message\": \"Production database is unreachable\" }. The Microsoft Teams node returns a success response.", + "dataSetup": "The webhook receives { \"level\": \"High\", \"title\": \"Server Down\", \"message\": \"Production database is unreachable\" }. The Microsoft Teams node returns a success response.", "successCriteria": "The workflow routes the high-priority notification to Microsoft Teams. The Teams message contains 'Server Down' and the database message. No other channels receive the message." }, { "name": "medium-priority", "description": "A medium-priority notification is routed to Slack", - "dataSetup": "The webhook receives { \"level\": \"medium\", \"title\": \"Deployment Complete\", \"message\": \"Version 2.5.0 deployed to staging\" }. The Slack node returns a success response.", + "dataSetup": "The webhook receives { \"level\": \"Medium\", \"title\": \"Deployment Complete\", \"message\": \"Version 2.5.0 deployed to staging\" }. The Slack node returns a success response.", "successCriteria": "The workflow routes the medium-priority notification to Slack #notifications. The Slack message contains the deployment details." }, { "name": "low-priority", "description": "A low-priority notification is routed to email", - "dataSetup": "The webhook receives { \"level\": \"low\", \"title\": \"Weekly Report Ready\", \"message\": \"The weekly analytics report has been generated\" }. The Gmail node returns a success response.", + "dataSetup": "The webhook receives { \"level\": \"Low\", \"title\": \"Weekly Report Ready\", \"message\": \"The weekly analytics report has been generated\" }. The Gmail node returns a success response.", "successCriteria": "The workflow routes the low-priority notification to Gmail. The email is sent to alerts@ourcompany.com with the report notification." } ] diff --git a/packages/@n8n/workflow-sdk/src/prompts/node-guidance/parameter-guides/switch-node.ts b/packages/@n8n/workflow-sdk/src/prompts/node-guidance/parameter-guides/switch-node.ts index d78174d8dfc..a27d3421c14 100644 --- a/packages/@n8n/workflow-sdk/src/prompts/node-guidance/parameter-guides/switch-node.ts +++ b/packages/@n8n/workflow-sdk/src/prompts/node-guidance/parameter-guides/switch-node.ts @@ -16,7 +16,7 @@ The Switch node routes items to different outputs based on conditions. Uses the { "conditions": { "options": { - "caseSensitive": true, + "caseSensitive": false, "leftValue": "", "typeValidation": "strict" }, @@ -41,10 +41,11 @@ The Switch node routes items to different outputs based on conditions. Uses the \`\`\` #### Key Points: -1. Each entry in rules.values[] creates ONE output -2. Conditions use the same filter structure as IF node -3. Multiple conditions per rule are combined with combinator ("and" or "or") -4. Use renameOutput: true + outputKey to label outputs descriptively +1. **Default \`caseSensitive: false\` for string comparisons.** Real-world inputs vary in casing (\`"high"\` vs \`"High"\` vs \`"HIGH"\`). Only set \`caseSensitive: true\` when the user has explicitly said case matters — if you're not sure, ask them before guessing. +2. Each entry in rules.values[] creates ONE output +3. Conditions use the same filter structure as IF node +4. Multiple conditions per rule are combined with combinator ("and" or "or") +5. Use renameOutput: true + outputKey to label outputs descriptively #### Numeric Operators - lt: Less than @@ -68,6 +69,6 @@ Use two conditions with combinator: "and": **String-Based Routing** (status/type values): - Use type: "string" with operation: "equals" -- Set caseSensitive: false in options for case-insensitive matching +- Keep \`caseSensitive: false\` (the default above) unless the user explicitly asked for case-sensitive matching `, }; diff --git a/packages/@n8n/workflow-sdk/src/workflow-builder/plugins/validators/filter-node-validator.test.ts b/packages/@n8n/workflow-sdk/src/workflow-builder/plugins/validators/filter-node-validator.test.ts index ee3c6fb0c41..8a91a6f5703 100644 --- a/packages/@n8n/workflow-sdk/src/workflow-builder/plugins/validators/filter-node-validator.test.ts +++ b/packages/@n8n/workflow-sdk/src/workflow-builder/plugins/validators/filter-node-validator.test.ts @@ -27,7 +27,7 @@ function createCtx(nodes: Map = new Map()): PluginContext { } const VALID_CONDITIONS = { - options: { caseSensitive: true, leftValue: '', typeValidation: 'strict' }, + options: { caseSensitive: false, leftValue: '', typeValidation: 'strict' }, conditions: [ { leftValue: '={{ $json.field }}', diff --git a/packages/@n8n/workflow-sdk/src/workflow-builder/plugins/validators/filter-node-validator.ts b/packages/@n8n/workflow-sdk/src/workflow-builder/plugins/validators/filter-node-validator.ts index 35858564e7c..50ec69eb46c 100644 --- a/packages/@n8n/workflow-sdk/src/workflow-builder/plugins/validators/filter-node-validator.ts +++ b/packages/@n8n/workflow-sdk/src/workflow-builder/plugins/validators/filter-node-validator.ts @@ -37,7 +37,7 @@ function validateFilterValue( if (!filterValue.options || typeof filterValue.options !== 'object') { issues.push({ code: 'FILTER_MISSING_OPTIONS', - message: `${nodeRef} is missing 'options' in ${paramPath}. Add: options: { caseSensitive: true, leftValue: '', typeValidation: 'strict' }`, + message: `${nodeRef} is missing 'options' in ${paramPath}. Add: options: { caseSensitive: false, leftValue: '', typeValidation: 'strict' }`, severity: 'error', nodeName, originalName, diff --git a/packages/nodes-base/nodes/Switch/V3/SwitchV3.node.ts b/packages/nodes-base/nodes/Switch/V3/SwitchV3.node.ts index f751a2a3206..e27d374b232 100644 --- a/packages/nodes-base/nodes/Switch/V3/SwitchV3.node.ts +++ b/packages/nodes-base/nodes/Switch/V3/SwitchV3.node.ts @@ -75,7 +75,7 @@ const routeByPriority = switchCase({ { outputKey: 'urgent', conditions: { - options: { caseSensitive: true, leftValue: '', typeValidation: 'strict' }, + options: { caseSensitive: false, leftValue: '', typeValidation: 'strict' }, conditions: [{ leftValue: expr('{{ $json.priority }}'), operator: { type: 'string', operation: 'equals' }, rightValue: 'urgent' }], combinator: 'and' } @@ -83,7 +83,7 @@ const routeByPriority = switchCase({ { outputKey: 'normal', conditions: { - options: { caseSensitive: true, leftValue: '', typeValidation: 'strict' }, + options: { caseSensitive: false, leftValue: '', typeValidation: 'strict' }, conditions: [{ leftValue: expr('{{ $json.priority }}'), operator: { type: 'string', operation: 'equals' }, rightValue: 'normal' }], combinator: 'and' } @@ -180,7 +180,7 @@ export default workflow('id', 'name') type: 'fixedCollection', builderHint: { propertyHint: - "Use `rules.values` (NOT `rules.rules`). Each rule needs `outputKey` and a complete `conditions` object with these three sibling keys: `combinator` ('and' | 'or'), `conditions` (array of condition objects), `options` (`{ caseSensitive, leftValue, typeValidation }`). Same shape as IF. Wire rule outputs by zero-based index with `.onCase(index, target)`; `outputKey` is the visible output label, not the `.onCase()` argument. Unwired cases silently drop their items.", + "Use `rules.values` (NOT `rules.rules`). Each rule needs `outputKey` and a complete `conditions` object with these three sibling keys: `combinator` ('and' | 'or'), `conditions` (array of condition objects), `options` (`{ caseSensitive, leftValue, typeValidation }`). Same shape as IF. Wire rule outputs by zero-based index with `.onCase(index, target)`; `outputKey` is the visible output label, not the `.onCase()` argument. Unwired cases silently drop their items. Default `caseSensitive: false` for string equality, only set `true` when specifically requested or confirmed by the user.", }, typeOptions: { multipleValues: true,