fix: preserve cross-module associations on CREATE object actions#502
Conversation
A `create TargetMod.Entity (OwnerMod.Assoc = $Ref)` statement round-tripped as a plain attribute assignment when the association lived in a module other than the create target. Two sides of the same bug: 1. **Describer** (`formatAction` on `CreateObjectAction`) stripped every association member down to its bare name (`Module.Assoc` → `Assoc`), so the authored cross-module qualifier was lost. 2. **Builder** (`resolveMemberChange`) then queried the create target's module for the bare name. The association was not defined there, so the lookup fell through to the attribute slot and Studio Pro reopened the MPR with CE1613 "selected attribute no longer exists". Fixes: - Describer now keeps the `Module.` prefix on any association whose owning module differs from the create target's module. - Builder now uses the authored module (when the member name is qualified) to look up the association, matching how the describer expresses it. Added builder unit test \`TestResolveMemberChange_CrossModuleAssociationKeepsAssociationSlot\` for the synthetic cross-module pattern. Part of mendixlabs#352. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
AI Code ReviewWhat Looks Good
Minor Issues
RecommendanceApprove. The PR resolves the reported issue with appropriate test coverage and minimal, well-documented changes. The fix aligns with the project's architecture and maintains full-stack consistency for the existing MDL feature. The minor issues do not block merging. Automated review via OpenRouter (Nemotron Super 120B) — workflow source |
Review: fix: preserve cross-module associations on CREATE object actionsVerdict: Approve with minor note What the PR doesFixes a describe → exec → describe roundtrip breakage for cross-module associations on Describer side ( Builder side ( No blockersMinor: formatter change is untested
A test for the cross-module path would be: action := µflows.CreateObjectAction{
EntityQualifiedName: "TargetMod.Entity",
OutputVariable: "NewObj",
InitialMembers: []*microflows.MemberChange{
{AssociationQualifiedName: "OwnerMod.Entity_Other", Value: "$Ref"},
},
}
want := "$NewObj = create TargetMod.Entity (OwnerMod.Entity_Other = $Ref);"Not a blocker — the builder test catches the end-to-end regression, and the formatter logic is straightforward — but worth adding for roundtrip completeness (Recurring Finding #4). Positive notes
|
Part of #352.
Summary
create TargetMod.Entity (OwnerMod.Assoc = \$Ref)statement round-tripped as an attribute assignment when the association lived in a different module than the create target.formatActiononCreateObjectActionstripped every association member down to its bare name, losing the cross-module qualifier.resolveMemberChangequeried the create target's module for the bare name, didn't find the association, and fell through toAttributeQualifiedName— triggering Studio Pro CE1613 ("selected attribute no longer exists") on re-open.Fix
Module.prefix on any association whose owning module differs from the create target's module.Test plan
TestResolveMemberChange_CrossModuleAssociationKeepsAssociationSlotverifies the association lands onAssociationQualifiedName(not the attribute slot) with synthetic module names.go build -tags integration ./...builds clean.go test ./mdl/executor/ -count=1passes.🤖 Generated with Claude Code