@principal-ai/principal-view-core 0.22.1 → 0.23.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (37) hide show
  1. package/dist/index.d.ts +2 -2
  2. package/dist/index.d.ts.map +1 -1
  3. package/dist/index.js +1 -1
  4. package/dist/index.js.map +1 -1
  5. package/dist/node.d.ts +2 -2
  6. package/dist/node.d.ts.map +1 -1
  7. package/dist/node.js +1 -1
  8. package/dist/node.js.map +1 -1
  9. package/dist/storyboard/builder.js +2 -2
  10. package/dist/storyboard/builder.js.map +1 -1
  11. package/dist/workflow/example.d.ts.map +1 -1
  12. package/dist/workflow/example.js +6 -23
  13. package/dist/workflow/example.js.map +1 -1
  14. package/dist/workflow/index.d.ts +2 -2
  15. package/dist/workflow/index.d.ts.map +1 -1
  16. package/dist/workflow/index.js +1 -1
  17. package/dist/workflow/index.js.map +1 -1
  18. package/dist/workflow/scenario-matcher.d.ts +11 -27
  19. package/dist/workflow/scenario-matcher.d.ts.map +1 -1
  20. package/dist/workflow/scenario-matcher.js +26 -123
  21. package/dist/workflow/scenario-matcher.js.map +1 -1
  22. package/dist/workflow/types.d.ts +4 -42
  23. package/dist/workflow/types.d.ts.map +1 -1
  24. package/dist/workflow/validator.d.ts +34 -2
  25. package/dist/workflow/validator.d.ts.map +1 -1
  26. package/dist/workflow/validator.js +199 -116
  27. package/dist/workflow/validator.js.map +1 -1
  28. package/package.json +1 -1
  29. package/src/index.ts +1 -4
  30. package/src/node.ts +1 -4
  31. package/src/storyboard/builder.ts +2 -2
  32. package/src/workflow/__tests__/validator.test.ts +691 -107
  33. package/src/workflow/example.ts +6 -23
  34. package/src/workflow/index.ts +1 -3
  35. package/src/workflow/scenario-matcher.ts +32 -145
  36. package/src/workflow/types.ts +6 -61
  37. package/src/workflow/validator.ts +222 -124
@@ -50,6 +50,8 @@ export interface WorkflowTemplate {
50
50
  name: string;
51
51
  /** Purpose of this workflow view */
52
52
  description: string;
53
+ /** Exact span name this workflow applies to */
54
+ spanPattern: string;
53
55
  /** How to structure the workflow */
54
56
  mode: WorkflowMode;
55
57
  /** Scenario selection strategy */
@@ -70,8 +72,8 @@ export type WorkflowMode = 'span-tree' | 'timeline';
70
72
  /**
71
73
  * Workflow scenario definition
72
74
  *
73
- * Scenarios are mutually exclusive workflow templates selected
74
- * based on which events occurred during execution.
75
+ * Scenarios are mutually exclusive workflow templates.
76
+ * Required events are automatically derived from template.events keys.
75
77
  */
76
78
  export interface WorkflowScenario {
77
79
  /** Unique scenario identifier (kebab-case) */
@@ -80,49 +82,9 @@ export interface WorkflowScenario {
80
82
  priority: number;
81
83
  /** What this scenario represents */
82
84
  description: string;
83
- /** Conditions that must be met for this scenario to match */
84
- condition: ScenarioCondition;
85
85
  /** Workflow template content */
86
86
  template: ScenarioTemplate;
87
87
  }
88
- /**
89
- * Scenario matching conditions
90
- */
91
- export interface ScenarioCondition {
92
- /** Must have these events (supports glob patterns like "*.error") */
93
- requires?: string[];
94
- /** Must NOT have these events (supports glob patterns) */
95
- excludes?: string[];
96
- /** Assertions on event attributes */
97
- assertions?: Record<string, Assertion>;
98
- /** Always matches (use as fallback) */
99
- default?: boolean;
100
- /** Match if ANY requires condition met (instead of ALL) */
101
- any?: boolean;
102
- }
103
- /**
104
- * Attribute assertion operators
105
- */
106
- export interface Assertion {
107
- /** Greater than */
108
- $gt?: number;
109
- /** Greater than or equal */
110
- $gte?: number;
111
- /** Less than */
112
- $lt?: number;
113
- /** Less than or equal */
114
- $lte?: number;
115
- /** Equal to */
116
- $eq?: string | number | boolean;
117
- /** Not equal to */
118
- $ne?: string | number | boolean;
119
- /** Attribute exists/doesn't exist */
120
- $exists?: boolean;
121
- /** Value in array */
122
- $in?: (string | number | boolean)[];
123
- /** Value not in array */
124
- $nin?: (string | number | boolean)[];
125
- }
126
88
  /**
127
89
  * Scenario template content
128
90
  */
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/workflow/types.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/workflow/types.ts"],"names":[],"mappings":"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"}
@@ -80,6 +80,19 @@ export declare class WorkflowValidator {
80
80
  * Validate a workflow template
81
81
  */
82
82
  validate(context: WorkflowValidationContext): Promise<WorkflowValidationResult>;
83
+ /**
84
+ * Validate multiple workflows for duplicate spanPatterns
85
+ *
86
+ * This is a CLI-level validation that checks for conflicts across workflows.
87
+ * Call this method after validating individual workflows.
88
+ *
89
+ * @param workflows Array of workflow templates with their file paths
90
+ * @returns Array of violations for duplicate spanPatterns
91
+ */
92
+ static validateSpanPatterns(workflows: Array<{
93
+ workflow: WorkflowTemplate;
94
+ workflowPath: string;
95
+ }>): WorkflowViolation[];
83
96
  /**
84
97
  * Check schema validity (required fields, valid values)
85
98
  */
@@ -88,6 +101,10 @@ export declare class WorkflowValidator {
88
101
  * Check that the referenced canvas file exists
89
102
  */
90
103
  private checkCanvasExists;
104
+ /**
105
+ * Check for deprecated fields (condition, condition.requires, etc.)
106
+ */
107
+ private checkDeprecatedFields;
91
108
  /**
92
109
  * Check that events referenced in templates exist in the canvas
93
110
  *
@@ -101,9 +118,24 @@ export declare class WorkflowValidator {
101
118
  */
102
119
  private checkScenarios;
103
120
  /**
104
- * Check that condition uses valid fields (not legacy format)
121
+ * Check for subset relationships between scenarios
122
+ *
123
+ * Ensures no scenario's event set is a strict subset of another scenario's event set.
124
+ * This prevents ambiguous matching where a trace could match multiple scenarios.
125
+ */
126
+ private checkScenarioSubsets;
127
+ /**
128
+ * Check if set A is a strict subset of set B
129
+ *
130
+ * A is a strict subset of B if:
131
+ * 1. All elements of A are in B
132
+ * 2. A has fewer elements than B
133
+ */
134
+ private isStrictSubset;
135
+ /**
136
+ * Generate helpful suggestion for fixing subset relationship
105
137
  */
106
- private checkConditionStructure;
138
+ private generateSubsetFixSuggestion;
107
139
  /**
108
140
  * Check that template uses valid fields (not legacy format)
109
141
  */
@@ -1 +1 @@
1
- {"version":3,"file":"validator.d.ts","sourceRoot":"","sources":["../../src/workflow/validator.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"validator.d.ts","sourceRoot":"","sources":["../../src/workflow/validator.ts"],"names":[],"mappings":"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"}
@@ -16,7 +16,9 @@ export class WorkflowValidator {
16
16
  // Run all validation rules
17
17
  violations.push(...this.checkSchema(context));
18
18
  violations.push(...this.checkCanvasExists(context));
19
+ violations.push(...this.checkDeprecatedFields(context));
19
20
  violations.push(...this.checkScenarios(context));
21
+ violations.push(...this.checkScenarioSubsets(context));
20
22
  // Check event name syntax BEFORE checking event references
21
23
  // This ensures we catch unsupported syntax before trying to match events
22
24
  violations.push(...this.checkEventNameSyntax(context));
@@ -34,6 +36,49 @@ export class WorkflowValidator {
34
36
  }
35
37
  return this.aggregateResults(violations);
36
38
  }
39
+ /**
40
+ * Validate multiple workflows for duplicate spanPatterns
41
+ *
42
+ * This is a CLI-level validation that checks for conflicts across workflows.
43
+ * Call this method after validating individual workflows.
44
+ *
45
+ * @param workflows Array of workflow templates with their file paths
46
+ * @returns Array of violations for duplicate spanPatterns
47
+ */
48
+ static validateSpanPatterns(workflows) {
49
+ const violations = [];
50
+ const spanPatternMap = new Map(); // spanPattern -> workflow paths
51
+ // Collect all spanPatterns
52
+ for (const { workflow, workflowPath } of workflows) {
53
+ if (!workflow.spanPattern) {
54
+ continue; // Will be caught by individual validation
55
+ }
56
+ if (!spanPatternMap.has(workflow.spanPattern)) {
57
+ spanPatternMap.set(workflow.spanPattern, []);
58
+ }
59
+ spanPatternMap.get(workflow.spanPattern).push(workflowPath);
60
+ }
61
+ // Check for duplicates
62
+ for (const [spanPattern, paths] of spanPatternMap.entries()) {
63
+ if (paths.length > 1) {
64
+ // Duplicate spanPattern found
65
+ for (const path of paths) {
66
+ const otherPaths = paths.filter(p => p !== path);
67
+ violations.push({
68
+ ruleId: 'workflow-span-pattern-duplicate',
69
+ severity: 'error',
70
+ file: path,
71
+ path: 'spanPattern',
72
+ message: `Duplicate spanPattern "${spanPattern}" found in multiple workflows`,
73
+ impact: 'Multiple workflows cannot match the same span - this creates ambiguous workflow selection',
74
+ suggestion: `This spanPattern is also used in:\n${otherPaths.map(p => ` - ${p}`).join('\n')}\n\nEach workflow must have a unique spanPattern. Consider:\n - Using different span names (e.g., "payment.authorize" vs "payment.refund")\n - Merging these workflows into one with multiple scenarios`,
75
+ fixable: false,
76
+ });
77
+ }
78
+ }
79
+ }
80
+ return violations;
81
+ }
37
82
  /**
38
83
  * Check schema validity (required fields, valid values)
39
84
  */
@@ -104,6 +149,31 @@ export class WorkflowValidator {
104
149
  fixable: false,
105
150
  });
106
151
  }
152
+ // Check spanPattern
153
+ if (!workflow.spanPattern) {
154
+ violations.push({
155
+ ruleId: 'workflow-schema-valid',
156
+ severity: 'error',
157
+ file: workflowPath,
158
+ path: 'spanPattern',
159
+ message: 'Missing required field "spanPattern"',
160
+ impact: 'Cannot determine which spans this workflow applies to',
161
+ suggestion: 'Add a spanPattern field specifying the exact span name (e.g., "payment.authorize")',
162
+ fixable: false,
163
+ });
164
+ }
165
+ else if (typeof workflow.spanPattern !== 'string' || workflow.spanPattern.trim() === '') {
166
+ violations.push({
167
+ ruleId: 'workflow-schema-valid',
168
+ severity: 'error',
169
+ file: workflowPath,
170
+ path: 'spanPattern',
171
+ message: 'spanPattern must be a non-empty string',
172
+ impact: 'Cannot match spans with invalid pattern',
173
+ suggestion: 'Provide a valid span name (e.g., "payment.authorize", "checkout.process")',
174
+ fixable: false,
175
+ });
176
+ }
107
177
  // Check mode
108
178
  const validModes = ['span-tree', 'timeline'];
109
179
  if (!workflow.mode) {
@@ -197,6 +267,60 @@ export class WorkflowValidator {
197
267
  }
198
268
  return violations;
199
269
  }
270
+ /**
271
+ * Check for deprecated fields (condition, condition.requires, etc.)
272
+ */
273
+ checkDeprecatedFields(context) {
274
+ const violations = [];
275
+ const { workflow, workflowPath } = context;
276
+ if (!workflow.scenarios || workflow.scenarios.length === 0) {
277
+ return violations;
278
+ }
279
+ workflow.scenarios.forEach((scenario, idx) => {
280
+ // Check if scenario has a 'condition' field
281
+ const scenarioAny = scenario;
282
+ if (scenarioAny.condition) {
283
+ const condition = scenarioAny.condition;
284
+ // Build list of what was in condition
285
+ const conditionFields = [];
286
+ if (condition.requires)
287
+ conditionFields.push('requires');
288
+ if (condition.excludes)
289
+ conditionFields.push('excludes');
290
+ if (condition.assertions)
291
+ conditionFields.push('assertions');
292
+ if (condition.default)
293
+ conditionFields.push('default');
294
+ if (condition.any)
295
+ conditionFields.push('any');
296
+ const fieldsDescription = conditionFields.length > 0
297
+ ? ` (${conditionFields.join(', ')})`
298
+ : '';
299
+ violations.push({
300
+ ruleId: 'workflow-deprecated-condition',
301
+ severity: 'error',
302
+ file: workflowPath,
303
+ path: `scenarios[${idx}].condition`,
304
+ message: `The "condition" field is no longer supported${fieldsDescription}`,
305
+ impact: 'Required events are now automatically derived from template.events keys',
306
+ suggestion: `Remove the "condition" field. Required events are inferred from template.events.\n\n` +
307
+ `Migration:\n` +
308
+ ` Before:\n` +
309
+ ` {\n` +
310
+ ` "condition": { "requires": ["event.a", "event.b"] },\n` +
311
+ ` "template": { "events": { "event.a": "...", "event.b": "..." }}\n` +
312
+ ` }\n\n` +
313
+ ` After:\n` +
314
+ ` {\n` +
315
+ ` "template": { "events": { "event.a": "...", "event.b": "..." }}\n` +
316
+ ` }\n\n` +
317
+ `Simply remove the "condition" field - the same events should already be defined in "template.events".`,
318
+ fixable: true,
319
+ });
320
+ }
321
+ });
322
+ return violations;
323
+ }
200
324
  /**
201
325
  * Check that events referenced in templates exist in the canvas
202
326
  *
@@ -237,18 +361,9 @@ export class WorkflowValidator {
237
361
  canvasEvents.add(node.pv.eventRef);
238
362
  }
239
363
  }
240
- // Extract all event names from workflow scenarios
364
+ // Extract all event names from workflow scenarios (from template.events)
241
365
  const workflowEvents = new Set();
242
366
  for (const scenario of workflow.scenarios) {
243
- // From condition.requires
244
- if (scenario.condition?.requires) {
245
- for (const eventPattern of scenario.condition.requires) {
246
- // Skip wildcard patterns for now
247
- if (!eventPattern.includes('*')) {
248
- workflowEvents.add(eventPattern);
249
- }
250
- }
251
- }
252
367
  // From template.events
253
368
  if (scenario.template?.events) {
254
369
  for (const eventName of Object.keys(scenario.template.events)) {
@@ -327,7 +442,6 @@ export class WorkflowValidator {
327
442
  }
328
443
  const scenarioIds = new Set();
329
444
  const priorities = new Set();
330
- let hasDefault = false;
331
445
  workflow.scenarios.forEach((scenario, idx) => {
332
446
  // Check for required fields
333
447
  if (!scenario.id) {
@@ -399,27 +513,6 @@ export class WorkflowValidator {
399
513
  }
400
514
  priorities.add(scenario.priority);
401
515
  }
402
- // Check for default scenario
403
- if (scenario.condition?.default === true) {
404
- hasDefault = true;
405
- }
406
- // Check condition
407
- if (!scenario.condition) {
408
- violations.push({
409
- ruleId: 'workflow-scenario-valid',
410
- severity: 'error',
411
- file: workflowPath,
412
- path: `scenarios[${idx}].condition`,
413
- message: 'Scenario is missing required "condition" field',
414
- impact: 'Cannot determine when to use this scenario',
415
- suggestion: 'Add a condition or set default: true',
416
- fixable: false,
417
- });
418
- }
419
- else {
420
- // Validate condition structure
421
- violations.push(...this.checkConditionStructure(scenario.condition, workflowPath, idx));
422
- }
423
516
  // Check template
424
517
  if (!scenario.template) {
425
518
  violations.push({
@@ -438,69 +531,48 @@ export class WorkflowValidator {
438
531
  violations.push(...this.checkTemplateStructure(scenario.template, workflowPath, idx));
439
532
  }
440
533
  });
441
- // Ensure at least one default scenario exists
442
- if (!hasDefault) {
443
- violations.push({
444
- ruleId: 'workflow-scenario-valid',
445
- severity: 'error',
446
- file: workflowPath,
447
- path: 'scenarios',
448
- message: 'No default scenario defined',
449
- impact: 'Workflow rendering may fail if no scenario matches',
450
- suggestion: 'Add a scenario with "condition.default: true" as a fallback',
451
- fixable: false,
452
- });
453
- }
454
534
  return violations;
455
535
  }
456
536
  /**
457
- * Check that condition uses valid fields (not legacy format)
537
+ * Check for subset relationships between scenarios
538
+ *
539
+ * Ensures no scenario's event set is a strict subset of another scenario's event set.
540
+ * This prevents ambiguous matching where a trace could match multiple scenarios.
458
541
  */
459
- checkConditionStructure(condition, file, scenarioIdx) {
542
+ checkScenarioSubsets(context) {
460
543
  const violations = [];
461
- const validFields = ['requires', 'excludes', 'assertions', 'default', 'any'];
462
- // Type guard: ensure condition is an object
463
- if (typeof condition !== 'object' || condition === null) {
464
- return violations;
544
+ const { workflow, workflowPath } = context;
545
+ if (!workflow.scenarios || workflow.scenarios.length < 2) {
546
+ return violations; // Need at least 2 scenarios to check subsets
465
547
  }
466
- const conditionKeys = Object.keys(condition);
467
- // Check for invalid/legacy fields
468
- for (const key of conditionKeys) {
469
- if (!validFields.includes(key)) {
470
- // Check for common legacy format fields
471
- if (key === 'event') {
472
- violations.push({
473
- ruleId: 'workflow-condition-structure',
474
- severity: 'error',
475
- file,
476
- path: `scenarios[${scenarioIdx}].condition.${key}`,
477
- message: `Invalid condition field "${key}" (legacy format detected)`,
478
- impact: 'Condition will not work - "event" field is not supported',
479
- suggestion: 'Use "requires: [...]" array instead of "event: ..." field',
480
- fixable: false,
481
- });
482
- }
483
- else if (key === 'attributes') {
548
+ const scenarios = workflow.scenarios;
549
+ for (let i = 0; i < scenarios.length; i++) {
550
+ for (let j = i + 1; j < scenarios.length; j++) {
551
+ const eventsA = new Set(Object.keys(scenarios[i].template?.events || {}));
552
+ const eventsB = new Set(Object.keys(scenarios[j].template?.events || {}));
553
+ // Check if A is a strict subset of B
554
+ if (this.isStrictSubset(eventsA, eventsB)) {
484
555
  violations.push({
485
- ruleId: 'workflow-condition-structure',
556
+ ruleId: 'workflow-scenario-subset',
486
557
  severity: 'error',
487
- file,
488
- path: `scenarios[${scenarioIdx}].condition.${key}`,
489
- message: `Invalid condition field "${key}" (legacy format detected)`,
490
- impact: 'Condition will not work - "attributes" field is not supported',
491
- suggestion: 'Use "assertions: { ... }" instead of "attributes: { ... }" field',
558
+ file: workflowPath,
559
+ path: `scenarios[${i}]`,
560
+ message: `Scenario "${scenarios[i].id}" is a strict subset of "${scenarios[j].id}"`,
561
+ impact: 'A trace with all events from both scenarios will match both, causing ambiguous scenario selection',
562
+ suggestion: this.generateSubsetFixSuggestion(scenarios[i], scenarios[j]),
492
563
  fixable: false,
493
564
  });
494
565
  }
495
- else {
566
+ // Check if B is a strict subset of A
567
+ if (this.isStrictSubset(eventsB, eventsA)) {
496
568
  violations.push({
497
- ruleId: 'workflow-condition-structure',
569
+ ruleId: 'workflow-scenario-subset',
498
570
  severity: 'error',
499
- file,
500
- path: `scenarios[${scenarioIdx}].condition.${key}`,
501
- message: `Unknown condition field "${key}"`,
502
- impact: 'This field will be ignored and may cause unexpected behavior',
503
- suggestion: `Valid fields are: ${validFields.join(', ')}`,
571
+ file: workflowPath,
572
+ path: `scenarios[${j}]`,
573
+ message: `Scenario "${scenarios[j].id}" is a strict subset of "${scenarios[i].id}"`,
574
+ impact: 'A trace with all events from both scenarios will match both, causing ambiguous scenario selection',
575
+ suggestion: this.generateSubsetFixSuggestion(scenarios[j], scenarios[i]),
504
576
  fixable: false,
505
577
  });
506
578
  }
@@ -508,6 +580,51 @@ export class WorkflowValidator {
508
580
  }
509
581
  return violations;
510
582
  }
583
+ /**
584
+ * Check if set A is a strict subset of set B
585
+ *
586
+ * A is a strict subset of B if:
587
+ * 1. All elements of A are in B
588
+ * 2. A has fewer elements than B
589
+ */
590
+ isStrictSubset(A, B) {
591
+ // A must have fewer elements than B
592
+ if (A.size >= B.size) {
593
+ return false;
594
+ }
595
+ // All elements of A must be in B
596
+ for (const item of A) {
597
+ if (!B.has(item)) {
598
+ return false;
599
+ }
600
+ }
601
+ return true;
602
+ }
603
+ /**
604
+ * Generate helpful suggestion for fixing subset relationship
605
+ */
606
+ generateSubsetFixSuggestion(subsetScenario, supersetScenario) {
607
+ const subsetEvents = Object.keys(subsetScenario.template?.events || {});
608
+ const supersetEvents = Object.keys(supersetScenario.template?.events || {});
609
+ const extraEvents = supersetEvents.filter(e => !subsetEvents.includes(e));
610
+ return (`Scenario "${subsetScenario.id}" events: [${subsetEvents.join(', ')}]\n` +
611
+ `Scenario "${supersetScenario.id}" events: [${supersetEvents.join(', ')}]\n\n` +
612
+ `Recommended fixes:\n\n` +
613
+ `1. Merge into one scenario with template conditionals:\n` +
614
+ ` {\n` +
615
+ ` "id": "${subsetScenario.id}",\n` +
616
+ ` "template": {\n` +
617
+ ` "events": { ${subsetEvents.map(e => `"${e}": "..."`).join(', ')} },\n` +
618
+ ` "flow": [\n` +
619
+ ` "Base flow steps...",\n` +
620
+ ` {{#if ${extraEvents[0]}}}Additional step...{{/if}}\n` +
621
+ ` ]\n` +
622
+ ` }\n` +
623
+ ` }\n\n` +
624
+ `2. Make them mutually exclusive by adding distinguishing events:\n` +
625
+ ` - Add an event to "${subsetScenario.id}" that distinguishes it (e.g., "*.timeout", "*.abandoned")\n` +
626
+ ` - Or ensure "${supersetScenario.id}" has events that never co-occur with "${subsetScenario.id}"`);
627
+ }
511
628
  /**
512
629
  * Check that template uses valid fields (not legacy format)
513
630
  */
@@ -608,40 +725,6 @@ export class WorkflowValidator {
608
725
  const violations = [];
609
726
  const { workflow, workflowPath } = context;
610
727
  workflow.scenarios.forEach((scenario, scenarioIdx) => {
611
- // Check condition.requires
612
- if (scenario.condition?.requires) {
613
- scenario.condition.requires.forEach((eventPattern, idx) => {
614
- if (eventPattern.includes('[') && eventPattern.includes(']')) {
615
- violations.push({
616
- ruleId: 'workflow-event-name-syntax',
617
- severity: 'error',
618
- file: workflowPath,
619
- path: `scenarios[${scenarioIdx}].condition.requires[${idx}]`,
620
- message: `Event name uses unsupported [attribute=value] syntax: "${eventPattern}"`,
621
- impact: 'Attribute filter syntax is not supported - event will not match',
622
- suggestion: `Use a distinct event name instead (e.g., "${this.extractBaseEventName(eventPattern)}.${this.extractAttributeValue(eventPattern)}")`,
623
- fixable: false,
624
- });
625
- }
626
- });
627
- }
628
- // Check condition.excludes
629
- if (scenario.condition?.excludes) {
630
- scenario.condition.excludes.forEach((eventPattern, idx) => {
631
- if (eventPattern.includes('[') && eventPattern.includes(']')) {
632
- violations.push({
633
- ruleId: 'workflow-event-name-syntax',
634
- severity: 'error',
635
- file: workflowPath,
636
- path: `scenarios[${scenarioIdx}].condition.excludes[${idx}]`,
637
- message: `Event name uses unsupported [attribute=value] syntax: "${eventPattern}"`,
638
- impact: 'Attribute filter syntax is not supported - event will not match',
639
- suggestion: `Use a distinct event name instead (e.g., "${this.extractBaseEventName(eventPattern)}.${this.extractAttributeValue(eventPattern)}")`,
640
- fixable: false,
641
- });
642
- }
643
- });
644
- }
645
728
  // Check template.events
646
729
  if (scenario.template?.events) {
647
730
  Object.keys(scenario.template.events).forEach((eventName) => {