fix: classify reverse-Reference traversal through entity inheritance#522
fix: classify reverse-Reference traversal through entity inheritance#522hjotha wants to merge 1 commit intomendixlabs:mainfrom
Conversation
AI Code ReviewCritical Issues
Moderate Issues
Minor Issues
What Looks Good
RecommendationApprove. The PR correctly fixes the inheritance-related bug in reverse Reference traversal, includes comprehensive tests, maintains backward compatibility, and follows the project's architectural patterns. No blocking issues were identified. Minor documentation improvements are suggested but not required for merge. Automated review via OpenRouter (Nemotron Super 120B) — workflow source |
addRetrieveAction decided whether `retrieve $X from $V/M.Assoc` was a
reverse traversal (child-side → parent-side, returning a list) by
comparing the start variable's entity type to the association's child
entity name *exactly*. Inheritance broke that check: when the start
variable was a subclass of the association's child entity (e.g. a
HttpRequest variable traversing a HttpHeaders association whose Child
is HttpMessage), the comparison missed and the result variable was
typed as the parent entity *singleton* instead of `List of <parent>`.
Downstream `find($List, attr = value)` then dispatched to
`FindByExpression` instead of `Find` because resolveListOperationMember
read the variable type as a non-list. Studio Pro authors `Find` for the
simple equality shape; emitting `FindByExpression` triggers `mx check`
CE0117 "Error in expression at List operation activity 'Find by
expression'" because the runtime can no longer see the qualified
attribute path the model expects.
Introduce entityIsSubtypeOf, an inheritance-aware walk (mirrors the
existing resolveAttributeInEntityHierarchy traversal), and replace the
exact-match `startVarType == childEntityQN` checks in addRetrieveAction
with `entityIsSubtypeOf(startVarType, childEntityQN)`. The replacement
covers both the expandReverseReference DatabaseRetrieveSource branch
and the AssociationRetrieveSource fallback that registers the result
type. Direct (non-inheriting) traversal still works the same way; the
helper short-circuits on identity.
Two regression tests:
- reverse Reference through an inherited child registers
`List of <parent>` (covers the original bug)
- reverse Reference with an exact child match still registers
`List of <parent>` (guards the non-inheritance path)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1833b52 to
8a983ac
Compare
AI Code ReviewReview SummaryThis PR fixes a bug in reverse association traversal when the start variable is a subtype of the association's child entity. The fix introduces an inheritance-aware helper function Critical Issues
Moderate Issues
Minor Issues
What Looks Good
RecommendationApprove the PR. The fix correctly addresses the inheritance issue in reverse association traversal, is well-tested, and maintains backward compatibility for non-inheritance cases. The solution follows established patterns in the codebase. Automated review via OpenRouter (Nemotron Super 120B) — workflow source |
Summary
addRetrieveActiondecided whetherretrieve $X from $V/M.Assocwas a reverse traversal (child-side → parent-side, returning a list) by comparing the start variable's entity type to the association's child entity name exactly. Inheritance broke that check: when the start variable was a subclass of the association's child entity, the comparison missed and the result variable was registered as the parent entity singleton instead ofList of <parent>.Downstream
find($List, attr = value)then dispatched toFindByExpressioninstead ofFindbecauseresolveListOperationMemberread the variable type as a non-list. Studio Pro authorsFindfor the simple equality shape; emittingFindByExpressiontriggersmx checkCE0117 "Error in expression at List operation activity 'Find by expression'"because the runtime can no longer see the qualified attribute path the model expects.Reproduction
A Reference association with
Parent=Header / Child=Message. A microflow holds a$Requestvariable typed as aRequestentity that extendsMessage. Traversing$Request/Module.Headersshould yieldList of Header:Before this PR:
mx checkreportsCE0117at theFind by expressionactivity. After this PR:mx checkreports 0 errors and the result variable is correctly typed asList of Module.Header.Fix
New
entityIsSubtypeOfhelper performs an inheritance-aware walk (mirrors the existingresolveAttributeInEntityHierarchytraversal). The exact-matchstartVarType == childEntityQNchecks inaddRetrieveActionare replaced withentityIsSubtypeOf(startVarType, childEntityQN). The replacement covers both theexpandReverseReferenceDatabaseRetrieveSourcebranch and theAssociationRetrieveSourcefallback that registers the result type. Direct (non-inheriting) traversal still works the same way; the helper short-circuits on identity.Test plan
TestAddRetrieveAction_ReverseRefThroughInheritedChild— reverse Reference through an inherited child entity registersList of <parent>.TestAddRetrieveAction_ReverseRefDirectChild— guards the non-inheritance path: exact child match continues to registerList of <parent>.go test ./mdl/executor/).go test ./...passes.🤖 Generated with Claude Code