@@ -235,7 +235,7 @@ class _Branch:
235235 label : str
236236 sink : list [ToolResult ] = field (default_factory = list )
237237 # Typed multi-model output: when the task declares an ``outputs`` schema,
238- # each branch's result is validated and the coerced value stored here
238+ # each branch's result is validated and the value stored here
239239 # (``None`` when the branch failed or violated the schema). ``output_set``
240240 # marks that validation ran so fan-in uses this value instead of decoding
241241 # the raw sink.
@@ -250,7 +250,7 @@ def _aggregate_fanin(branches: list[_Branch]) -> list[dict[str, Any]]:
250250 "result": <value>}``. ``result`` is the decoded final tool result of the
251251 branch (its raw text when not JSON, or ``None`` when the branch produced no
252252 tool result). When the task declares an ``outputs`` schema, ``result`` is
253- the per-branch validated/coerced value instead (``None`` for a branch that
253+ the per-branch validated value instead (``None`` for a branch that
254254 failed or violated the schema). Used to expose multi-model / cross-product
255255 outputs to later tasks as ``outputs.<id>``.
256256 """
@@ -375,7 +375,7 @@ def _capture_task_output(
375375 """Capture a task's final tool result as a named typed output.
376376
377377 The task's output is its most recent tool result. When *schema* is
378- non-empty the decoded value is validated/coerced against it (raising a
378+ non-empty the decoded value is validated against it (raising a
379379 clear error on mismatch); otherwise the decoded value is stored as-is,
380380 falling back to the raw text when it is not JSON. The result is exposed to
381381 later tasks as ``outputs.<output_id>``.
@@ -501,7 +501,8 @@ async def _build_prompts_to_run(
501501
502502 Raises:
503503 IndexError: If the legacy path has no previous tool result.
504- ValueError: If the derived value is not valid JSON or not iterable.
504+ ValueError: If the legacy path's tool result is not valid JSON.
505+ TypeError: If the derived value is not iterable.
505506 """
506507 outputs = outputs or {}
507508 prompts_to_run : list [str ] = []
@@ -530,17 +531,22 @@ async def _build_prompts_to_run(
530531 raise IndexError ("No last tool result available for repeat_prompt" )
531532 iterable_result = decode_tool_result (last )
532533
534+ # Materialise before testing/iterating: an ``over:`` expression may
535+ # yield a one-shot iterator (e.g. Jinja ``map``/``select`` filters),
536+ # which is always truthy and would defeat the emptiness check and be
537+ # consumed by a single pass. ``list(...)`` also raises TypeError for a
538+ # genuinely non-iterable value.
533539 try :
534- iter (iterable_result )
540+ items = list (iterable_result )
535541 except TypeError :
536542 logging .critical ("repeat_prompt iterable is not iterable: %r" , iterable_result )
537543 raise
538544
539- if not iterable_result :
545+ if not items :
540546 await render_model_output ("** 🤖❗repeat_prompt iterable is empty!\n " )
541547 else :
542- logging .debug ("Rendering templated prompts for results: %s" , iterable_result )
543- for value in iterable_result :
548+ logging .debug ("Rendering templated prompts for results: %s" , items )
549+ for value in items :
544550 try :
545551 rendered_prompt = render_template (
546552 template_str = task_prompt ,
@@ -1240,11 +1246,15 @@ async def _branch_record(tr: ToolResult) -> None:
12401246 f"** 🤖💾 Session saved: { session .session_id } \n "
12411247 f"** 🤖💡 Resume with: --resume { session .session_id } \n "
12421248 )
1243- break
1249+ # Raise (rather than break) so run_main propagates the
1250+ # failure and the CLI exits non-zero, matching the other
1251+ # hard-failure paths; the session is already saved for
1252+ # --resume.
1253+ raise RuntimeError (f"Required task { task_name !r} did not complete" )
12441254
12451255 # Capture this task's typed named output (M2). The task's
12461256 # output is its final tool result; when an ``outputs`` schema
1247- # is declared it is validated/coerced before being stored under
1257+ # is declared it is validated before being stored under
12481258 # ``outputs.<id>`` for downstream tasks to consume by name. A
12491259 # declared output contract that the produced value violates is
12501260 # a hard failure, surfaced with the same session-saved / resume
0 commit comments