Skip to content

Sync ako/mxcli: references to missing members, reserved and inherited names, constants in XPath, workflow notes - #1661

Merged
ako merged 16 commits into
mendixlabs:mainfrom
ako:main
Oct 27, 2026
Merged

ako merged 16 commits into
mendixlabs:mainfrom
ako:main

Conversation

@ako

@ako ako commented Oct 27, 2026

Copy link
Copy Markdown
Collaborator

Syncs ako/mxcli:main into upstream: 10 commits from fork PRs ako#1738, ako#1739, ako#1740 and ako#1742.

Fixes #1642 and #1650.

References to members that don't exist (#1650)

These all passed check -p and exec, then failed mx check:

  • $Var/<member> for a member the entity lacks, including the system id (check passes $Var/<member> for a member the entity does not have, incl. the system id (mx check CE0117; check/exec pass) #1650). toString($User/id), $A/id = $B/id and $User/Bogus passed the type pass, then gave CE0117.
  • find, filter, sort and aggregate by a member the list's entity lacks. find $l by Numbr = '42' gave CE1613 once per activity. A retrieve constraint was already checked for this, and list operations now are too. An aggregate by an inherited attribute was mishandled as well.
  • A bare association in a retrieve. retrieve $C from $O/Ord_Cust; was stored as .Ord_Cust, with an empty module, and gave CE1613.

Names Mendix rejects (#1642)

  • A page parameter named Context is refused (A page parameter named Context passes check and exec; mx check then reports CE7247 "The name 'Context' is a reserved word" #1642). It gave CE7247 "reserved word", after the page and its callers had been written. The platform reserved-word list now binds page, snippet, Java action and JavaScript action parameters (MDL-PARAM01).
  • A member that reuses a name on its generalization chain is refused. mxbuild rejects these with CE0069. That covers:
    • a specialization member named like an inherited one;
    • a generalization gaining a name a specialization already has;
    • a rename into such a name;
    • an attribute and an association of one name.

A constant in an XPath constraint

  • where [Total > @Shop.C_Limit] parses. It used to be a parse error.
  • Three silent losses behind it are fixed:
    • A data source written where Total > @Shop.C_Limit stored [Total > ].
    • describe lost a stored constant.
    • A re-render dropped it.
  • Where Mendix rejects a constant, it is refused as CE0161.

Workflows

  • New: alter workflow sets a workflow's or an activity's note. set (Annotation: '…') and set (Annotation: '…') on X used to be refused.
  • The note on a workflow's start is written to and read from the start activity, which is where Studio Pro shows it. Before, it went to the workflow's own Annotation.
  • A bare set in alter workflow names the braces it needs.

Docs

  • CLAUDE.md stays within its 28,000-byte context budget. The reserved-names note moved to its canonical home

ako and others added 16 commits October 26, 2026 22:21
…s it on re-render (#1685)

`retrieve $Items from Shop.Item where [Total > @Shop.C_Limit];` failed with
"mismatched input 'Shop' expecting ']'", which reads as a gap in MDL. Three
silent losses sat behind it: a data source written `where Total > @Shop.C_Limit`
stored `[Total > ]`; describe of a stored constraint holding a constant, on an
entity with an enumeration attribute, printed `where Total > @;`; and a long
one written in the quoted form was laid out and stored as `[Total > @]`.

Cause: the XPath grammar had no constant. `@` was admitted by the negated
xpathWord set as a one-word name, so the strict parse failed at the module
name, and ParseXPathConstraint (error listeners off) recovered past it and
returned ok with an attribute called "@" — every re-render of that parse
(enum enrichment, FormatXPathConstraint, check's member walk) dropped the
constant. The page-side xpathExprToString had no ConstantRefExpr case and
rendered "" for the expression-grammar form.

The issue expected the constant to be stored and built. Measured, mxbuild
rejects it: on 11.13.0, each case alone in a fresh app, CE0161 "Error(s) in
XPath constraint." for an Integer, String and Boolean constant in a retrieve,
in a list view's database source, in an access rule (production security —
with security off mx check validates no access rule, a garbage `[Total >]`
passes) and in a user task's targeting; an unknown constant is the same
CE0161, not CE1613. The retrieve is CE0161 on 11.15.0 too. The control,
`declare $Limit Integer = @Shop.C_Limit;` then `[Total > $Limit]`, is clean.

Fix: `@Module.Name` is an xpathValueExpr (xpathConstantRef — a value, not a
path step) and AT is out of xpathWord; the visitor builds ast.ConstantRefExpr
and the page renderer prints it, so every slot stores the constraint as
written and describe prints it back. `mxcli check` (ValidateProgram, so exec
too; no project needed) refuses a constant in any XPath slot as MDL-XPATH03
with the variable workaround; an excluded flow warns. walkXPathConstraints
gains a sibling that names the slot.

Verification: visitor, check and executor tests; the PedApp test writes the
constraint into a retrieve, a list view and an access rule, describes all
three back, re-executes the descriptions with no file changed, and renames
the constant through them ("Updated 3 reference(s)"). Reverting the grammar
fails every test with the reported parse error and `[Total > @]`; reverting
only the renderer case gives `[Total > ]`; stubbing the check fails the
refusal tests. Real 11.13.0 app: baseline describe printed `where Total > @;`
for both retrieves, fixed describe prints the constraints and describe -> exec
reports 5 documents in sync; `rename constant` updated 5 references in 4
documents, and mx check is the same five CE0161 before and after the rename
with the variable control clean. TestMxCheck_DoctypeScripts passes for the
microflow, nanoflow, page, security, xpath, workflow and offline-sync scripts.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…1690)

`retrieve $C from $O/Ord_Cust;` - an association path written with the
association's short name - passed `check -p --references` and exec and
was stored as the association ".Ord_Cust": mx check CE1613 "The selected
association '.Ord_Cust' no longer exists.", and describe printed
`$O/.Ord_Cust`, which does not parse.

Cause: addRetrieveAction joined the AST's empty Module to the name, and
nothing on the check side looked at a bare retrieve association.
MDL-RETRIEVE03's qualifyAssoc guessed the start entity's module - a
second answer to the same question.

Fix: one resolver, resolveBareAssociation (bare_association.go), a
function of plain data - the name, the start entity's generalization
chain, and an association catalog with both ends of every association.
A bare name resolves when exactly one association of that name has an
end on the chain (either end; a specialization inherits) and is stored
qualified; none or several is refused, with the qualified spelling to
write. The builder feeds it the project as the earlier statements left
it, check --references the project plus the associations declared
before the flow (validate_flow_member_refs.go), so the two agree; exec
also refuses a bare name it cannot resolve rather than writing ".Name".
The describe speller and MDL-RETRIEVE03 ask the same resolver, so a
re-run of the script reports "Unchanged microflow".

Verification: retrieve_bare_association_1690_test.go (PedApp, check and
exec agree; stored unit bytes; describe; re-run unchanged; refusals;
speller). Reverting the fix fails every case with ".Ord_Cust" stored and
check/exec accepting. Real 11.13.0 app: the reported repro went from
CE1613 to 0 errors with "Shop.Ord_Cust" stored and described; owner
side, TO side, specialization, cross-module inherited and
Account/UserRoles -> System.UserRoles all resolve and build at 0 errors;
a missing and an ambiguous name are refused by check, exec and
exec --no-check. No doctype or bug-test script changes verdict.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…heck and exec (#1646)

`find $l by Numbr = '42'`, and filter / sort / sum by a member the
list's entity does not have, passed `check -p` and exec; mx check then
reported CE1613 "The selected attribute 'Shop.Order.Numbr' no longer
exists." once per activity. An aggregate by an INHERITED attribute was
broken the same way: `sum $sl by Total` was written as
"Shop.SubOrd.Total" (CE1613).

Cause: the builder's member resolution (resolveMemberChange via
resolveListOperationMember, the sort branch, the aggregate) fell back to
writing `<entity>.<name>` when the name resolved to nothing, and the
aggregate never walked the generalization chain. check --references
judged CREATE/CHANGE members and retrieve constraints, but no list
operation member.

Fix: one chain walk, memberVerdictOver (member_verdict.go), over a
per-link interface - read from the backend by the builder, from the
script over the backend by check - so the script overlay changes how a
link is read, never the rule. The builder refuses a member established
as missing with listMemberRefusal, the message check reports, so exec
without the preflight says what check says; resolveMemberOnEntity now
delegates to the same walk. The aggregate qualifies an attribute with
the entity that declares it. A sort may read createdDate/changedDate
where the root stores them; an aggregate may not (CE0226, measured).

Verification: list_member_refs_1646_test.go (PedApp, check and exec
agree): the four reported shapes plus maximum, bare `owner` and an
aggregate over createdDate are refused; own/inherited attributes, bare
and inherited cross-module associations, System.owner, sorts on a
stored system date, an untyped list and a member the script adds first
stay accepted; the inherited aggregate stores the declaring entity.
Reverting the fix fails the refusal tests with check and exec accepting
and the aggregate storing B1646.SubOrd.Total. Real 11.13.0 app: the
repro went from 4 x CE1613 to refused by check and exec; the controls
build at 0 errors. No doctype or bug-test script changes verdict.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…id (#1697)

`toString($User/id)`, `$A/id = $B/id` and `$User/Bogus` passed
`check -p --references` - the type pass said "Expression types OK" -
and exec wrote them; mx check reported CE0117 "Error(s) in expression."
(mirror of mendixlabs#1650).

Cause: nothing resolved the member steps of an expression's `$Var/...`
path against the variable's entity. The type pass infers an unknown
attribute as KindUnknown and stays silent, and it is not part of exec's
preflight anyway.

Fix: the reference pass - which check --references and exec's preflight
both run - follows every `$Var/...` path in a flow statement's
expressions through the same member verdict as #1646, across
association hops: an attribute the entity (or anything it extends) does
not have, a bare association, an association that does not exist or
does not start at the entity are reported, and `id` gets the
compare-the-objects / CommunityCommons.getGUID hint. The hop's target
entity may follow the association qualified or bare (the builder
qualifies a bare one, #829). Paths are collected by reflecting over each
statement's fields, so a statement kind added later is covered; nested
bodies are left to the typed walk.

Verification: expression_member_refs_1697_test.go (PedApp): the twelve
shapes measured as CE0117 on 11.13.0 are reported, the ten measured
clean are not (plus the bare target entity step), the System.User repro
and a loop/IF body. Reverting
the fix fails every report case. Real 11.13.0 app: the repro went from
2 x CE0117 to refused by check and exec; the controls build at 0
errors. Every CE0117 shape was measured in an app with no other error -
a CE1613 elsewhere suppresses them all. No doctype or bug-test script
changes verdict.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… and exec (#1645)

A specialization's attribute or association named like a member it
inherits, a generalization gaining a member a specialization already has,
a rename into such a name, and an entity's own attribute and association
of one name all passed `check -p` and exec; mxbuild rejected the model
with CE0069 "Duplicate member 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 looks only within the target set; the single
statements checked the entity's own attributes or the module's
association names.

Fix: one verdict, memberNameClash, over a member world (each entity's
generalization and attributes, each association's ends):
- exec calls it against the project before writing, in create entity,
  alter entity add/rename attribute, create association, alter
  association set owner both and rename association; bulk ALTER ENTITIES
  judges every target before its first write;
- check -p replays the script over the project's members in statement
  order (so a drop-then-re-add passes and a one-script parent/child pair
  is caught), judging each statement with the same verdict;
- `check` without a project runs the same replay over the System module
  from modelsdk/meta (MDL-MEMBER01), so the issue's scripts fail even
  there.
The message names the member already holding the name, its declaring
entity, and the CE0069 it prevents.

Measured on mx 11.13.0 (17 shapes in one app, then 4 more): CE0069 for
attribute/attribute, association/association, both mixed directions, two
levels up, System.User.Name, System.UserRoles, System.FileDocument.Size,
the downward direction, attribute and association renames, and an
entity's own attribute vs association; an association is a member of its
TO entity only under owner Both. Clean: names differing only in case,
siblings, an unrelated entity, an owner-Default TO-side attribute.

Verification: PedApp check/exec agreement tests for every shape plus
controls, and a project-free test; reverting the wiring fails all nine
(controls still pass), reverting only the check pass fails check's half.
Real 11.13 app: baseline wrote 16 CE0069 models; fixed, check and exec
refuse those 16, the 5 clean shapes are written, mx check 0 errors.
Doctype sweep (81 scripts, check with and without -p) and 238
domain-model bug-test/skill scripts: identical to main.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Mirror of mendixlabs#1642. A page parameter named `Context` passed
check and exec, and mx check reported CE7247 "The name 'Context' is a
reserved word." — after the page and its callers had been written.

Cause: the platform reserved-word list (types.PlatformReservedWords) was
applied to attribute names (MDL021) and enumeration values (MDL010) only;
nothing looked at parameter names. The list also lacked `object`.

Fix: MDL-PARAM01 checks the parameters of create page / create snippet /
alter page|snippet add parameters / create java action / create
javascript action against that same list (case-insensitive, quotes
stripped), in ValidateProgram (check, exec's pre-flight) and in the five
exec handlers, so --no-check cannot write it either. The message names
the CE7247 and suggests the parameter's entity as a name ($Sale). `object`
is added to the shared list, so MDL021/MDL010 cover it too.

Measured on mx 11.13.0, ~120 names over six contexts: pages, snippets,
Java and JavaScript actions take exactly the attribute list — every
listed name tried is CE7247 in any case and quoted, every unlisted one
clean (incl. currentObject, latestError, String, System, User, List,
record) — except `Object`, CE7247 there and on an attribute, an
enumeration value and an entity. Microflow and nanoflow parameters and
page variables do not use this list ($Context, $Type, $Class build
clean); flows refuse the system-variable names instead (CE0110/CE0039),
which this does not cover.

Verification: unit tests per context plus controls (another name, a
system-variable name, microflow/nanoflow parameters), a PedApp
check/exec agreement test; reverting the wiring fails all of them,
reverting only the page handler fails exec's half. Real 11.13 app: the 61
probe scripts baseline mx check reported CE7247 for (62 parameters) and
the reporter's page are refused by check and exec, the other 109 are
written, mx check 0 errors. Doctype sweep (81 scripts,
check with and without -p) and 412 related bug-test scripts: identical
to main.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The reserved-names note grew CLAUDE.md past the budget
TestRepoClaudeMDStaysWithinItsContextBudget enforces (28224 bytes). Keep the
facts it carries — Object is reserved, the list binds page/snippet/Java/
JavaScript action parameters (MDL-PARAM01), and where the list lives — in
fewer words; the microflow-parameter nuance is in the skills and the finding.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ences

# Conflicts:
#	mdl/executor/validate_member_refs.go
fix: a constant in an XPath constraint — parse it, refuse it (CE0161), stop losing it (#1685)
fix: names Mendix rejects pass check — inherited member names (CE0069), reserved parameter names (CE7247) (#1645, #1651)
fix: references to members/associations that do not exist pass check (#1690, #1646, #1697)
The header's `annotation '…'` is the note on the workflow's start. Since
#707 mxcli wrote it to the workflow's own Annotation, and
describe read only that. Studio Pro shows the start's note from the start
activity: mxbuild 11.15.0's workflow editor (ide-client/workflow-editor)
draws note badges in four views, ActivityView, BoundaryEventView,
EventSubProcessView and StartActivityView. Each draws its own element's
annotation, and the Workflow view draws none. So a note mxcli wrote
showed nowhere in Studio Pro, and a Studio Pro author's note on the start
was dropped by describe → replay. Both slots are in the metamodel from
9.15.0, which is why the metamodel could not settle it.

CREATE now writes the header clause to the start activity. The reader
reads the start activity's note. describe prints StartNote(): the start
activity's note, else the workflow's own for workflows written before
this change.

Measured on 11.15.0:
- CREATE stores the note on the start activity and null on the workflow.
- A workflow written by the previous build describes the same; its replay
  moves the note to the start once ("Modified", then "Unchanged").
- mx check is clean.

Refs #1717

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019xfGeNtrQ5BJrH2Ew64rt5
`alter workflow M.W { set (Annotation: '…'); }` was refused with "a
workflow has no property Annotation", and so was `set (Annotation: '…')
on X`. CREATE writes both notes, but the refusal never named its
spellings, so an eval agent concluded that workflow notes were
unsupported.

`Annotation` is now a workflow key and an activity key:
- The workflow key sets the start activity's note, which is the header's
  `annotation '…'`. It also clears the workflow's own Annotation, so
  describe does not find a stale note there.
- `on X` sets the `@annotation '…'` before activity X. `on Start` is the
  workflow key.
- `''` stores null and removes the note.
- An end is refused: Studio Pro's editor draws no note on an end.

The note is written as Studio Pro's exact set {$ID, $Type:
Workflows$Annotation, Description}. An edited note keeps its $ID. A
document stored without the Annotation slot is refused rather than given
one. MCP refuses the key, as it refuses every unsupported property.

Measured on 11.15.0: after the ALTER the project checks clean, describe
prints the notes in CREATE's spellings, and replaying describe reports
"Unchanged workflow".

Refs #1717

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019xfGeNtrQ5BJrH2Ew64rt5
`alter workflow M.W set (Annotation: '…');` ended its errors with
"`set (annotation` is a session command". The old per-action form takes
`set display '…'` without braces, so the parser tried that form, failed
at the parenthesis and recovered into the session command `set <name> =
<value>`. Its refusal read as the diagnosis, and no error mentioned the
braces.

A line of that shape now gets a hint that shows the braced statement.
The session-command refusal stands down on a line the parser already
reported, because the syntax error is the one to act on. A session
command on a line of its own is still refused.

Fixes #1717

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019xfGeNtrQ5BJrH2Ew64rt5
feat(workflows): set workflow and activity notes in ALTER; the start's note is the start activity's
engalar pushed a commit to engalar/mxcli that referenced this pull request Oct 27, 2026
…l7uoj

fix: misleading diagnostics — MDL031 on CASE columns, and a parenless routine header
@ako
ako merged commit 97e1cce into mendixlabs:main Oct 27, 2026
15 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

A page parameter named Context passes check and exec; mx check then reports CE7247 "The name 'Context' is a reserved word"

2 participants