fix: preserve rule-based decision subtype across microflow roundtrips#265
Conversation
AI Code ReviewCritical IssuesNone found. Moderate IssuesNone found. Minor Issues
What Looks Good
RecommendationApprove. The PR correctly fixes the reported bug with comprehensive tests, maintains architectural boundaries, and includes a valuable secondary fix for annotation handling. The minor issues (unused field, lightweight documentation) do not block merging and can be addressed in follow-up work. This PR is ready for integration. Automated review via OpenRouter (Nemotron Super 120B) — workflow source |
|
Re: the AI reviewer's minor note about the unused The second note (performance/caching characteristics of |
AI Code ReviewWhat Looks Good
Minor Issues
RecommendationApprove - The PR correctly fixes the reported issue with adequate test coverage, follows the project's architectural patterns, and maintains code quality standards. The minor concern about parameter name construction is validated by the included tests. The annotation fix, while slightly broadening the scope, addresses a related correctness issue in the same code area. Automated review via OpenRouter (Nemotron Super 120B) — workflow source |
When an IF, LOOP, or WHILE statement carried an @caption or @annotation and contained other annotated statements in its body, the inner statement's annotations would overwrite fb.pendingAnnotations (shared builder state) before the outer loop in buildFlowGraph attached them to the right object. The outer split/loop then ended up with the wrong caption — or with the inner statement's caption applied to it. The fix snapshots & clears pendingAnnotations at the top of each compound statement handler, re-applies them to the split/loop/while activity right after it's created, and (for IF) moves applyAnnotations above the branch recursion so the split is decorated before the bodies are walked. Also extends applyAnnotations to recognise ExclusiveSplit and InheritanceSplit — on main these were only handled for ActionActivity, so @caption on a split was silently dropped even without nesting. Verified end-to-end against Apps.GetOrCreateMendixVersionFromString: describe -> exec -> describe now produces byte-identical @caption, @annotation, and @position output. Regression tests cover: - nested IFs with explicit @caption on each level - single IF @caption baseline - @annotation attachment targeting the correct split when nested Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
- mergeStatementAnnotations: add WhileStmt case (was falling through to default: nil, so @caption on a WHILE was silently dropped). - applyAnnotations: add LoopedActivity case — LOOP and WHILE activities can now carry the captions they already parse. - Add TestLoopCaptionPreserved, TestWhileLoopCaptionPreserved, and TestInheritanceSplitCaptionApplied to cover the gaps ako flagged (loop and InheritanceSplit caption paths were previously untested). - Add `mdl-examples/bug-tests/263-nested-caption-preservation.mdl` reproducer per CLAUDE.md checklist.
An IF whose condition calls a Mendix rule (Module.RuleName(Param = arg)) was always serialized as Microflows$ExpressionSplitCondition, causing Mendix Studio Pro to raise CE0117 "Error(s) in expression" and silently demoting the decision subtype from Rule to Expression on every describe -> exec roundtrip. The flow builder now inspects the IF condition: when it is a qualified function call whose name resolves to a rule (checked via the new MicroflowBackend.IsRule), it emits Microflows$RuleSplitCondition with a nested RuleCall sub-document whose ParameterMappings carry the named arguments. Plain expressions and non-rule function calls still produce ExpressionSplitCondition. The MPR writer gained the corresponding BSON branch so the new split type reaches disk with the shape Studio Pro expects. To make the fix self-contained, this change also extends microflows.RuleSplitCondition with RuleQualifiedName and RuleCallParameterMapping with ParameterName — the two string fields the builder/writer/parser now round-trip through (model.ID-only pointers weren't enough to reconstruct the rule reference after exec). Regression tests cover: - Rule-call IF produces RuleSplitCondition with correct parameter names - Non-rule call IF still produces ExpressionSplitCondition - Flow builder without a backend (syntax-only check) falls back safely - Writer -> BSON -> parser roundtrip preserves RuleSplitCondition shape Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Per PR review — the field was only set by the parser but never read anywhere. The rule call is already identified by RuleQualifiedName, which is what both the writer and describer use.
1589b68 to
57f8739
Compare
AI Code Reviewapprove Review SummaryThis PR fixes a critical bug where rule-based IF conditions were being incorrectly serialized as ExpressionSplitCondition instead of RuleSplitCondition, causing Studio Pro validation errors (CE0117). The fix properly distinguishes between rule calls and expressions in IF conditions and ensures correct BSON serialization/deserialization. Key Changes:
Checklist Compliance:
The PR correctly addresses the round-trip preservation issue for rule-based decisions and includes the necessary test coverage to prevent regression. The stacked approach (with PR #266 for describer side) is properly noted. Recommended for approval. Automated review via OpenRouter (Nemotron Super 120B) — workflow source |
PR #265 Review — fix: preserve decision/loop subtype across nested control flowOverviewFull-stack fix for What's Good
Notes / Non-blockers
Positional arguments to rule calls silently dropped if _, ok := arg.(*ast.NamedArgExpr); !ok {
return nil, fmt.Errorf("rule call %q: positional arguments are not supported; use named arguments", qualifiedName)
}Mock default for VerdictLGTM — the correctness fix and test coverage are solid. The |
Summary
A decision (
ExclusiveSplit) whose condition was a rule call was being silently demoted to an expression split on round-trip. The BSON stores aMicroflows$RuleSplitCondition(notExpressionSplitCondition), and emitting the wrong subtype causes Studio Pro to raise CE0117 "Error(s) in expression".Fix adds a
RuleQualifiedName+ParameterNamefield to the split condition struct and wires the parser / writer to round-trip the subtype faithfully. The describer side of this fix is in PR #266 (stacked).Part of umbrella #257.
Test plan
if Mod.Rule(param=$value) then ...round-trip; the resulting BSON containsMicroflows$RuleSplitCondition, not the expression variant.go build ./... && go test ./...pass.Stack
This PR is the second of a 3-PR stack. It depends on #263 (nested caption preservation) because both modify the same flow-builder structs. The describer-side counterpart is in #266, which depends on this PR.