The runtime accepts {{ $item }}; validation calls it a syntax error
UnifiedTemplateResolver._preprocess_dollar_variables (unified_template_resolver.py:545) rewrites {{ $variable }} to {{ variable }} before rendering, because "Jinja2 doesn't support variables starting with $". It does the same inside {% %} blocks.
TemplateValidator._validate_syntax parses the raw text, so it reports:
for a template the runtime handles correctly.
Reproduction
TemplateValidator().validate_pipeline_templates(
{"id": "p", "steps": [{"id": "s", "for_each": "{{ rows }}",
"parameters": {"t": "{{ $item }}"}}]},
{"rows": [1]},
)
# -> is_valid=False, ['syntax_error']
Scope
32 uses across the catalogue — $item (18), $iteration (10), $index (2), $is_first (2) — in files including:
examples/control_flow_for_loop.yaml
examples/iterative_fact_checker.yaml
examples/control_flow_advanced.yaml
examples/enhanced_until_conditions_demo.yaml
examples/original_research_report_pipeline.yaml
All of these currently fail validation, and this is at least one of the reasons.
The compiler/runtime accepts a spelling the validator does not. Each time, the validator's private notion of the language disagreed with the runtime's:
| issue |
disagreement |
| #465 |
inferred dependencies rejected rather than used |
| #469 |
bare loop names unknown to the template validator |
| #472 |
one binding set for every loop construct; source validated in loop scope |
| this |
$-prefixed spelling rejected as a syntax error |
Fix
Validation should apply the same preprocessing the runtime applies, from a shared implementation rather than a second regex — a reimplementation would be the fourth mechanism problem #466 removed for dependencies.
Once {{ $item }} is normalised to {{ item }} before parsing, the DOLLAR_LOOP_VARIABLES raw-text scan in _validate_variables becomes redundant: the names appear in the AST like any other. That scan exists only because $item cannot be parsed.
Tests
{{ $item }} inside a for_each validates and renders to the item
{{ $item }} outside a loop is still rejected, as {{ item }} is
$-names in {% if %} / {% for %} blocks are handled
- the preprocessing has one implementation, asserted by identity
- a pipeline using
$item compiles and runs, producing the expected artifact
- the affected catalogue examples move into the validating baseline
The runtime accepts
{{ $item }}; validation calls it a syntax errorUnifiedTemplateResolver._preprocess_dollar_variables(unified_template_resolver.py:545) rewrites{{ $variable }}to{{ variable }}before rendering, because "Jinja2 doesn't support variables starting with $". It does the same inside{% %}blocks.TemplateValidator._validate_syntaxparses the raw text, so it reports:for a template the runtime handles correctly.
Reproduction
Scope
32 uses across the catalogue —
$item(18),$iteration(10),$index(2),$is_first(2) — in files including:examples/control_flow_for_loop.yamlexamples/iterative_fact_checker.yamlexamples/control_flow_advanced.yamlexamples/enhanced_until_conditions_demo.yamlexamples/original_research_report_pipeline.yamlAll of these currently fail validation, and this is at least one of the reasons.
Same class as #465, #469 and #472
The compiler/runtime accepts a spelling the validator does not. Each time, the validator's private notion of the language disagreed with the runtime's:
$-prefixed spelling rejected as a syntax errorFix
Validation should apply the same preprocessing the runtime applies, from a shared implementation rather than a second regex — a reimplementation would be the fourth mechanism problem #466 removed for dependencies.
Once
{{ $item }}is normalised to{{ item }}before parsing, theDOLLAR_LOOP_VARIABLESraw-text scan in_validate_variablesbecomes redundant: the names appear in the AST like any other. That scan exists only because$itemcannot be parsed.Tests
{{ $item }}inside afor_eachvalidates and renders to the item{{ $item }}outside a loop is still rejected, as{{ item }}is$-names in{% if %}/{% for %}blocks are handled$itemcompiles and runs, producing the expected artifact