Add support for for-each expansion over computed ranges:
loop:
range: "1..2^{disks}-1" # Evaluated at cook time
var: move_num
body: [...]
Features:
- Range field in LoopSpec for computed iterations
- Var field to expose iteration value to body steps
- Expression evaluator supporting + - * / ^ and parentheses
- Variable substitution in range expressions using {name} syntax
- Title/description variable substitution in expanded steps
Example use case: Towers of Hanoi formula where step count is 2^n-1
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- ApplyBranches and ApplyGates now clone steps before modifying,
matching the immutability pattern of ApplyLoops and ApplyAdvice
- Added internal applyBranchesWithMap/applyGatesWithMap for efficiency
- ApplyControlFlow builds stepMap once for both (gt-gpgdv optimization)
- Added cloneStepsRecursive helper
- Added TestApplyBranches_Immutability and TestApplyGates_Immutability
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Fixes a bug where outer iteration chaining was lost when nested loops
were expanded. The problem was:
1. chainLoopIterations set: outer.iter2.inner depends on outer.iter1.inner
2. ApplyLoops recursively expanded nested loops
3. outer.iter2.inner became outer.iter2.inner.iter1.work, etc.
4. The dependency was lost (referenced non-existent ID)
The fix:
- Move recursive ApplyLoops BEFORE chaining
- Add chainExpandedIterations that finds iteration boundaries by ID prefix
- Works with variable step counts per iteration (nested loops expand differently)
Now outer.iter2's first step correctly depends on outer.iter1's LAST step,
ensuring sequential execution of outer iterations.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
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>
Critical bug fixes identified during code review:
1. External dependency rewriting: Dependencies on steps OUTSIDE the loop
are now preserved as-is instead of being incorrectly prefixed.
2. Children dependency rewriting: Child step dependencies are now properly
rewritten with iteration context.
3. Missing fields: Added Expand, ExpandVars, Gate fields to loop expansion.
4. Condition validation: Loop `until` conditions are now validated with
ParseCondition to catch syntax errors early.
5. Label format: Changed gate and loop labels from colon-delimited to
JSON format for unambiguous parsing:
- gate:{"condition":"expr"}
- loop:{"until":"expr","max":N}
Added TestApplyLoops_ExternalDependencies to verify the fix.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Add loop, branch, and gate operators for formula step transformation:
- LoopSpec: Fixed-count loops expand body N times with chained iterations.
Conditional loops expand once with runtime metadata labels.
- BranchRule: Fork-join patterns wire dependencies for parallel paths.
- GateRule: Adds condition labels for runtime evaluation before steps.
Types added to types.go:
- LoopSpec (count/until/max/body)
- BranchRule (from/steps/join)
- GateRule (before/condition)
- Loop field on Step
- Branch/Gate arrays on ComposeRules
New controlflow.go with ApplyLoops, ApplyBranches, ApplyGates, and
ApplyControlFlow convenience function. Wired into cook.go before
advice and expansion operators.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>