ContextLab / ContextLab/orchestrator
Validation rejects {{ $item }} as a syntax error; the runtime rewrites it
- Dominant language
- Python
- Stars
- 3
- Forks
- 2
- Avg merge
- 13m
- Merged PRs (30d)
- 1
Description
## The runtime accepts `{{ $item }}`; validation calls it a syntax error
`UnifiedTemplateResolver._preprocess_dollar_variables` ([unified_template_resolver.py:545](src/orchestrator/core/unified_template_resolver.py#L545)) 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:
```
syntax_error
```
for a template the runtime handles correctly.
## Reproduction
```python
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.
## 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:
| 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
Contributor guide
Assessment
This issue has not been assessed yet.