fix: microflow layout issues (ConnectionIndex, redundant Merge, DESCRIBE roundtrip)#204
fix: microflow layout issues (ConnectionIndex, redundant Merge, DESCRIBE roundtrip)#204engalar wants to merge 5 commits intomendixlabs:mainfrom
Conversation
Writer serialized OriginConnectionIndex and DestinationConnectionIndex as int64, but parser read them with int32 type assertion. The assertion silently failed, defaulting all anchor indices to 0 (Top). This caused all flow connections to attach at the top of activities after any read-modify-write cycle, breaking visual layout in Studio Pro. Fix: parser uses extractInt() which handles both int32/int64; writer now emits int32 to match Mendix Studio Pro convention. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
When an IF statement has one branch ending with RETURN (guard check pattern), the ExclusiveMerge only had one incoming edge and was redundant. The split's continuing branch now connects directly to the next activity, producing more compact and readable layouts. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Remove spurious $ prefix on RETURN string expressions (only add $ for bare identifiers, not expressions with operators/quotes) - Trim trailing newline from DECLARE initial values (constant refs) - Detect guard pattern (IF + direct RETURN) and output as IF/END IF without ELSE, both at top-level and nested traversal. Follow false branch through merge to emit continuation at same indent level. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
AI Code ReviewCritical Issues: None found. First, understand the PR: It fixes three things:
Now, review against the checklist. Overlap & duplication
MDL syntax design (for new or modified MDL syntax)The PR does not add or modify MDL syntax. It fixes bugs in the execution of existing MDL (microflows). The DESCRIBE roundtrip fix affects how MDL is generated, but it doesn't change the MDL syntax itself; it fixes the output to be correct MDL syntax. So no new or modified MDL syntax. Therefore, the MDL syntax design checklist items are not applicable. We can skip this section. Full-stack consistency (for MDL features)Since no new MDL syntax or commands are added, this checklist is not applicable. The PR is fixing bugs in existing microflow execution and DESCRIBE generation, which are part of the existing stack. The fixes are within the executor and parser/writer, which are part of the stack. No new grammar, AST, visitor, etc. needed because no new syntax. So full-stack consistency is not applicable for this PR as it's a bug fix, not a new feature. Test coverage
Security & robustness
Scope & atomicity
Code quality
Bugs & correctness
Now, structure the review. Critical Issues: None Recommendation: Approve. But let's double-check for any potential issues. Look at the diff for cmd_microflows_builder_control.go: The logic for needMerge and the handling of nextFlowCase seems complex. But the PR says it fixes redundant Merge nodes for guard chains. The logic looks correct: it skips the merge when only one branch flows into it (the other returns). The nextFlowCase is set so that the parent connects the split to the next activity with the correct case. This seems correct. In cmd_microflows_builder_graph.go, the pendingCase logic is added to handle the case value for the next flow when there's no merge. This seems correct. In cmd_microflows_format_action.go, the DECLARE constant trailing newline is fixed by trimming suffix. Also, the RETURN expression $ prefix fix: they added a condition to not add $ if the returnVal contains any of +'"() (operators, quotes, parens). This makes sense because if the return value is an expression like $a + $b, it shouldn't have an extra $ prefix. The previous code only checked for prefix $, Mendix keyword, or qualified enum literal. Now it also checks for containing operators/quotes/parens. This seems correct to avoid double $ or incorrect prefixing. In cmd_microflows_show_helpers.go, the guard pattern detection is added: if true branch is a single EndEvent (RETURN), then it's a guard. Then it outputs IF/RETURN/END IF without ELSE, and continues from the false branch. This seems correct for DESCRIBE roundtrip. In sdk/mpr/parser_microflow.go, the ConnectionIndex parsing now uses extractInt (which likely handles both int32 and int64 from BSON). The writer now writes int32 instead of int64. This fixes the mismatch. Potential issue: In writer_microflow.go, they changed to int32, but what if the value is larger than int32? ConnectionIndex is likely small (0-3 for Top/Right/Bottom/Left), so int32 is fine. Now, any potential bugs in the fixes? In cmd_microflows_builder_control.go, in the else block (no merge), they set fb.nextConnectionPoint = splitID, and then set pendingCase based on conditions. Then in buildFlowGraph, when fb.nextConnectionPoint is set, they use it as lastID and set pendingCase = fb.nextFlowCase. This seems correct to propagate the case value. In the guard pattern detection in traverseFlow and traverseFlowUntilMerge, they set isGuard if trueFlow DestinationID is EndEvent. Then they handle it by outputting IF/RETURN/END IF and continuing from false branch. This seems correct for a guard (IF cond THEN RETURN; END IF). However, what about ELSE guards? Like IF cond THEN ... ELSE RETURN; END IF? The PR doesn't mention that, but the guard pattern they are fixing is likely the common case where the true branch returns. The PR body says "guard pattern detection (IF/RETURN/END IF without ELSE)", so they are handling the case where the true branch returns and there's no ELSE. The false branch continues. That seems correct. Also, in the DESCRIBE fix, they fixed trailing newline in DECLARE constants: in cmd_microflows_format_action.go, they trim suffix "\n" from InitialValue. This was likely causing extra newlines in DESCRIBE output. And the $ prefix fix for RETURN expressions. Overall, the fixes seem correct. Now, for the checklist: Overlap & duplication: No issues. MDL syntax design: Not applicable (no new syntax). Full-stack consistency: Not applicable (no new MDL feature). Test coverage: The PR provides test plan evidence. Since it's a bug fix, and they verified with specific examples, it's likely sufficient. The checklist for test coverage says "New executor commands should have MDL examples in mdl-examples/doctype-tests/". Not applicable. "Integration paths should be tested" — they did test integration (DESCRIBE → parse → execute roundtrip). So okay. Security & robustness: No obvious issues. Scope & atomicity: The PR groups three related fixes under "microflow layout issues". They are all related to microflow layout and DESCRIBE output, so it's a single concern. Okay. Code quality: The changes look consistent. Thus, recommendation is to approve. But let's see if there's any missing checklist item. The checklist has "Bugs & correctness": We should ensure the fixes are correct. In cmd_microflows_builder_control.go, the needMerge logic: Original code had: New code: Let's verify:
Case 1: IF ... THEN RETURN; END IF (no ELSE) Case 2: IF ... THEN ...; END IF (no ELSE, both continue) Case 3: IF ... THEN RETURN; ELSE ...; END IF Case 4: IF ... THEN ...; ELSE RETURN; END IF Case 5: IF ... THEN RETURN; ELSE RETURN; END IF Case 6: IF ... THEN ...; ELSE ...; END IF (both continue) So the logic seems correct. In the no merge case, they set fb.nextConnectionPoint = splitID, and set pendingCase. In buildFlowGraph, when fb.nextConnectionPoint is set, they set lastID = fb.nextConnectionPoint, and pendingCase = fb.nextFlowCase. This means that the next flow will originate from the splitID (not from a merge), and the case value is pendingCase. For example, in IF ... THEN RETURN; END IF (no ELSE), the split has two outputs: FALSE to merge? Wait, no merge, so the split's FALSE output should go directly to the next activity, and TRUE output goes to the RETURN (EndEvent). Since there's no merge, the split's FALSE output is connected to the next activity with case "false", and the TRUE output goes to EndEvent. In the code, when needMerge is false, they set fb.nextConnectionPoint = splitID, and set pendingCase based on which branch returns. In the IF without ELSE case: thenReturns = true, so pendingCase = "false" (since hasElseBody=false, they set fb.nextFlowCase = "false"). Then, when processing the next statement, buildFlowGraph will see pendingCase != "", so it will create a horizontalFlowWithCase from lastID (which is splitID, set via fb.nextConnectionPoint) to the next activity with case = pendingCase ("false"). And the TRUE branch: in addIfStatement, when !needMerge (no merge), they still draw the TRUE path below: they set thenCenterY and traverse the THEN branch. Since the THEN branch ends with RETURN (EndEvent), it will be connected appropriately. In the traverseFlow for the IF statement, when needMerge is false, they set fb.posX and fb.posY, and fb.nextConnectionPoint = splitID, and fb.nextFlowCase. Then, in buildFlowGraph, after addStatement returns the splitID, they set lastID = splitID (from fb.nextConnectionPoint), and pendingCase = fb.nextFlowCase. Then, when processing the next activity, they create a flow with case. This seems correct. One potential issue: In the no merge case, they set fb.posX and fb.posY, but what is the position used for? It's used for placing subsequent activities. In the no merge case, they calculate afterSplit and afterBranch and set fb.posX to the max. This seems to position after the split and after the downward branch (if any). This is likely correct for layout. Now, for the DESCRIBE fixes: In cmd_microflows_show_helpers.go, they added guard pattern detection. They check if trueFlow DestinationID is EndEvent. Then they set isGuard = true. Then, in the guard case, they traverse the trueFlow (to the EndEvent) until merge, then output END IF, then continue from false branch. But note: In a guard IF ... THEN RETURN; END IF, there is no ELSE, so falseFlow is nil. In the code, they have: if isGuard { In the guard case with no ELSE (falseFlow nil), they do not append ELSE, and do not traverse false branch. They just output END IF and then continue after the merge point. But what about the false branch? In a guard IF ... THEN RETURN Automated review via OpenRouter (Nemotron Super 120B) — workflow source |
- Add MODEL to settingsSection grammar rule (no longer matches IDENTIFIER after f82cee3 made it a keyword) - Remove Mendix reserved words (Type, Owner, Object) from keyword-as-identifier.mdl to avoid CE7247 in mx check - Fix guard pattern detection: when both IF branches return directly, emit IF/ELSE/END IF instead of guard-style IF/END IF Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
AI Code ReviewLet's check the diff for cmd_microblogs_format_action.go to see what was changed there: In cmd_microflows_format_action.go:
This relates to DESCRIBE roundtrip mentioned in the PR: Fixed '$' prefix on RETURN expressions, trailing newline in DECLARE constants. So these changes are part of making DESCRIBE output re-parseable. Now let's look at cmd_microflows_show_helpers.go changes - significant rewrite of traverseFlow and traverseFlowUntilMerge functions. This is where the guard pattern detection and rendering happens. They added logic to detect guard patterns (IF with the PRats RETURN without ELSE where only one branch returns) and render them without creating a merge node. This implements the checklist items related to IF/RETURN/END IF without ELSE pattern detection and DESCRIBE roundtrip. The grammar change in MDLParser.g4 added MODEL as an option in settingsSection alongside WORKFLOWS and IDENTIFIER. This seems minor - allowing "MODEL" as a settings section identifier. The significant logic changes are in the microflows builder files where they implemented:
Automated review via OpenRouter (Nemotron Super 120B) — workflow source |
…x-int64 # Conflicts: # mdl/grammar/parser/MDLParser.interp # mdl/grammar/parser/mdl_parser.go
AI Code ReviewCritical Issues
Moderate Issues
Minor Issues
What Looks Good
RecommendationApprove with minor clarification needed The PR successfully fixes three significant issues that affect correctness (ConnectionIndex), layout quality (redundant Merge), and usability (DESCRIBE roundtrip). The test plan confirms these fixes work end-to-end. Regarding the grammar change: since the PR description doesn't mention new settings functionality and focuses exclusively on the listed fixes, I believe the MODEL addition to
Please confirm: Is the MODEL grammar change intended to support the DESCRIBE roundtrip fixes (e.g., ensuring model settings format correctly in DESCRIBE output), or is it adding new standalone functionality? If the latter, please verify the full-stack updates are present in the non-visible portions of the diff. Given the instructions to treat generated ANTLR parser files as noise, and assuming Automated review via OpenRouter (Nemotron Super 120B) — workflow source |
ReviewOverall: solid fixes on real bugs, two concerns. The three headline fixes (ConnectionIndex type, redundant Merge elimination, DESCRIBE RETURN prefix) are all legitimate. Plus this PR fixes the What I like
Concerns
VerdictApprove after (1) is clarified/split, (2) fixed, (3) cleaned up. (4) and (5) before merge. |
Fixes #132
Summary
OriginConnectionIndex/DestinationConnectionIndexasint64, but parser read with.(int32)type assertion — silently failed, all anchors defaulted to 0 (Top). Connections pointed at wrong edges after any read-modify-write cycle.IF cond THEN RETURN; END IF) createdExclusiveMergewith in-degree=1, wasting layout space. Now skipped when only one branch flows into the merge.$prefix on RETURN expressions, trailing newline in DECLARE constants, and guard pattern detection (IF/RETURN/END IF without ELSE). DESCRIBE output is now re-parseable.Insight
ALTER MICROFLOWpreserves@positionfrom original activities, preventing Studio Pro from applying auto-layout. DESCRIBE should omit positions to let the layout engine or Studio Pro compute optimal positions.Test plan
Tool_SendOrderToSupplier(28 flows, correct R/L/B/T anchors)Tool_SendOrderToSupplier4created successfully🤖 Generated with Claude Code