fix: fail loudly when a fan-out 'items' expression does not resolve to a list (#2957)

A non-list result from the items expression is a wiring error (the
template did not resolve to a collection); silently fanning out over
zero items hides it until a confusing downstream failure. Fail the
step with an error naming the expression instead. An explicit empty
list remains valid input.

Fixes #2956
This commit is contained in:
Huy Do
2026-06-17 03:33:11 +07:00
committed by GitHub
parent f20e8ee6f7
commit 36fd5f6f49
2 changed files with 38 additions and 6 deletions

View File

@@ -1475,9 +1475,9 @@ class TestFanOutStep:
assert result.output["item_count"] == 2
assert result.output["max_concurrency"] == 3
def test_execute_non_list_items_resolves_empty(self):
def test_execute_non_list_items_fails_loudly(self):
from specify_cli.workflows.steps.fan_out import FanOutStep
from specify_cli.workflows.base import StepContext
from specify_cli.workflows.base import StepContext, StepStatus
step = FanOutStep()
ctx = StepContext()
@@ -1487,8 +1487,24 @@ class TestFanOutStep:
"step": {"id": "impl", "command": "speckit.implement"},
}
result = step.execute(config, ctx)
assert result.status == StepStatus.FAILED
assert "'items' must resolve to a list" in (result.error or "")
assert result.output["item_count"] == 0
def test_execute_empty_list_items_is_valid(self):
from specify_cli.workflows.steps.fan_out import FanOutStep
from specify_cli.workflows.base import StepContext, StepStatus
step = FanOutStep()
ctx = StepContext(steps={"tasks": {"output": {"task_list": []}}})
config = {
"id": "parallel",
"items": "{{ steps.tasks.output.task_list }}",
"step": {"id": "impl", "command": "speckit.implement"},
}
result = step.execute(config, ctx)
assert result.status == StepStatus.COMPLETED
assert result.output["item_count"] == 0
assert result.output["items"] == []
def test_validate_missing_fields(self):
from specify_cli.workflows.steps.fan_out import FanOutStep