Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
16 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"area": "mdl/executor", "date": "2026-10-26", "symptom": "`retrieve $C from $O/Ord_Cust;` (association written by its short name) passed `check -p --references` and exec and was stored as the association `.Ord_Cust` (empty module): mx check CE1613 \"The selected association '.Ord_Cust' no longer exists.\", and describe printed `retrieve $C from $O/.Ord_Cust;`, which does not parse", "cause": "addRetrieveAction built the association name as `s.Source.Module + \".\" + s.Source.Name`; the visitor leaves Module empty for a bare name, and nothing on the check side looked at a bare retrieve association at all. MDL-RETRIEVE03's qualifyAssoc guessed the start entity's module instead — a second answer to the same question", "file": "`mdl/executor/bare_association.go` (new: resolveBareAssociation, associationCatalog, generalizationChainFor, the shared refusal text), `mdl/executor/cmd_microflows_builder_actions.go` (addRetrieveAction, qualifyBareRetrieveAssociation), `mdl/executor/validate_flow_member_refs.go` (new, check side), `mdl/executor/flow_member_spelling.go` (speller qualifies it), `mdl/executor/validate_retrieve_association_list.go` (qualifyAssoc)", "insight": "Resolve rather than refuse: exactly one association of the name with an end on the start entity's generalization chain (either end — a retrieve from the TO side is legal; a specialization inherits) is unambiguous, and every resolved shape built at 0 errors on 11.13.0, including `$Account/UserRoles` -> System.UserRoles. Refuse the rest, and never write the bare name: an empty module is CE1613 every time, so even 'cannot tell' (start entity unknown to the builder) is a refusal at exec, while check stays silent there. The resolver is a function of plain data (name, chain, catalog) so exec feeds it the project as earlier statements left it and check feeds project + associations declared BEFORE the flow — a later-declared one resolves at check time and not in exec's statement order. Resolving introduced a second spelling of the same member, so the describe speller (describedMemberSpellings) must qualify it too, or `create or modify` rewrote the flow on every re-run (asserted: 'Unchanged microflow')", "fix": "Bare retrieve associations resolve through resolveBareAssociation in the builder, the speller, MDL-RETRIEVE03 and check --references; missing/ambiguous are refused by check and exec with the qualified spelling; exec also refuses an unresolvable bare name rather than writing `.Name`", "refs": ["ako/mxcli#1690", "ako/mxcli#1399"], "ce": ["CE1613"]}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"area": "mdl/executor", "date": "2026-10-26", "symptom": "`toString($User/id)`, `$A/id = $B/id`, `$User/Bogus` in a microflow expression passed `check -p --references` (and the type pass's 'Expression types OK') and exec; mx check CE0117 \"Error(s) in expression.\" (mendixlabs/mxcli#1650)", "cause": "Nothing resolved the member steps of an expression's `$Var/...` path against the variable's entity: the expression type checker infers KindUnknown for an attribute it cannot find and stays silent, and the reference pass only judged CREATE/CHANGE members", "file": "`mdl/executor/validate_flow_member_refs.go` (expressionPath, associationHop, statementAttributePaths)", "insight": "Put it in the reference pass, not the type pass: exec's preflight runs ValidateProgramWithWarnings, not TypeCheckProgram, so only the former gives check and exec one verdict. Measured on 11.13.0 in an app with no other error (one CE1613 elsewhere suppressed all twelve CE0117s): CE0117 for `id` in any case, a wrong-case attribute, a capitalised `CreatedDate`, `changedDate` the root does not store, bare `owner`, a BARE association (`$O/Ord_Cust/Name`), a missing association, and a missing attribute after a hop; clean for inherited members incl. System.User's and `createdDate` on a specialization whose root stores it. Collect paths by reflecting over each statement's fields (skipping nested bodies, which the typed walk visits with their own scope, a retrieve's XPath and a feedback target) rather than a hand-kept switch of statement kinds. System.User is readable from the project here (modelsdk backend), so `$User/Bogus` is judged", "fix": "check --references (and so exec's preflight) reports a `$Var/<member>` step that is not an attribute of the entity or its generalizations, a bare association, a missing association or one the entity is no end of; `id` gets the CommunityCommons.getGUID / compare-the-objects hint", "refs": ["ako/mxcli#1697", "mendixlabs/mxcli#1650"], "ce": ["CE0117"]}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"area": "mdl/executor", "date": "2026-10-26", "symptom": "find / filter / sort / an aggregate by a member the list's entity does not have (`find $l by Numbr = '42'`, `sum $l by Totl`) passed `check -p` and exec; mx check CE1613 \"The selected attribute 'Shop.Order.Numbr' no longer exists.\" once per activity. Also: `sum $sl by Total` with Total INHERITED was written as `Shop.SubOrd.Total` (CE1613) though the member exists", "cause": "resolveMemberChange (reached through resolveListOperationMember), the sort branch and the aggregate all fell back to writing `<entity>.<name>` when the name resolved to nothing; the aggregate never walked the generalization chain at all. check --references resolved CREATE/CHANGE members (#1048) and retrieve constraints (#1213) but no list-operation member", "file": "`mdl/executor/member_verdict.go` (new: memberVerdictOver, the one chain walk, with backendMemberModel; flowMemberVerdict; listMemberRefusal), `mdl/executor/cmd_microflows_builder_actions.go` (refuseMissingListMember in find/filter/sort/aggregate; aggregate qualifies with the declaring entity), `mdl/executor/validate_flow_member_refs.go` (scriptMemberModel, listMember), `mdl/executor/validate_member_refs.go` (resolveMemberOnEntity/nearMembers delegate)", "insight": "Make the builder refuse with the check's verdict and the check's words, instead of adding a check that predicts a builder which fabricates: one walk (memberVerdictOver) over a per-link interface, read from the backend by exec and from script-over-backend by check — the script overlay only changes how a LINK is read, never the walk, which is the duplicate-resolver-drift remedy taken literally. System dates are the trap: a sort reads `createdDate` where the root stores it (clean), but an aggregate cannot — `maximum $l by createdDate` is CE0226 'Entity ... does not contain attribute ...' even where stored. That was first measured as clean because the same app had a CE1613: **mx check reports neither CE0117 nor CE0226 while any CE1613 exists** — re-measure every control in an app with no other error", "fix": "find/filter by member, sort and aggregate refuse a bare member established as missing (exec without the preflight says exactly what check says, asserted); aggregates qualify an inherited attribute with its declaring entity; an aggregate over a system date is refused naming CE0226", "refs": ["ako/mxcli#1646", "ako/mxcli#1637", "ako/mxcli#1629"], "ce": ["CE1613", "CE0226"]}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"area": "mdl/executor", "date": "2026-10-26", "symptom": "A member that reuses a name already on the entity's generalization chain passed `check -p` and exec, and mxbuild rejected it as CE0069 \"Duplicate member name 'X'.\" — an attribute on a specialization named like an inherited attribute (`HR.Emp extends Other.Person (Name: …)`), a cross-module association `HR.Person_Dept from HR.Emp` beside the inherited `Other.Person_Dept`, `extends System.User (Name: …)` / `(UserRoles: …)`, `alter entity <generalization> add attribute` of a name a specialization has, a rename into such a name, and an entity's own attribute and association of one name", "cause": "Nothing decided 'this entity already has a member of that name' along the chain. The only CE0069 guard was dropInheritedTargets inside bulk ALTER ENTITIES, which drops a child when its parent is also a target and does not look outside the target set; create entity, alter entity add/rename attribute, create association, rename association and `set owner both` checked only the entity's own attribute list or the module's association names", "file": "`mdl/executor/member_name_clash.go` (memberWorld, memberNameClash — the one verdict; refuseMemberNameClash in execCreateEntity, execAlterEntity add/rename, execAlterEntities before any write, execCreateAssociation, execAlterAssociation set owner both, execRenameAssociation; validateMemberNameClashes in the check -p reference pass; scriptMemberNameClashes as MDL-MEMBER01 in project-free ValidateProgram)", "insight": "Measure the rule's edges before writing it, in one app with no other error — 17 shapes in a single mx check run on 11.13.0 settled it: CE0069 covers attribute/attribute, association/association, attribute/association BOTH ways, two levels up, System ancestors' attributes AND associations (System.User.Name, System.UserRoles, System.FileDocument.Size), the DOWNWARD direction (generalization gains a specialization's member), renames, and an entity's own attribute vs own association. An association is a member of its FROM entity only — of its TO entity too under owner Both (reference and reference set); an attribute on the TO end of an owner-Default association does not collide. Names differing only in case do NOT collide, so the comparison is exact, unlike validateBusinessEventInheritedMembers. Check must replay the script in order over the project's members (create, alter add/drop/rename, create/drop/rename association, rename/move entity): judging against the stored model alone refuses a drop-then-re-add and misses the ordinary one-script case, and a child created before its lazily-resolved parent is only caught by also walking DOWN from the parent when it is created. A refusal reports only clashes the statement introduces (a re-run `create or modify` of an unchanged entity is not judged), and the bulk path is judged for every target before the first write so a refusal leaves nothing behind", "refs": ["ako/mxcli#1645"], "ce": ["CE0069"], "rules": ["MDL-MEMBER01"]}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"area": "mdl/executor", "date": "2026-10-26", "symptom": "A page parameter named `Context` (`Params: ( $Context: MyFirstModule.Sale )`) passed `check` and exec, and mx check reported CE7247 \"The name 'Context' is a reserved word.\" at Page parameter 'Context' — after the page and its callers were written. The same for a snippet, Java action or JavaScript action parameter named with any platform-reserved word (`Type`, `Id`, `Owner`, `CurrentUser`, a Java keyword such as `Class`), and for anything named `Object`", "cause": "The platform reserved-word list (types.PlatformReservedWords) was applied to attribute names (MDL021) and enumeration values (MDL010) only; no validator looked at parameter names. The list also lacked `object`, which is CE7247 everywhere the list applies", "file": "`mdl/executor/validate_reserved_parameter_names.go` (validateReservedParameterNames, MDL-PARAM01, wired into ValidateProgram; refuseReservedParameterNames in execCreatePageV3, execCreateSnippetV3, execAlterPage, execCreateJavaAction, execCreateJavaScriptAction), `mdl/types/reserved_words.go` (+`object`)", "insight": "Measure which list applies WHERE, before reusing one: ~120 names × 6 contexts in three mx check runs on 11.13.0. Pages, snippets, Java actions and JavaScript actions share the attribute list exactly (case-insensitive, quoted refused) — every listed name tried was CE7247, every unlisted one clean, except `Object`, which nobody had listed because it is not a Java keyword (also CE7247 on an attribute, an enumeration value and an ENTITY). Microflow and nanoflow parameters and variables do NOT use it — `$Context`, `$Type`, `$Class` build clean there — and are refused for a different set: the system variables (CE0110 \"Variable name 'latestError' is reserved.\" for currentObject/latestError/latestHttpResponse/latestSoapFault/currentSession/currentDeviceType, parameters and variables) and CE0039 for a variable named CurrentUser — not covered by this fix. Page VARIABLES named Context/Type/Object build clean too. Extending the shared list rather than adding a parallel one made MDL021/MDL010 pick up `Object` for free", "refs": ["ako/mxcli#1651", "mendixlabs/mxcli#1642"], "ce": ["CE7247"], "rules": ["MDL-PARAM01", "MDL021", "MDL010"]}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"area": "mdl/grammar", "date": "2026-10-26", "symptom": "ako/mxcli#1685: `retrieve $Items from Shop.Item where [Total > @Shop.C_Limit];` was a parse error (`mismatched input 'Shop' expecting ']'`), read as 'MDL cannot write a constant in an XPath'. Worse, silent: a page data source written `where Total > @Shop.C_Limit` stored `[Total > ]`; describe of any stored constraint holding a constant on an entity with an enum attribute printed `where Total > @;`; and a long one written in the quoted form was laid out and STORED as `[Total > @]`", "cause": "No XPath rule for a constant: `@` (AT) was admitted by the negated xpathWord set as a one-word name. ParseXPathConstraint (error listeners off) recovered past the module name and returned ok with IdentifierExpr \"@\", so every re-render of the parse (enum enrichment, FormatXPathConstraint, check's member walk, which reported an attribute named \"@\") lost the constant; separately the page-side xpathExprToString had no ConstantRefExpr case and rendered \"\" for the expression-grammar form", "file": "mdl/grammar/domains/MDLPage.g4 (xpathConstantRef, AT out of xpathWord), mdl/visitor/visitor_xpath.go (buildXPathConstantRef), mdl/visitor/visitor_page_v3.go (xpathExprToString), mdl/executor/validate_xpath_constants.go (MDL-XPATH03), validate_user_role_tokens.go (walkXPathSlots)", "fix": "`@Module.Name` is an xpathValueExpr (a value, not a step) and AT is no longer a name word; the visitor builds ast.ConstantRefExpr and the page renderer prints it. check refuses a constant in any XPath slot (MDL-XPATH03, ValidateProgram, no project needed) with the variable workaround; excluded flows warn", "test": "mdl/visitor/visitor_xpath_constant_test.go, mdl/executor/validate_xpath_constants_test.go, mdl/executor/xpath_constant_1685_test.go (PedApp: stored verbatim in retrieve, list view and access rule, describe -> exec writes nothing, rename constant rewrites all three). Reverting the grammar fails every test with the reported parse error and `[Total > @]`; reverting only the renderer case gives `[Total > ]`", "insight": "The issue's premise was wrong and only a measurement showed it: mxbuild 11.13.0 rejects `@Mod.C` in a retrieve (Integer, String, Boolean constant), a list view data source, an access rule and a user task's targeting with CE0161, and an UNKNOWN constant is the same CE0161 rather than CE1613 (11.15.0 agrees for the retrieve) - XPath never resolves the name; the variable form is clean. So the right fix for this parse error was to parse it and REFUSE it with the workaround, not to store it. The Mendix runtime's MxPath grammar does have a CONSTANT token (com.mendix.languages.jar), which is why the syntax looks plausible. Access-rule XPaths are not validated by mx check at all while project security is Off - a garbage `[Total >]` passes - so measure access rules under Production security, with a bad-constraint control. A lenient parse that returns ok is not evidence it read everything: grep a renderer for `default: return \"\"` before trusting a new AST node to round-trip", "refs": ["ako/mxcli#1685", "ako/mxcli#1671"], "ce": ["CE0161"], "rules": ["MDL-XPATH03"]}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"area": "mdl/visitor", "date": "2026-10-27", "symptom": "`alter workflow M.W { set (Annotation: '…'); }` was refused (\"a workflow has no property Annotation\"), and so was `set (Annotation: '…') on X`, though CREATE writes both notes (header `annotation '…'`, `@annotation '…'`); the refusal did not name the CREATE spellings, so an eval agent concluded workflow notes were unsupported. Written without the braces it was diagnosed as \"`set (annotation` is a session command\". Underneath: the header clause wrote the workflow's own Annotation, which Studio Pro shows nowhere, and describe never read the start activity's, so a Studio Pro note on the start was dropped by describe -> replay", "cause": "The ALTER key tables and the wfmutator setters were never given the notes CREATE had since ako/mxcli#707, and #707 put the start's note in Workflows$Workflow.Annotation: both it and WorkflowActivity.annotation are in the metamodel from 9.15.0, so the metamodel could not say which one Studio Pro shows. The braceless form: the old per-action grammar takes `set display '…'` unbraced, so the parser tried it, failed at `(`, and recovered into the session command `set <name> = <value>`", "file": "`mdl/visitor/visitor_alter_workflow.go` (keys), `mdl/backend/wfmutator/mutator.go` (`setStartNote`, `setNote`), `mdl/backend/modelsdk/workflow_read.go` (start activity's note), `sdk/workflows/workflow.go` (`StartNote`), `mdl/executor/cmd_workflows.go` + `cmd_workflows_write.go` (describe, CREATE), `mdl/visitor/visitor.go` (braces hint), `mdl/visitor/visitor_session_commands.go` (stand down on a failed line)", "fix": "The start's note is StartWorkflowActivity.Annotation: CREATE writes the header clause there, the reader reads it (and the workflow's own, for workflows written before), describe prints StartNote(). `Annotation` is an ALTER workflow key (the start's note; it also clears the workflow's own) and an activity key; `''` stores null; `on Start` is the workflow key; an end is refused. setNote writes {$ID, $Type: Workflows$Annotation, Description}, keeps an edited note's $ID, and refuses a document with no Annotation slot. The braceless line gets a hint showing the braced statement; the session-command refusal stands down on a line that already has a syntax error", "insight": "**When the metamodel has two slots for one concept, ask the editor which one it draws.** Workflow.annotation and the start activity's are both in the 9.15.0 metamodel; the answer was in mxbuild's own `ide-client/workflow-editor/editor.js` — four views render the note badge (ActivityView, BoundaryEventView, EventSubProcessView, StartActivityView), each from its own element, and the Workflow view renders none. Ten minutes of grep settled what #707 had to guess, and turned an ALTER feature into a describe data-loss fix: a Studio Pro start note was silently dropped by every describe -> replay. **A recovery-made statement's error outranks the real one in the reader's eyes** because it reads as a diagnosis: guard visitor errors on recovered trees with `syntaxErrorLines`. **A missing slot is information** — every type with a note stores `Annotation: null` when empty (11.15.0), so refuse rather than add one", "refs": ["ako/mxcli#1717", "ako/mxcli#707"]}
Loading
Loading