@@ -2224,6 +2224,101 @@ def test_create_continue_executor_no_loop_context(self):
22242224class TestBuilderEdgeWiring :
22252225 """Tests for builder edge wiring methods."""
22262226
2227+ def test_foreach_advance_edge_wired_from_last_body_action (self ):
2228+ """Advance edge must come from the last body action."""
2229+ from agent_framework_declarative ._workflows import DeclarativeWorkflowBuilder
2230+
2231+ yaml_def = {
2232+ "name" : "foreach_seq" ,
2233+ "actions" : [
2234+ {"kind" : "SetValue" , "id" : "set_items" , "path" : "Local.items" , "value" : ["A" , "B" ]},
2235+ {
2236+ "kind" : "Foreach" ,
2237+ "id" : "loop" ,
2238+ "itemsSource" : "=Local.items" ,
2239+ "iteratorVariable" : "Local.item" ,
2240+ "actions" : [
2241+ {"kind" : "SendActivity" , "id" : "step_1" , "activity" : {"text" : "one" }},
2242+ {"kind" : "SendActivity" , "id" : "step_2" , "activity" : {"text" : "two" }},
2243+ {"kind" : "SendActivity" , "id" : "step_3" , "activity" : {"text" : "three" }},
2244+ ],
2245+ },
2246+ ],
2247+ }
2248+
2249+ workflow = DeclarativeWorkflowBuilder (yaml_def ).build ()
2250+ edges = {(e .source_id , e .target_id ) for group in workflow .edge_groups for e in group .edges }
2251+
2252+ assert ("step_3" , "loop_next" ) in edges
2253+ assert ("step_1" , "loop_next" ) not in edges
2254+ assert ("step_2" , "loop_next" ) not in edges
2255+ assert ("step_1" , "step_2" ) in edges
2256+ assert ("step_2" , "step_3" ) in edges
2257+
2258+ def test_foreach_advance_edge_skipped_for_terminator_body (self ):
2259+ """BreakLoop at end of body wires itself to loop_next; no duplicate edge."""
2260+ from agent_framework_declarative ._workflows import DeclarativeWorkflowBuilder
2261+
2262+ yaml_def = {
2263+ "name" : "foreach_terminator" ,
2264+ "actions" : [
2265+ {"kind" : "SetValue" , "id" : "set_items" , "path" : "Local.items" , "value" : ["A" ]},
2266+ {
2267+ "kind" : "Foreach" ,
2268+ "id" : "loop" ,
2269+ "itemsSource" : "=Local.items" ,
2270+ "iteratorVariable" : "Local.item" ,
2271+ "actions" : [
2272+ {"kind" : "SendActivity" , "id" : "step_1" , "activity" : {"text" : "one" }},
2273+ {"kind" : "BreakLoop" , "id" : "stop" },
2274+ ],
2275+ },
2276+ ],
2277+ }
2278+
2279+ workflow = DeclarativeWorkflowBuilder (yaml_def ).build ()
2280+ all_edges = [(e .source_id , e .target_id ) for group in workflow .edge_groups for e in group .edges ]
2281+ assert all_edges .count (("stop" , "loop_next" )) == 1
2282+ assert ("step_1" , "loop_next" ) not in all_edges
2283+
2284+ def test_foreach_advance_edge_with_if_as_last_body_action (self ):
2285+ """Trailing If in a Foreach body wires every branch exit to loop_next."""
2286+ from agent_framework_declarative ._workflows import DeclarativeWorkflowBuilder
2287+
2288+ yaml_def = {
2289+ "name" : "foreach_if_last" ,
2290+ "actions" : [
2291+ {"kind" : "SetValue" , "id" : "set_items" , "path" : "Local.items" , "value" : ["A" , "B" ]},
2292+ {
2293+ "kind" : "Foreach" ,
2294+ "id" : "loop" ,
2295+ "itemsSource" : "=Local.items" ,
2296+ "iteratorVariable" : "Local.item" ,
2297+ "actions" : [
2298+ {"kind" : "SendActivity" , "id" : "step_1" , "activity" : {"text" : "one" }},
2299+ {
2300+ "kind" : "If" ,
2301+ "id" : "check" ,
2302+ "condition" : '=Local.item = "A"' ,
2303+ "then" : [
2304+ {"kind" : "SendActivity" , "id" : "then_action" , "activity" : {"text" : "then" }},
2305+ ],
2306+ "else" : [
2307+ {"kind" : "SendActivity" , "id" : "else_action" , "activity" : {"text" : "else" }},
2308+ ],
2309+ },
2310+ ],
2311+ },
2312+ ],
2313+ }
2314+
2315+ workflow = DeclarativeWorkflowBuilder (yaml_def ).build ()
2316+ edges = {(e .source_id , e .target_id ) for group in workflow .edge_groups for e in group .edges }
2317+
2318+ assert ("then_action" , "loop_next" ) in edges
2319+ assert ("else_action" , "loop_next" ) in edges
2320+ assert ("step_1" , "loop_next" ) not in edges
2321+
22272322 def test_wire_to_target_with_if_structure (self ):
22282323 """Test wiring to an If structure routes to evaluator."""
22292324 from agent_framework import WorkflowBuilder
0 commit comments