Schema Validation
JSON Schema validation and custom rules for workflow correctness.
AutoFlow validates all generated workflows against two JSON schemas plus custom structural rules implemented in src/autoflow/schemas/validator.py.
Workflow Schema
schemas/asksage_workflow/schema.json defines the structure of Agent Builder workflows:
Node Types
8 supported node types:
| Type | Purpose |
|---|---|
llm | Call an LLM with system prompt, user prompt template, temperature, and model |
read_file | Read a file and expose its content as {{read_file.content}} |
loop | Iterate over a list, exposing {{loop.item}} and collecting {{looped.items}} |
if_else | Binary conditional routing (true/false or if/else handles) |
variable_assignment | Set a variable to a static or computed value |
flat_response | Return a plain text response (no LLM call) |
decision_tree | Multi-way routing based on categories (dynamic sourceHandle names) |
variable_transform | Transform data with expression-based operations |
Edge Rules
sourceHandlevalues:"out","true"/"false","if"/"else","loop_out"/"complete","pass"/"fail", or dynamic category names fromdecision_treetargetHandlevalues:"in"or"loop_in"- Every edge must reference existing node IDs
Variable Patterns
Workflows use double-brace variable references:
| Pattern | Meaning |
|---|---|
{{input.message}} | The user's input message |
{{node_id.response}} | Output from an LLM node |
{{loop.item}} | Current item in a loop iteration |
{{looped.items}} | Collected results after loop completes |
{{read_file.content}} | Content from a read_file node |
{{node_id.category}} | Category output from a decision_tree |
{{node_id.confidence}} | Confidence score from a decision_tree |
{{node_id.value}} | Value from a variable_transform |
Identity Model (v1 vs v2)
AutoFlow supports two workflow identity formats. The validator auto-detects the version using detect_workflow_version():
v1 Format (Legacy)
Nodes use snake_case string IDs. Edges reference nodes by these string IDs.
{
"id": "intake",
"type": "llm",
"label": "Intake",
"position": { "x": 0, "y": 0 },
"config": { ... }
}v2 Format (Current)
Nodes use a three-layer identity model: UUID id (execution wiring), slug (variable references), and label (display). Edges wire by UUID. All nodes include a key field (default "").
{
"id": "862ffdc5-f240-535b-ba94-05807abb7952",
"slug": "extractor",
"key": "",
"type": "llm",
"label": "Extractor",
"position": { "x": 0, "y": 0 },
"config": { ... }
}| Layer | Field | Format | Purpose |
|---|---|---|---|
| Execution | id | UUID (v4 or v5) | Edge wiring, Ask Sage internal |
| Reference | slug | snake_case | Variable references ({{slug.response}}) |
| Display | label | Free text | Human-readable name in the UI |
Variable references use slugs, not UUIDs: {{extractor.response}}, not {{862ffdc5-....response}}.
Custom Validation Rules
v1 Rules
- Unique snake_case node IDs — Every
idmust be unique and usesnake_caseformatting - Valid edge references — All
sourceandtargetin edges must reference existing node IDs - No orphan nodes — Every node must be connected by at least one edge (except the entry node)
- Position bounds — Node positions must be within -10000 to 10000 on both axes
- Variable resolution — All
{{variable}}references must resolve to a node that appears earlier in the graph
v2 Rules (Superset)
All v1 structural rules apply, plus:
- UUID node IDs — Every node
idmust be a valid UUID - Slug required — Every node must have a
slugfield insnake_caseformat - Slug uniqueness — No two nodes may share the same slug
- UUID edge IDs — Every edge
id,source, andtargetmust be valid UUIDs - Edge references by UUID — Edge
source/targetmust match node UUIDs - Variable refs use slugs —
{{slug.response}}resolves against node slugs, not UUIDs
Semantic Validation (v2 Only)
Beyond structural checks, v2 workflows are validated for execution safety:
- Terminal node required — At least one
flat_responsenode or anllmnode withterminal_node: truein its config - Dangling handle detection —
if_elsemust have both branches wired (true/falseorif/else);decision_treemust have an edge for every category;loopmust have bothloop_outandcompletehandles wired - Mustache restriction —
if_elsecondition fields must not contain{{...}}variable syntax - Loop bounds —
loopnodes must havemax_iterationsset within allowed limits - Registry config validation — Each node's config is validated against the node registry's parameter definitions (required fields, types, ranges, enums)
Node Registry
The node registry (node_registry/asksage_nodes.yaml) is the single source of truth for all 8 node types. It defines each type's parameters, handles, and constraints. The validator uses the registry for config validation, and per-node JSON Schemas are auto-generated from it into schemas/nodes/.
Output Envelope Schema
schemas/universal_output_envelop/schema.json validates the wrapper around every pipeline output:
{
"objective": "string",
"deliverable_artifact": "object",
"quality_scores": {
"correctness": "integer (1-5)",
"completeness": "integer (1-5)",
"format_validity": "integer (1-5)",
"efficiency": "integer (1-5)",
"reusability": "integer (1-5)"
},
"confidence": "number (0-1)",
"assumptions": ["string"],
"constraints": ["string"],
"known_gaps": ["string"],
"estimated_human_edit_minutes": "integer",
"recommended_next_actions": ["string"],
"metadata": "object"
}Quality Rubric
The 5-point scoring scale used by the EvaluatorAgent:
| Dimension | What It Measures |
|---|---|
| Correctness | Does the workflow solve the stated objective? |
| Completeness | Are all necessary steps and edge cases covered? |
| Format Validity | Is the output valid Ask Sage Agent Builder JSON? |
| Efficiency | Minimal nodes, smart routing, no redundancy? |
| Reusability | Can the workflow be generalized for similar tasks? |