fix: preserve nested loop body flow traversal#329
fix: preserve nested loop body flow traversal#329hjotha wants to merge 5 commits intomendixlabs:mainfrom
Conversation
AI Code ReviewWe are given a PR titled "fix: preserve nested loop body flow traversal" The PR summary says:
The changes are in two files:
We are to review against the provided checklist. First, note that the PR does not introduce any new MDL syntax (it's a fix to the executor's traversal logic for loops). Therefore, we skip the MDL syntax design section. Now, let's go through the checklist: Overlap & duplication
Full-stack consistency (for MDL features)Since this is a bug fix and not a new MDL feature, we do not expect changes to the grammar, AST, visitor, etc. The changes are confined to the executor. Therefore, we skip the full-stack consistency check for MDL features. Test coverage
Security & robustness
Scope & atomicity
Code quality
Bugs & correctness
Now, let's look at the changes in detail: In
In the test file:
Automated review via OpenRouter (Nemotron Super 120B) — workflow source |
AI Code ReviewReview SummaryCritical Issues: None
Recommendation: Approve Detailed FindingsOverlap & Duplication
MDL Syntax Design
Full-Stack Consistency
Test Coverage
Security & Robustness
Scope & Atomicity
Code Quality
ConclusionThe PR correctly addresses bug #328 by ensuring parent-level continuation flows are merged into loop-local traversal maps for nested loop bodies. The solution is minimal, well-tested, and maintains existing code quality standards. No checklist violations found. Ready for merge. Automated review via OpenRouter (Nemotron Super 120B) — workflow source |
ako
left a comment
There was a problem hiding this comment.
Review: fix: preserve nested loop body flow traversal
Summary: Correct, well-targeted fix. The root cause (short-circuiting parent-flow merging when a local loop-boundary flow already existed for the same origin) is directly addressed, and the recursive descent via collectLoopObjectIDs correctly handles the nested-loop case the old loopActivityMap lookup missed.
Core fix correctness
Old guard:
if _, inLoop := loopActivityMap[originID]; inLoop {
if _, exists := loopFlowsByOrigin[originID]; !exists {
loopFlowsByOrigin[originID] = flows
}
}The !exists short-circuit silently dropped parent-level continuation flows for any origin that already had a local loop-boundary flow. The new code merges both using appendSequenceFlowIfMissing. ✓
The same fix is correctly applied to the flowsByDest side too. ✓
collectLoopObjectIDs — recursive descent is necessary
The old code used loopActivityMap (immediate loop objects only). The new loopObjectIDs descends into nested LoopedActivity.ObjectCollection objects, which is needed so parent-level flows for nested loop body activities are also merged. The recursion is straightforward and handles nil ObjectCollections. ✓
preferLoopBodyStart — deterministic start selection
Replacing the previous non-deterministic break with a position-based tiebreaker is a good improvement. One edge: if two activities have exactly the same X/Y position AND both have zero incoming flows, the winner still depends on map iteration order. This is an acceptable edge case (same-position activities are a user authoring issue), but a secondary tiebreaker on ID string would make it fully deterministic.
sequenceFlowIdentity — composite key gap
return fmt.Sprintf("%s>%s:%t:%d:%d", flow.OriginID, flow.DestinationID, flow.IsErrorHandler, flow.OriginConnectionIndex, flow.DestinationConnectionIndex)The composite fallback key (used when flow.ID == "") does not include CaseValue. If a split emits two flows from the same origin to the same destination with different case values but without IDs (possible in test-constructed graphs), one would be incorrectly deduplicated. In production data every flow has a UUID so this only affects test helpers — but since test flows are constructed without IDs in this very file, it's worth noting. Adding :%v for CaseValue would close the gap.
Test coverage
The test graph is well-designed — adversarial storage order (nested loop listed first), local boundary flow on fetch to trigger the original bug, and parent-level continuation flow from fetch to nested. The ordering assertions (setup before fetch before nested loop) catch the regression.
One gap: the test verifies that nested and nested-tail appear in the output, but not their relative order inside the nested loop body. A position check similar to the outer body assertions would catch a future inner-body ordering regression.
Bug test script
Covers a realistic nested-loop microflow with outer and inner body activities. The describe → exec → describe fixpoint requirement is documented in the header. ✓
Verdict
Approve. The fix is correct and the recursive descent approach is the right solution. The two minor items (same-position tiebreaker and CaseValue in composite key) are low-priority but easy wins.
AI Code ReviewCritical IssuesNone found. Moderate IssuesNone found. Minor Issues
What Looks Good
RecommendationApprove the PR. The changes are correct, well-tested, and follow project conventions. Minor test improvement suggestions do not block merging. Automated review via OpenRouter (Nemotron Super 120B) — workflow source |
The composite fallback used when flow.ID is empty omitted CaseValue, so two split branches with the same origin/destination but different case values were collapsed by the loop-body merge. Production flows always have UUIDs so this only manifests in test-constructed graphs, but the fix is one extra %v in the format string and prevents the class of bug entirely. Addresses ako's review on PR mendixlabs#329. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
AI Code ReviewWhat Looks Good
RecommendationApprove. This is a well-scoped bug fix that:
The fix correctly merges parent-level body flows into loop-local traversal maps for origins in the loop body, resolving the issue where nested loop bodies would collapse into single statements during DESCRIBE → EXECUTE → DESCRIBE roundtrips. Automated review via OpenRouter (Nemotron Super 120B) — workflow source |
Symptom: describe could omit loop-body continuation activities when a loop stored boundary flows locally but stored the real body continuation in the parent graph. Root cause: loop traversal preferred the loop-local origin map whenever an origin already existed there, so parent-level flows for the same origin were not merged into the loop body. Nested loop descendants also did not receive the relevant parent-level body flows. Fix: collect loop body object IDs recursively, merge missing parent-level body flows into loop-local flow maps, and choose loop body starts from the merged flow graph. Tests: add a nested-loop traversal regression where local boundary flows and parent-level continuation flows share origins.
Adds an MDL script under mdl-examples/bug-tests/ exercising nested loops where the inner loop's body activities live in the parent microflow graph. The describe → exec → describe fixpoint confirms the describer merges parent-level body flows into the loop-local traversal map. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Symptom: loop bodies that contained nested decision flows could describe the body as a flat sequence and lose the structured `else` / `end if` shape needed for a valid roundtrip. Root cause: loop-body traversal had a separate simplified walker that did not build a loop-local split-to-merge map before walking nested branches. Fix: extract split/merge discovery for an arbitrary graph and reuse the main structured traversal inside loop bodies. Tests: added synthetic loop-body coverage for an IF/ELSE split inside a loop object collection.
The composite fallback used when flow.ID is empty omitted CaseValue, so two split branches with the same origin/destination but different case values were collapsed by the loop-body merge. Production flows always have UUIDs so this only manifests in test-constructed graphs, but the fix is one extra %v in the format string and prevents the class of bug entirely. Addresses ako's review on PR mendixlabs#329. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
4c404cc to
e9a004c
Compare
AI Code ReviewCritical Issues
Moderate Issues
Minor Issues
What Looks Good
Recommendation Automated review via OpenRouter (Nemotron Super 120B) — workflow source |
|
Review follow-up status on the current head:
Local validation after the update was green: make build
make test
make lint-go |
Part of #332.
Summary
Closes #328
Validation
go test ./mdl/executor -run TestTraverseFlow_LoopBodyMergesParentFlowsForExistingOriginmake buildmake lint-gomake test