Output Contract Enforcer
Validates that a DAG node's output matches its declared JSON schema before passing to downstream nodes. The glue that makes multi-agent DAGs reliable. Without this, downstream nodes receive unpredictable input and the DAG breaks.
When to Use
✅ Use for:
- Validating a node's output against its declared schema
- Generating output schemas from natural-language output descriptions
- Debugging why a downstream node rejected its input
- Ensuring contract compatibility between connected nodes
❌ NOT for:
- Assessing content quality or correctness (use
dag-quality) - Grading skill quality (use
skill-grader) - General JSON schema work outside DAG context
Validation Process
flowchart TD
O[Node output] --> P[Parse as JSON]
P -->|Parse error| E1[FAIL: Not valid JSON]
P -->|Valid JSON| S[Check against schema]
S --> R{Required fields?}
R -->|Missing| E2[FAIL: Missing required field X]
R -->|Present| T{Type check?}
T -->|Wrong type| E3[FAIL: Field X expected string, got number]
T -->|Correct| C{Constraints?}
C -->|Violated| E4[FAIL: Field X violates constraint Y]
C -->|Met| V[PASS: Contract satisfied]
What Gets Checked
| Check | Example | Failure Message |
|---|---|---|
| JSON parseable | {broken json | "Output is not valid JSON" |
| Required fields | status missing | "Missing required field: status" |
| Field types | status: 42 (expected string) | "Field 'status' expected string, got number" |
| Enum values | status: "maybe" | "Field 'status' must be one of: pass, warn, fail" |
| String constraints | summary: "" (minLength: 1) | "Field 'summary' must have minLength 1" |
| Number constraints | score: 1.5 (maximum: 1.0) | "Field 'score' must be ≤ 1.0" |
| Array constraints | items: [] (minItems: 1) | "Field 'items' must have at least 1 item" |
| Nested objects | Missing sub-field | "Field 'metadata.cost' is required" |
The Standard Output Contract
Every DAG node should produce output matching this base schema (fields can be extended):
{
"type": "object",
"required": ["status", "summary"],
"properties": {
"status": {
"type": "string",
"enum": ["pass", "warn", "fail"]
},
"summary": {
"type": "string",
"minLength": 1,
"description": "1-3 sentence description of what was produced"
},
"artifacts": {
"type": "array",
"items": { "type": "string" },
"description": "List of files created or modified"
},
"data": {
"type": "object",
"description": "Node-specific output data (schema varies per node)"
},
"risks": {
"type": "array",
"items": { "type": "string" },
"description": "Remaining risks or assumptions"
}
}
}
Contract Compatibility Check
When connecting Node A's output to Node B's input, verify:
- Every field B requires is present in A's output schema
- Types match (A produces string, B expects string)
- A's output constraints are compatible with B's input constraints
- If A produces optional fields that B requires → incompatible
Node A output: { status: string, recommendations: string[] }
Node B input: { status: string, recommendations: string[], priority: number }
Result: INCOMPATIBLE — Node B requires 'priority' but Node A doesn't produce it.
Fix: Add 'priority' to Node A's output, or add a transformer node between A and B.
Schema Generation
When a node description says "produces a list of recommendations with priorities," generate:
{
"type": "object",
"required": ["status", "summary", "data"],
"properties": {
"status": { "type": "string", "enum": ["pass", "warn", "fail"] },
"summary": { "type": "string", "minLength": 1 },
"data": {
"type": "object",
"required": ["recommendations"],
"properties": {
"recommendations": {
"type": "array",
"minItems": 1,
"items": {
"type": "object",
"required": ["text", "priority"],
"properties": {
"text": { "type": "string" },
"priority": { "type": "integer", "minimum": 1, "maximum": 5 }
}
}
}
}
}
}
}
Anti-Patterns
No Contract at All
Wrong: Nodes produce free-form text with no schema. Why: Downstream nodes can't reliably parse the input. The DAG is fragile. Right: Every node declares its output schema. Every output is validated before passing downstream.
Overly Strict Contracts
Wrong: Requiring exact character counts, specific formatting, or field values that depend on runtime context. Right: Constrain structure (types, required fields), not content. Let dag-quality handle content assessment.
Ignoring Optional Fields
Wrong: Treating all fields as required.
Right: Use required only for fields that downstream nodes absolutely need. Mark everything else as optional.