feat: Add nested loop support in control flow (gt-zn35j)

Enables loops within loop bodies to be properly expanded:

- Copy Loop field in expandLoopIteration (was intentionally omitted)
- Add cloneLoopSpec for deep copying of LoopSpec with body steps
- Recursively call ApplyLoops at end of expandLoop to expand nested loops
- Also copy OnComplete field for consistency

Nested IDs follow natural chaining pattern:
  outer.iter1.inner.iter2.step

Tested with 2-level and 3-level nesting scenarios.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Steve Yegge
2025-12-25 16:22:36 -08:00
parent 47cc84de3a
commit b43358b600
2 changed files with 184 additions and 2 deletions

View File

@@ -129,7 +129,8 @@ func expandLoop(step *Step) ([]*Step, error) {
result = iterSteps
}
return result, nil
// Recursively expand any nested loops in the result (gt-zn35j)
return ApplyLoops(result)
}
// expandLoopIteration expands a single iteration of a loop.
@@ -155,7 +156,8 @@ func expandLoopIteration(step *Step, iteration int) ([]*Step, error) {
WaitsFor: bodyStep.WaitsFor,
Expand: bodyStep.Expand,
Gate: bodyStep.Gate,
// Note: Loop field intentionally not copied - nested loops need explicit support
Loop: cloneLoopSpec(bodyStep.Loop), // Support nested loops (gt-zn35j)
OnComplete: cloneOnComplete(bodyStep.OnComplete),
}
// Clone ExpandVars if present
@@ -408,3 +410,22 @@ func cloneStepDeep(s *Step) *Step {
return clone
}
// cloneLoopSpec creates a deep copy of a LoopSpec (gt-zn35j).
func cloneLoopSpec(loop *LoopSpec) *LoopSpec {
if loop == nil {
return nil
}
clone := &LoopSpec{
Count: loop.Count,
Until: loop.Until,
Max: loop.Max,
}
if len(loop.Body) > 0 {
clone.Body = make([]*Step, len(loop.Body))
for i, step := range loop.Body {
clone.Body[i] = cloneStepDeep(step)
}
}
return clone
}