core/loop_contracts.py (merged in #473) declared foreach a supported alias of for_each, on the strength of a comment rather than a run. It is not one.
Two shapes, run through the hermetic Python API:
- id: loop
foreach: "['A','B']"
steps:
- id: w
tool: filesystem
action: write
parameters: {path: "out/{{ index }}.txt", content: "{{ item }}"}
→ YAMLCompilerError: Failed to compile pipeline: Schema validation failed: 325 errors
The same file with for_each: writes 0.txt and 1.txt containing A and B.
In a shape that does compile, the body runs once and $item/$index resolve to None — the loop never expands.
Where the spelling does appear:
engine/pipeline_spec.py:72 converts a foreach field into a LoopSpec, for the declarative engine only.
compiler/control_flow_compiler.py:119 — the principal compiler — branches on for_each only, and _create_for_each_task reads only that spelling.
So the two engines disagree, and the validator was siding with the one that does not run the pipeline. #479 removes foreach from the loop contracts so validation stops reporting it as a working construct.
The remaining decision is whether foreach should be implemented in the control-flow compiler or rejected at schema level. It is currently neither: it parses and silently does nothing. Catalogue files that use it: examples/migration_showcase.yaml, examples/web_research_pipeline.yaml, examples/advanced/parallel_processing.yaml — all currently failing validation for other reasons too.
core/loop_contracts.py(merged in #473) declaredforeacha supported alias offor_each, on the strength of a comment rather than a run. It is not one.Two shapes, run through the hermetic Python API:
→
YAMLCompilerError: Failed to compile pipeline: Schema validation failed: 325 errorsThe same file with
for_each:writes0.txtand1.txtcontainingAandB.In a shape that does compile, the body runs once and
$item/$indexresolve toNone— the loop never expands.Where the spelling does appear:
engine/pipeline_spec.py:72converts aforeachfield into aLoopSpec, for the declarative engine only.compiler/control_flow_compiler.py:119— the principal compiler — branches onfor_eachonly, and_create_for_each_taskreads only that spelling.So the two engines disagree, and the validator was siding with the one that does not run the pipeline. #479 removes
foreachfrom the loop contracts so validation stops reporting it as a working construct.The remaining decision is whether
foreachshould be implemented in the control-flow compiler or rejected at schema level. It is currently neither: it parses and silently does nothing. Catalogue files that use it:examples/migration_showcase.yaml,examples/web_research_pipeline.yaml,examples/advanced/parallel_processing.yaml— all currently failing validation for other reasons too.