@ema.co/mcp-toolkit 2026.1.27 → 2026.1.28-2
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.
Potentially problematic release.
This version of @ema.co/mcp-toolkit might be problematic. Click here for more details.
- package/dist/mcp/handlers/data/index.js +3 -0
- package/dist/mcp/handlers/persona/create.js +16 -0
- package/dist/mcp/handlers/persona/list.js +9 -4
- package/dist/mcp/handlers/persona/update.js +24 -2
- package/dist/mcp/handlers/workflow/deploy.js +20 -2
- package/dist/mcp/handlers/workflow/generate.js +39 -2
- package/dist/mcp/handlers/workflow/index.js +8 -3
- package/dist/mcp/handlers/workflow/modify.js +34 -7
- package/dist/mcp/handlers/workflow/validate.js +85 -0
- package/dist/mcp/handlers/workflow/validation.js +160 -0
- package/dist/mcp/resources.js +286 -4
- package/dist/mcp/server.js +16 -3
- package/dist/mcp/tools.js +32 -11
- package/dist/sdk/client.js +36 -9
- package/dist/sdk/ema-client.js +32 -4
- package/dist/sdk/index.js +3 -1
- package/dist/sdk/knowledge.js +5 -5
- package/dist/sdk/structural-rules.js +498 -0
- package/dist/sdk/workflow-generator.js +2 -1
- package/dist/sdk/workflow-intent.js +28 -96
- package/dist/sdk/workflow-path-enumerator.js +278 -0
- package/dist/sdk/workflow-static-validator.js +291 -0
- package/dist/sdk/workflow-validation-types.js +7 -0
- package/docs/README.md +14 -0
- package/docs/go-validator-analysis.md +323 -0
- package/docs/rule-format-specification.md +346 -0
- package/docs/validation-contract.md +397 -0
- package/docs/validation-error-format.md +326 -0
- package/package.json +1 -1
- package/dist/mcp/workflow-operations.js +0 -100
- package/dist/sdk/workflow-fixer.js +0 -48
- package/docs/dashboard-operations.md +0 -281
- package/docs/ema-user-guide.md +0 -1201
- package/docs/email-patterns.md +0 -120
- package/docs/mcp-tools-guide.md +0 -575
|
@@ -0,0 +1,397 @@
|
|
|
1
|
+
# Validation Contract - Go to TypeScript Compatibility
|
|
2
|
+
|
|
3
|
+
**Date**: 2026-01-27
|
|
4
|
+
**Purpose**: Define exact semantics for TypeScript validator to match Go validator behavior
|
|
5
|
+
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## Core Validation Algorithm
|
|
9
|
+
|
|
10
|
+
### Overview
|
|
11
|
+
|
|
12
|
+
The Go validator performs **static validation** by:
|
|
13
|
+
1. Enumerating all possible execution paths in a workflow
|
|
14
|
+
2. Validating each path for named results compliance
|
|
15
|
+
3. Reporting errors for paths that fail validation
|
|
16
|
+
|
|
17
|
+
### Key Principle
|
|
18
|
+
|
|
19
|
+
**Static validation only applies to workflows with named results.**
|
|
20
|
+
- If `workflow.Proto.NamedResults` is empty, validation is skipped
|
|
21
|
+
- This is intentional - workflows without named results don't need path validation
|
|
22
|
+
|
|
23
|
+
---
|
|
24
|
+
|
|
25
|
+
## Path Enumeration Algorithm
|
|
26
|
+
|
|
27
|
+
### Step 1: Create Initial Session
|
|
28
|
+
|
|
29
|
+
```go
|
|
30
|
+
initialSession, err := CreateValidationWorkflowSession(ctx, workflow, logger)
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
Creates a validation session with:
|
|
34
|
+
- Dummy workflow inputs (type-aware)
|
|
35
|
+
- Dummy widget configs (type-aware)
|
|
36
|
+
- Empty action states
|
|
37
|
+
- Static validation strategy
|
|
38
|
+
|
|
39
|
+
### Step 2: Process Session Queue
|
|
40
|
+
|
|
41
|
+
Main loop:
|
|
42
|
+
```go
|
|
43
|
+
sessionsToProcess := []ValidationWorkflowSession{initialSession}
|
|
44
|
+
for len(sessionsToProcess) > 0 {
|
|
45
|
+
session := sessionsToProcess[0]
|
|
46
|
+
sessionsToProcess = sessionsToProcess[1:]
|
|
47
|
+
|
|
48
|
+
result, err := session.RunValidation(ctx)
|
|
49
|
+
// Handle result...
|
|
50
|
+
}
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
### Step 3: Handle Branching
|
|
54
|
+
|
|
55
|
+
When `result.Outcome == OutcomeBranch`:
|
|
56
|
+
|
|
57
|
+
1. **Identify Branching Action**: Action with enumerable output (enum or boolean)
|
|
58
|
+
2. **Get Possible Values**: All enum options or [true, false] for boolean
|
|
59
|
+
3. **Fork Session**: Create one session per possible value
|
|
60
|
+
4. **Plug Dummy Values**:
|
|
61
|
+
- For branching output: Use chosen value
|
|
62
|
+
- For other outputs: Generate type-aware dummy values
|
|
63
|
+
5. **Mark Action Complete**: Mark branching action as completed in forked session
|
|
64
|
+
6. **Queue New Sessions**: Add all forked sessions to queue
|
|
65
|
+
|
|
66
|
+
**Branching Actions**:
|
|
67
|
+
- Categorizers (enum output: category)
|
|
68
|
+
- Boolean outputs (true/false)
|
|
69
|
+
- Any action with enumerable output
|
|
70
|
+
|
|
71
|
+
**Non-Branching Actions**:
|
|
72
|
+
- Generate type-aware dummy outputs for all outputs
|
|
73
|
+
- Mark action as completed
|
|
74
|
+
- Continue traversal
|
|
75
|
+
|
|
76
|
+
### Step 4: Handle Completion
|
|
77
|
+
|
|
78
|
+
When `result.Outcome == OutcomeCompleted`:
|
|
79
|
+
|
|
80
|
+
1. **Track Completed Path**:
|
|
81
|
+
- Completed actions (topological order)
|
|
82
|
+
- Final outputs produced
|
|
83
|
+
- Path identifier (branching action + value, or last action)
|
|
84
|
+
|
|
85
|
+
2. **Validate Named Results**:
|
|
86
|
+
- Check if all named results are produced
|
|
87
|
+
- Check for multiple writers
|
|
88
|
+
- Check for response/abstain
|
|
89
|
+
- Apply custom validators (persona-type specific)
|
|
90
|
+
|
|
91
|
+
3. **Store Path Info**: Add to `completedSessionMap`
|
|
92
|
+
|
|
93
|
+
### Step 5: Cleanup
|
|
94
|
+
|
|
95
|
+
Remove incomplete sessions (those that only branched but never completed).
|
|
96
|
+
|
|
97
|
+
---
|
|
98
|
+
|
|
99
|
+
## Validation Error Types
|
|
100
|
+
|
|
101
|
+
### 1. RESULT_ERROR_TYPE_REQUIRED_OUTPUT_NOT_PRODUCED
|
|
102
|
+
|
|
103
|
+
**Definition**: A named result is not produced on at least one execution path.
|
|
104
|
+
|
|
105
|
+
**Detection**:
|
|
106
|
+
- For each named result in `workflow.Proto.NamedResults`
|
|
107
|
+
- For each completed path in `completedSessionMap`
|
|
108
|
+
- Check if named result is in `FinalOutputs` of that path
|
|
109
|
+
- If missing on any path → error
|
|
110
|
+
|
|
111
|
+
**Path Identification**:
|
|
112
|
+
- If path branched: `{actionName: branching_action, outputName: category, outputValue: chosen_value}`
|
|
113
|
+
- If path didn't branch: `{actionName: last_completed_action}`
|
|
114
|
+
|
|
115
|
+
**Example**:
|
|
116
|
+
```
|
|
117
|
+
Named result: "response_with_sources"
|
|
118
|
+
Path "Billing" (from categorizer, value="Billing") does not produce it
|
|
119
|
+
→ Error: RESULT_ERROR_TYPE_REQUIRED_OUTPUT_NOT_PRODUCED
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
### 2. RESULT_ERROR_TYPE_MULTIPLE_WRITERS
|
|
123
|
+
|
|
124
|
+
**Definition**: Multiple actions produce the same named result on the same execution path.
|
|
125
|
+
|
|
126
|
+
**Detection**:
|
|
127
|
+
- For each named result
|
|
128
|
+
- For each completed path
|
|
129
|
+
- Count how many actions produce that result
|
|
130
|
+
- If count > 1 on any path → error
|
|
131
|
+
|
|
132
|
+
**Note**: This may be intentional (aggregation), but Go validator flags it as error.
|
|
133
|
+
|
|
134
|
+
**Path Identification**: Same as above.
|
|
135
|
+
|
|
136
|
+
**Example**:
|
|
137
|
+
```
|
|
138
|
+
Named result: "response_with_sources"
|
|
139
|
+
Path "Billing" has both "respond_1" and "respond_2" producing it
|
|
140
|
+
→ Error: RESULT_ERROR_TYPE_MULTIPLE_WRITERS
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
### 3. RESULT_ERROR_TYPE_MISSING_RESPONSE_OR_ABSTAIN_REASON
|
|
144
|
+
|
|
145
|
+
**Definition**: An execution path does not produce a response and does not have an abstain reason.
|
|
146
|
+
|
|
147
|
+
**Detection**:
|
|
148
|
+
- For each completed path
|
|
149
|
+
- Check if path produces any response output:
|
|
150
|
+
- `respond_with_sources.response_with_sources`
|
|
151
|
+
- `call_llm.llm_output`
|
|
152
|
+
- `fixed_response.response`
|
|
153
|
+
- Other response-like outputs
|
|
154
|
+
- If no response AND no abstain reason → error
|
|
155
|
+
|
|
156
|
+
**Abstain Reason**: TBD - need to check how this is defined in Go code.
|
|
157
|
+
|
|
158
|
+
**Path Identification**: Same as above.
|
|
159
|
+
|
|
160
|
+
**Example**:
|
|
161
|
+
```
|
|
162
|
+
Path "Billing" ends at "search" node
|
|
163
|
+
No response node connected
|
|
164
|
+
No abstain reason specified
|
|
165
|
+
→ Error: RESULT_ERROR_TYPE_MISSING_RESPONSE_OR_ABSTAIN_REASON
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
### 4. RESULT_ERROR_TYPE_CATEGORY_HIERARCHY_VIOLATION
|
|
169
|
+
|
|
170
|
+
**Definition**: Categorizer category structure violates hierarchy rules.
|
|
171
|
+
|
|
172
|
+
**Detection**: TBD - need to analyze `named_result_agent_assist_validator.go`
|
|
173
|
+
|
|
174
|
+
**Path Identification**: Same as above.
|
|
175
|
+
|
|
176
|
+
**Example**: TBD
|
|
177
|
+
|
|
178
|
+
---
|
|
179
|
+
|
|
180
|
+
## Path Identification Rules
|
|
181
|
+
|
|
182
|
+
### Rule 1: Branching Path Identification
|
|
183
|
+
|
|
184
|
+
If path was created by branching action:
|
|
185
|
+
```
|
|
186
|
+
{
|
|
187
|
+
actionName: "<branching_action_name>",
|
|
188
|
+
outputName: "<branching_output_name>",
|
|
189
|
+
outputValue: "<chosen_value>"
|
|
190
|
+
}
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
**Example**:
|
|
194
|
+
```
|
|
195
|
+
{
|
|
196
|
+
actionName: "chat_categorizer",
|
|
197
|
+
outputName: "category",
|
|
198
|
+
outputValue: "Billing"
|
|
199
|
+
}
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
### Rule 2: Non-Branching Path Identification
|
|
203
|
+
|
|
204
|
+
If path had no branching action:
|
|
205
|
+
```
|
|
206
|
+
{
|
|
207
|
+
actionName: "<last_completed_action_name>"
|
|
208
|
+
}
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
**Example**:
|
|
212
|
+
```
|
|
213
|
+
{
|
|
214
|
+
actionName: "respond_with_sources"
|
|
215
|
+
}
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
### Rule 3: Empty Path
|
|
219
|
+
|
|
220
|
+
If no actions were executed (shouldn't happen, but handle gracefully):
|
|
221
|
+
```
|
|
222
|
+
{
|
|
223
|
+
actionName: ""
|
|
224
|
+
}
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
---
|
|
228
|
+
|
|
229
|
+
## Dummy Value Generation
|
|
230
|
+
|
|
231
|
+
### For Workflow Inputs
|
|
232
|
+
|
|
233
|
+
- Generate type-aware dummy values based on input type
|
|
234
|
+
- Use `util.CreateTypeAwareDummyValue(type, enumTypes)`
|
|
235
|
+
- Validate type compatibility
|
|
236
|
+
|
|
237
|
+
### For Widget Configs
|
|
238
|
+
|
|
239
|
+
- Generate type-aware dummy values based on widget type
|
|
240
|
+
- Use same utility function
|
|
241
|
+
- Validate type compatibility
|
|
242
|
+
|
|
243
|
+
### For Action Outputs (Non-Branching)
|
|
244
|
+
|
|
245
|
+
- Generate type-aware dummy values for all outputs
|
|
246
|
+
- Use `getDummyValueForActionOutput(outputDef, enumTypes)`
|
|
247
|
+
- Based on output type definition
|
|
248
|
+
|
|
249
|
+
### For Action Outputs (Branching)
|
|
250
|
+
|
|
251
|
+
- For branching output: Use chosen value (enum option or boolean)
|
|
252
|
+
- For other outputs: Generate type-aware dummy values
|
|
253
|
+
|
|
254
|
+
---
|
|
255
|
+
|
|
256
|
+
## Custom Validators
|
|
257
|
+
|
|
258
|
+
### Interface
|
|
259
|
+
|
|
260
|
+
```go
|
|
261
|
+
type CustomNamedResultValidator interface {
|
|
262
|
+
Validate(workflow *WorkflowGraph, pathInfo *completedPathInfo) error
|
|
263
|
+
}
|
|
264
|
+
```
|
|
265
|
+
|
|
266
|
+
### Behavior
|
|
267
|
+
|
|
268
|
+
- Called after general named result validations pass
|
|
269
|
+
- Only called if `!pathInfo.NamedResultsValidationsFailed`
|
|
270
|
+
- Can add persona-type specific rules
|
|
271
|
+
- Example: `named_result_agent_assist_validator.go` for Agent Assist personas
|
|
272
|
+
|
|
273
|
+
### Implementation Note
|
|
274
|
+
|
|
275
|
+
- TypeScript validator should support plugin system for custom validators
|
|
276
|
+
- For now, focus on the 4 core error types
|
|
277
|
+
- Custom validators can be added later
|
|
278
|
+
|
|
279
|
+
---
|
|
280
|
+
|
|
281
|
+
## Error Message Format (Go)
|
|
282
|
+
|
|
283
|
+
### Structure
|
|
284
|
+
|
|
285
|
+
```go
|
|
286
|
+
type WorkflowError_NamedResultError struct {
|
|
287
|
+
ErrorType pb.WorkflowError_ResultErrorType
|
|
288
|
+
NamedResultName string
|
|
289
|
+
PathIdentifier *WorkflowError_ActionOutput
|
|
290
|
+
Message string
|
|
291
|
+
}
|
|
292
|
+
```
|
|
293
|
+
|
|
294
|
+
### Path Identifier
|
|
295
|
+
|
|
296
|
+
```go
|
|
297
|
+
type WorkflowError_ActionOutput struct {
|
|
298
|
+
ActionName string
|
|
299
|
+
OutputName string // Optional, only for branching
|
|
300
|
+
OutputValue string // Optional, only for branching
|
|
301
|
+
}
|
|
302
|
+
```
|
|
303
|
+
|
|
304
|
+
### Message Format
|
|
305
|
+
|
|
306
|
+
TBD - need to analyze actual error messages from Go code.
|
|
307
|
+
|
|
308
|
+
---
|
|
309
|
+
|
|
310
|
+
## TypeScript Compatibility Requirements
|
|
311
|
+
|
|
312
|
+
### Must Match
|
|
313
|
+
|
|
314
|
+
1. **Error Types**: All 4 error types must be detected
|
|
315
|
+
2. **Path Enumeration**: Same algorithm (topological sort + DFS + forking)
|
|
316
|
+
3. **Path Identification**: Same format (actionName, outputName, outputValue)
|
|
317
|
+
4. **Validation Logic**: Same checks (required outputs, multiple writers, etc.)
|
|
318
|
+
|
|
319
|
+
### Can Differ
|
|
320
|
+
|
|
321
|
+
1. **Error Message Format**: Can be enhanced (what/why/how)
|
|
322
|
+
2. **Performance**: Can optimize (caching, early pruning)
|
|
323
|
+
3. **Additional Validations**: Can add more (but document as extensions)
|
|
324
|
+
|
|
325
|
+
---
|
|
326
|
+
|
|
327
|
+
## Test Cases
|
|
328
|
+
|
|
329
|
+
### Test Case 1: Simple Linear Workflow
|
|
330
|
+
|
|
331
|
+
```
|
|
332
|
+
trigger → action1 → action2 → respond → WORKFLOW_OUTPUT
|
|
333
|
+
Named result: "response"
|
|
334
|
+
|
|
335
|
+
Expected: ✅ Valid (all paths produce response)
|
|
336
|
+
```
|
|
337
|
+
|
|
338
|
+
### Test Case 2: Categorizer with Missing Response
|
|
339
|
+
|
|
340
|
+
```
|
|
341
|
+
trigger → categorizer → [Billing branch] → search → (no response)
|
|
342
|
+
Named result: "response"
|
|
343
|
+
|
|
344
|
+
Expected: ❌ Error: RESULT_ERROR_TYPE_REQUIRED_OUTPUT_NOT_PRODUCED
|
|
345
|
+
Path: {actionName: "categorizer", outputName: "category", outputValue: "Billing"}
|
|
346
|
+
```
|
|
347
|
+
|
|
348
|
+
### Test Case 3: Multiple Writers
|
|
349
|
+
|
|
350
|
+
```
|
|
351
|
+
trigger → categorizer → [Billing] → respond1 → WORKFLOW_OUTPUT
|
|
352
|
+
→ [Billing] → respond2 → WORKFLOW_OUTPUT
|
|
353
|
+
Named result: "response" (both respond1 and respond2 produce it)
|
|
354
|
+
|
|
355
|
+
Expected: ❌ Error: RESULT_ERROR_TYPE_MULTIPLE_WRITERS
|
|
356
|
+
Path: {actionName: "categorizer", outputName: "category", outputValue: "Billing"}
|
|
357
|
+
```
|
|
358
|
+
|
|
359
|
+
### Test Case 4: Missing Response/Abstain
|
|
360
|
+
|
|
361
|
+
```
|
|
362
|
+
trigger → categorizer → [Billing] → search → (ends, no response, no abstain)
|
|
363
|
+
Named result: "response"
|
|
364
|
+
|
|
365
|
+
Expected: ❌ Error: RESULT_ERROR_TYPE_MISSING_RESPONSE_OR_ABSTAIN_REASON
|
|
366
|
+
Path: {actionName: "categorizer", outputName: "category", outputValue: "Billing"}
|
|
367
|
+
```
|
|
368
|
+
|
|
369
|
+
---
|
|
370
|
+
|
|
371
|
+
## Performance Considerations
|
|
372
|
+
|
|
373
|
+
### Timeout Limits
|
|
374
|
+
|
|
375
|
+
- Target: <100ms for typical workflows
|
|
376
|
+
- Max paths: Prevent exponential explosion (suggest 1000 paths max)
|
|
377
|
+
- Early pruning: Stop if path is obviously invalid
|
|
378
|
+
|
|
379
|
+
### Optimization Opportunities
|
|
380
|
+
|
|
381
|
+
1. **Path Pruning**: If path already missing required output, don't continue
|
|
382
|
+
2. **Caching**: Cache validation results for unchanged workflows
|
|
383
|
+
3. **Incremental**: Only validate changed paths (future enhancement)
|
|
384
|
+
|
|
385
|
+
---
|
|
386
|
+
|
|
387
|
+
## Open Questions
|
|
388
|
+
|
|
389
|
+
1. **Abstain Reason**: How is abstain reason defined/stored?
|
|
390
|
+
2. **Category Hierarchy**: What are the exact hierarchy rules?
|
|
391
|
+
3. **Custom Validators**: What persona types have custom validators?
|
|
392
|
+
4. **Error Messages**: What's the exact Go error message format?
|
|
393
|
+
|
|
394
|
+
---
|
|
395
|
+
|
|
396
|
+
**Status**: ✅ Contract defined
|
|
397
|
+
**Next**: Analyze Go code for missing details, then implement TypeScript validator
|
|
@@ -0,0 +1,326 @@
|
|
|
1
|
+
# Validation Error Format Specification
|
|
2
|
+
|
|
3
|
+
**Date**: 2026-01-27
|
|
4
|
+
**Purpose**: Define enhanced error message format for TypeScript validator
|
|
5
|
+
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## Design Principles
|
|
9
|
+
|
|
10
|
+
1. **Actionable**: Users must know what to fix
|
|
11
|
+
2. **Clear**: Explain what's wrong and why
|
|
12
|
+
3. **Specific**: Point to exact location (node, edge, path)
|
|
13
|
+
4. **Helpful**: Provide remediation steps
|
|
14
|
+
5. **Referenceable**: Link to rule documentation
|
|
15
|
+
|
|
16
|
+
---
|
|
17
|
+
|
|
18
|
+
## Error Response Structure
|
|
19
|
+
|
|
20
|
+
### TypeScript Interface
|
|
21
|
+
|
|
22
|
+
```typescript
|
|
23
|
+
export interface ValidationError {
|
|
24
|
+
// Identification
|
|
25
|
+
type: ValidationErrorType;
|
|
26
|
+
rule_id: string;
|
|
27
|
+
severity: "critical" | "warning" | "info";
|
|
28
|
+
|
|
29
|
+
// Location
|
|
30
|
+
location: {
|
|
31
|
+
node_id?: string;
|
|
32
|
+
edge_id?: string;
|
|
33
|
+
path?: string[]; // Execution path: ["trigger", "categorizer", "billing_branch", "search"]
|
|
34
|
+
category?: string; // Category name if categorizer-related
|
|
35
|
+
named_result?: string; // Named result name if applicable
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
// Explanation (Enhanced)
|
|
39
|
+
what: string; // What is wrong (specific)
|
|
40
|
+
why: string; // Why it's wrong (explanation)
|
|
41
|
+
how_to_fix: string; // How to fix (actionable steps)
|
|
42
|
+
|
|
43
|
+
// Reference
|
|
44
|
+
rule_reference: string; // URI to rule: "ema://validation/rules#required_output_all_paths"
|
|
45
|
+
|
|
46
|
+
// Examples (Optional)
|
|
47
|
+
examples?: {
|
|
48
|
+
bad: string; // Bad pattern example
|
|
49
|
+
good: string; // Good pattern example
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export interface ValidationResult {
|
|
54
|
+
valid: boolean;
|
|
55
|
+
errors: ValidationError[];
|
|
56
|
+
warnings: ValidationError[];
|
|
57
|
+
info: ValidationError[];
|
|
58
|
+
summary: {
|
|
59
|
+
total_paths: number;
|
|
60
|
+
valid_paths: number;
|
|
61
|
+
invalid_paths: number;
|
|
62
|
+
errors_by_type: Record<ValidationErrorType, number>;
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export type ValidationErrorType =
|
|
67
|
+
| "required_output_not_produced"
|
|
68
|
+
| "multiple_writers"
|
|
69
|
+
| "missing_response_or_abstain_reason"
|
|
70
|
+
| "category_hierarchy_violation";
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
---
|
|
74
|
+
|
|
75
|
+
## Error Type Specifications
|
|
76
|
+
|
|
77
|
+
### 1. required_output_not_produced
|
|
78
|
+
|
|
79
|
+
**Rule ID**: `required_output_all_paths`
|
|
80
|
+
|
|
81
|
+
**What**:
|
|
82
|
+
```
|
|
83
|
+
"Path '{path_name}' does not produce required output '{named_result}'"
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
**Why**:
|
|
87
|
+
```
|
|
88
|
+
"All execution paths from trigger must produce all named results. The path '{path_name}' ends at '{last_node}' without producing '{named_result}'."
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
**How to Fix**:
|
|
92
|
+
```
|
|
93
|
+
"Add a node that produces '{named_result}' to the '{path_name}' path. Connect the node's '{output_name}' output to WORKFLOW_OUTPUT via resultMappings."
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
**Example**:
|
|
97
|
+
```json
|
|
98
|
+
{
|
|
99
|
+
"type": "required_output_not_produced",
|
|
100
|
+
"rule_id": "required_output_all_paths",
|
|
101
|
+
"severity": "critical",
|
|
102
|
+
"location": {
|
|
103
|
+
"path": ["trigger", "categorizer", "billing_branch", "search"],
|
|
104
|
+
"category": "Billing",
|
|
105
|
+
"named_result": "response_with_sources"
|
|
106
|
+
},
|
|
107
|
+
"what": "Path 'Billing' does not produce required output 'response_with_sources'",
|
|
108
|
+
"why": "All execution paths from trigger must produce all named results. The path 'Billing' ends at 'search' node without producing 'response_with_sources'.",
|
|
109
|
+
"how_to_fix": "Add a response node (respond_with_sources, call_llm, or fixed_response) after the 'search' node in the 'Billing' branch. Connect the response node's output to WORKFLOW_OUTPUT via resultMappings.",
|
|
110
|
+
"rule_reference": "ema://validation/rules#required_output_all_paths",
|
|
111
|
+
"examples": {
|
|
112
|
+
"bad": "trigger → categorizer → [Billing] → search → (no response)",
|
|
113
|
+
"good": "trigger → categorizer → [Billing] → search → respond_with_sources → WORKFLOW_OUTPUT"
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
---
|
|
119
|
+
|
|
120
|
+
### 2. multiple_writers
|
|
121
|
+
|
|
122
|
+
**Rule ID**: `single_writer_per_output`
|
|
123
|
+
|
|
124
|
+
**What**:
|
|
125
|
+
```
|
|
126
|
+
"Multiple nodes produce '{named_result}' on path '{path_name}'"
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
**Why**:
|
|
130
|
+
```
|
|
131
|
+
"A single named result can only have one producer per execution path. The path '{path_name}' has both '{node1}' and '{node2}' producing '{named_result}'."
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
**How to Fix**:
|
|
135
|
+
```
|
|
136
|
+
"Ensure only one node produces '{named_result}' on the '{path_name}' path. Either remove one of the producers or use different named results for each node."
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
**Example**:
|
|
140
|
+
```json
|
|
141
|
+
{
|
|
142
|
+
"type": "multiple_writers",
|
|
143
|
+
"rule_id": "single_writer_per_output",
|
|
144
|
+
"severity": "critical",
|
|
145
|
+
"location": {
|
|
146
|
+
"path": ["trigger", "categorizer", "billing_branch"],
|
|
147
|
+
"category": "Billing",
|
|
148
|
+
"named_result": "response_with_sources"
|
|
149
|
+
},
|
|
150
|
+
"what": "Multiple nodes produce 'response_with_sources' on path 'Billing'",
|
|
151
|
+
"why": "A single named result can only have one producer per execution path. The path 'Billing' has both 'respond_billing' and 'respond_general' producing 'response_with_sources'.",
|
|
152
|
+
"how_to_fix": "Ensure only one node produces 'response_with_sources' on the 'Billing' path. Either remove 'respond_general' or use a different named result for it.",
|
|
153
|
+
"rule_reference": "ema://validation/rules#single_writer_per_output"
|
|
154
|
+
}
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
---
|
|
158
|
+
|
|
159
|
+
### 3. missing_response_or_abstain_reason
|
|
160
|
+
|
|
161
|
+
**Rule ID**: `response_or_abstain_required`
|
|
162
|
+
|
|
163
|
+
**What**:
|
|
164
|
+
```
|
|
165
|
+
"Path '{path_name}' does not produce a response and has no abstain reason"
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
**Why**:
|
|
169
|
+
```
|
|
170
|
+
"Every execution path must either produce a user-visible response or explicitly abstain with a reason. The path '{path_name}' ends without a response node and no abstain reason is specified."
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
**How to Fix**:
|
|
174
|
+
```
|
|
175
|
+
"Either add a response node (respond_with_sources, call_llm, or fixed_response) to the '{path_name}' path, or add an abstain reason explaining why this path doesn't respond."
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
**Example**:
|
|
179
|
+
```json
|
|
180
|
+
{
|
|
181
|
+
"type": "missing_response_or_abstain_reason",
|
|
182
|
+
"rule_id": "response_or_abstain_required",
|
|
183
|
+
"severity": "critical",
|
|
184
|
+
"location": {
|
|
185
|
+
"path": ["trigger", "categorizer", "billing_branch", "search"],
|
|
186
|
+
"category": "Billing"
|
|
187
|
+
},
|
|
188
|
+
"what": "Path 'Billing' does not produce a response and has no abstain reason",
|
|
189
|
+
"why": "Every execution path must either produce a user-visible response or explicitly abstain with a reason. The path 'Billing' ends at 'search' node without a response node and no abstain reason is specified.",
|
|
190
|
+
"how_to_fix": "Either add a response node (respond_with_sources, call_llm, or fixed_response) after 'search' in the 'Billing' branch, or add an abstain reason to the workflow configuration.",
|
|
191
|
+
"rule_reference": "ema://validation/rules#response_or_abstain_required"
|
|
192
|
+
}
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
---
|
|
196
|
+
|
|
197
|
+
### 4. category_hierarchy_violation
|
|
198
|
+
|
|
199
|
+
**Rule ID**: `category_hierarchy_valid`
|
|
200
|
+
|
|
201
|
+
**What**:
|
|
202
|
+
```
|
|
203
|
+
"Category hierarchy violation in categorizer '{categorizer_name}'"
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
**Why**:
|
|
207
|
+
```
|
|
208
|
+
TBD - Need to analyze category hierarchy rules from Go validator
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
**How to Fix**:
|
|
212
|
+
```
|
|
213
|
+
TBD - Need to analyze category hierarchy rules
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
**Example**:
|
|
217
|
+
```json
|
|
218
|
+
{
|
|
219
|
+
"type": "category_hierarchy_violation",
|
|
220
|
+
"rule_id": "category_hierarchy_valid",
|
|
221
|
+
"severity": "critical",
|
|
222
|
+
"location": {
|
|
223
|
+
"node_id": "chat_categorizer",
|
|
224
|
+
"category": "Billing"
|
|
225
|
+
},
|
|
226
|
+
"what": "Category hierarchy violation in categorizer 'chat_categorizer'",
|
|
227
|
+
"why": "TBD",
|
|
228
|
+
"how_to_fix": "TBD",
|
|
229
|
+
"rule_reference": "ema://validation/rules#category_hierarchy_valid"
|
|
230
|
+
}
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
---
|
|
234
|
+
|
|
235
|
+
## Path Format
|
|
236
|
+
|
|
237
|
+
### Path Representation
|
|
238
|
+
|
|
239
|
+
Paths are represented as arrays of node IDs in execution order:
|
|
240
|
+
|
|
241
|
+
```typescript
|
|
242
|
+
path: ["trigger", "categorizer", "billing_branch", "search", "respond_with_sources"]
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
### Path Naming
|
|
246
|
+
|
|
247
|
+
For branching paths, use category/value name:
|
|
248
|
+
```
|
|
249
|
+
"Billing" // From categorizer, value="Billing"
|
|
250
|
+
"Fallback" // From categorizer, value="Fallback"
|
|
251
|
+
"true" // From boolean output, value=true
|
|
252
|
+
```
|
|
253
|
+
|
|
254
|
+
For non-branching paths, use last action name:
|
|
255
|
+
```
|
|
256
|
+
"respond_with_sources" // Last action in path
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
---
|
|
260
|
+
|
|
261
|
+
## Rule Reference Format
|
|
262
|
+
|
|
263
|
+
### URI Structure
|
|
264
|
+
|
|
265
|
+
```
|
|
266
|
+
ema://validation/rules#{rule_id}
|
|
267
|
+
```
|
|
268
|
+
|
|
269
|
+
### Examples
|
|
270
|
+
|
|
271
|
+
- `ema://validation/rules#required_output_all_paths`
|
|
272
|
+
- `ema://validation/rules#single_writer_per_output`
|
|
273
|
+
- `ema://validation/rules#response_or_abstain_required`
|
|
274
|
+
- `ema://validation/rules#category_hierarchy_valid`
|
|
275
|
+
|
|
276
|
+
---
|
|
277
|
+
|
|
278
|
+
## Error Message Templates
|
|
279
|
+
|
|
280
|
+
### Template Variables
|
|
281
|
+
|
|
282
|
+
- `{path_name}` - Human-readable path name
|
|
283
|
+
- `{named_result}` - Named result name
|
|
284
|
+
- `{node_id}` - Node identifier
|
|
285
|
+
- `{last_node}` - Last node in path
|
|
286
|
+
- `{output_name}` - Output name
|
|
287
|
+
- `{category}` - Category name
|
|
288
|
+
|
|
289
|
+
### Template Functions
|
|
290
|
+
|
|
291
|
+
- `formatPath(path: string[]): string` - Format path array to readable string
|
|
292
|
+
- `formatNode(nodeId: string): string` - Format node ID to display name
|
|
293
|
+
- `formatCategory(category: string): string` - Format category name
|
|
294
|
+
|
|
295
|
+
---
|
|
296
|
+
|
|
297
|
+
## Compatibility with Go Validator
|
|
298
|
+
|
|
299
|
+
### Must Match
|
|
300
|
+
|
|
301
|
+
- Error types (4 types)
|
|
302
|
+
- Path identification format
|
|
303
|
+
- Validation logic
|
|
304
|
+
|
|
305
|
+
### Can Enhance
|
|
306
|
+
|
|
307
|
+
- Error message format (what/why/how)
|
|
308
|
+
- Additional context (examples, references)
|
|
309
|
+
- Severity levels (critical/warning/info)
|
|
310
|
+
|
|
311
|
+
---
|
|
312
|
+
|
|
313
|
+
## Implementation Checklist
|
|
314
|
+
|
|
315
|
+
- [ ] Define TypeScript interfaces
|
|
316
|
+
- [ ] Create error message generators for each error type
|
|
317
|
+
- [ ] Implement path formatting utilities
|
|
318
|
+
- [ ] Add rule reference generation
|
|
319
|
+
- [ ] Create example generators (good vs bad)
|
|
320
|
+
- [ ] Test error message clarity
|
|
321
|
+
- [ ] Validate against Go validator error format
|
|
322
|
+
|
|
323
|
+
---
|
|
324
|
+
|
|
325
|
+
**Status**: ✅ Format designed
|
|
326
|
+
**Next**: Implement error message generators
|