Flow validation paths
Triggers Studio uses two validation surfaces. They share the same flows.Validate() engine but load different dependency snapshots and sometimes different flow bodies.
Studio editor (live)
While you edit a flow, the Issues tab merges:
- Client rules - wiring, orphans, cycles (
validateGlobal) POST /api/v2/flows/validate-draft- debounced after graph changes
This path loads fresh manifests, sources, outputs, and event schemas from Mongo on every request (validation.LoadDeps). Release and save use the same validator against the version body before release normalization.
Manifest validation also checks action ownership. An action must be declared by its parent service segment's active manifest. To call an action from another service after a lookup or action, add that service's service segment before the action; selecting an action does not change its parent service.
Release is blocked only when this merged list has blocking issues. Warnings can be acknowledged and released anyway.
Flow library (broken_reasons)
The library Broken column reads broken_reasons from GET /api/v2/flows. That field is persisted on the flow document, not computed at list time.
It is updated when manifest drift runs: after a service registers a new active manifest hash, DriftRunner re-validates every released flow body that references that service and writes broken_reasons via MarkValid / MarkInvalid.
Drift validation uses the same flows.Validate() passes as the studio, including:
- Normalized released bodies - action identity lives in
config.action_ref(top-levelnameis stripped at release) - Fresh registry deps - active manifests, sources, outputs, and event schemas loaded per drift run
A successful release sets status: valid and clears broken_reasons.
When they disagree
Historically these could diverge when:
- Drift used startup-frozen manifests and omitted event schemas (false
condition_schema_unavailable, missing auto-matched inputs) ValidateManifestCompatread top-levelnameon action nodes while released bodies only hadconfig.action_ref- Release cleared
statusbut left stalebroken_reasonson the flow doc
The editor showed No issues while the library still showed a red count. After the drift fixes, drift and validate-draft should agree on the same released graph; re-release or the next manifest registration clears stale library badges.
Quick checks
API=http://localhost:3000/notify-api/api/v2
# Live draft validation (matches Issues tab)
curl -s -X POST "$API/flows/validate-draft" -H 'Content-Type: application/json' -d @draft.json | jq '.valid, .errors'
# Released version validation (full deps)
curl -s -X POST "$API/flows/<flow_id>/validate" | jq '.valid, .errors'
# Persisted drift output (library badge)
curl -s "$API/flows" | jq '.[] | {id, status, broken_reasons}'
If validate-draft and /:id/validate are valid but broken_reasons is non-empty, the library field is stale - re-release or wait for the next manifest drift pass after services re-register.