-
Notifications
You must be signed in to change notification settings - Fork 0
Description
Summary
wfctl validate and wfctl template validate check structural correctness (valid YAML, known module/step/trigger types, step name existence) but do not validate field-level references across the data flow. When a DB column is renamed, a step output field changes, or a cross-file import breaks, there is no static check that catches it — the error only surfaces at runtime.
What is validated today
| Check | Status |
|---|---|
| Valid YAML syntax | ✅ |
| Known module types | ✅ |
| Known step types | ✅ |
| Known trigger types | ✅ |
Step name references (steps.X exists) |
✅ |
| Forward references (step references later step) | ✅ |
| Self-references (step references itself) | ✅ |
What is NOT validated
| Check | Status | Example |
|---|---|---|
| Step output field names | ❌ | steps.query.row.nonexistent_column |
| DB column alignment | ❌ | SQL returns auth_token but template reads token |
| Step type output schema | ❌ | step.db_query mode=single produces {row, found} but not checked |
| Cross-file import targets | ❌ | Imported YAML may reference pipelines/steps that don't exist |
| Conditional field paths | ❌ | field: "steps.query.found" in step.conditional — found not validated |
secret_from paths |
❌ | secret_from: steps.load_integration.row.auth_token — not validated |
backend_url_key paths |
❌ | backend_url_key: "row.backend_url" — not validated |
Real-world example
From a production pipeline (messaging.yaml):
- name: load_integration
type: step.db_query
config:
query: >
SELECT provider_config->>'auth_token' AS auth_token, affiliate_id
FROM messaging_integrations WHERE id = $1
mode: single
- name: verify
type: step.webhook_verify
config:
secret_from: steps.load_integration.row.auth_token # references SQL alias
- name: upsert_texter
type: step.db_exec
config:
query: >
INSERT INTO messaging_texters_{{.steps.load_integration.row.affiliate_id}}If someone renames the SQL alias auth_token → token or affiliate_id → tenant_id:
wfctl validatepasses ✅wfctl template validatepasses ✅ (only checks thatload_integrationstep exists)- Pipeline fails at runtime with empty/wrong values
Proposed approach
1. Step type output schema registry
Each built-in step type could declare its output schema:
// step.db_query output schema depends on mode
func (s *DBQueryStep) OutputSchema(config map[string]any) map[string]FieldType {
mode := config["mode"]
switch mode {
case "single":
return map[string]FieldType{"row": Object, "found": Bool}
case "list":
return map[string]FieldType{"rows": Array, "count": Int}
}
}2. Deep template reference validation
Extend validateStepRef to walk the full dot-path:
steps.auth→ check step exists ✅ (already done)steps.auth.affiliate_id→ checkauthstep type's output schema foraffiliate_idsteps.query.row.column_name→ checkquerystep type's output schema forrow, warn thatcolumn_namecannot be statically verified (depends on SQL)
3. SQL alias extraction (best-effort)
For step.db_query, parse the SQL SELECT clause to extract column aliases, then validate references against them. This would be best-effort (can't handle SELECT * or dynamic SQL) but would catch the most common case.
4. Cross-file import validation
When processing imports:, verify that all step names and pipeline names referenced in templates actually exist in the merged config.
References
- Current validation:
cmd/wfctl/validate.go:117-150 - Template validation:
cmd/wfctl/template_validate.go:568-646(step name only) - Step ref regex:
cmd/wfctl/template_validate.go:569— stops at step name, ignores field path - Pipeline context types:
interfaces/pipeline.go:46-60—map[string]anythroughout - Related: Template missingkey=zero silently swallows field-level typos in step references #367 (missingkey=zero silently swallows typos at runtime)