Expand description
Static spec validator. Port of platform/validator.py.
Runs before compilation. A spec that validates is guaranteed to compile to a
well-formed workflow under the chassis safety invariants. Each violation
carries a rule_id so spec authors and tests can pinpoint the failure.
Ported rules (structural + safety, all decidable from the spec + node-type prefixes, no business nodes required):
- R1 — each branch is a DAG (no cycles)
- R4 — each branch has ≥1 ingress
- R5 — no edge crosses a branch boundary
- R11 — a
map.*fan-out reachingexecute.*must declare a STATIC bound withinMAX_MAP_FANOUT, and a product-declared side-effect guard must dominate themap.*node itself - R9 — fan-in allowed; structural fan-out forbidden for every node type
except
map.*, which exists to fan out (non-sink out-degree exactly 1) - R1a — every non-ingress node is reachable from some ingress
- R3a — side-effect guard config cannot reference
$event.* - R2 — every
execute.*is dominated by a product-declared side-effect guard. Dominance, not reachability: EVERY path from an entry to the execute must carry a guard. - R4’ — no fan-out-capable node upstream of
execute.* - R7 — taint (
transform.ask_llm/ LLM-backedtool_invoke) cannot reach a side-effect guard - R3 — registered per-node config schemas are checked before compile
- R6 — explicitly cross-branch state cannot feed execution decisions
Structs§
- Violation
- A single rule violation.
Functions§
- validate
- Validate a spec against the chassis rules. Returns every violation found (not just the first) so authors can fix in one pass.