Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
1 change: 1 addition & 0 deletions .claude/skills/fix-issue/findings/mdl-executor.jsonl
Original file line number Diff line number Diff line change
Expand Up @@ -876,4 +876,5 @@
{"area": "mdl/executor", "date": "2026-10-07", "symptom": "A button added to a snippet with `alter snippet … insert` (or replace) calling `call microflow M.F(P = $P)` with a snippet parameter fails mxbuild with CE0115 \"The arguments that are passed to microflow 'M.F' do not match the expected parameters and need to be refreshed\" on that button only; the same button in CREATE SNIPPET or via ALTER SNIPPET SET is clean", "cause": "`buildWidgetsFromAST` and `buildColumnSpecsFromAST` (mdl/executor/cmd_alter_page.go) seeded paramScope and localVariables from the mutator but not `isSnippet`, so `classifyFlowArgValue` returned kind \"parameter\" and the Forms$PageVariable named PageParameter instead of SnippetParameter", "file": "`mdl/executor/cmd_alter_page.go` (isSnippet from mutator.ContainerType() in both builders)", "insight": "**A pageBuilder built outside CREATE needs THREE scope fields, not two** — paramScope, localVariables and isSnippet; #1317 found the SET builder missing all three and this one missing the last. When adding a builder, copy the seeding from convertASTAction rather than from memory. **Unlike the Expression-form defect (#1140/#1317), mxbuild DOES catch the wrong slot** — CE0115 rather than CE1571 — so one script exercising CREATE, INSERT and SET on the same snippet with `mx check` is a complete A/B: only the broken path's button is named. Verified on 11.12.2: 1 error before, 0 after", "refs": ["mendixlabs/mxcli#1317", "mendixlabs/mxcli#1140"], "ce": ["CE0115"]}
{"area": "mdl/executor", "date": "2026-10-07", "symptom": "`create or modify microflow M.F () begin @excluded declare $Variable Boolean = false; return; end;` reports success but the activity is written enabled (ActionActivity Disabled=false); DESCRIBE prints no @excluded, so the round trip of any microflow with a disabled activity re-enables it. Same for every action statement (log, call, change, ...)", "cause": "`flowBuilder.mergeStatementAnnotations` copies ast.ActivityAnnotations into fb.pendingAnnotations field by field and never copied `Excluded`. The visitor set it, applyAnnotations honoured it (activity.Disabled = true), the backend writes Disabled, the describer emits @excluded — every layer was right except the hand-written merge between them", "file": "`mdl/executor/cmd_microflows_builder_annotations.go` (mergeStatementAnnotations)", "insight": "**Bisect by layer with the cheapest probe at each**: a visitor probe showed the AST carried Annotations, `bson dump` showed Disabled=false on disk, so the loss was between AST and write — and grepping `Disabled` showed exactly one setter, which was fine, pointing at its input. A field-by-field copy of a struct is a silent-drop site for every field added after it was written; the guard is a reflection test that sets every field and asserts it survives, with an explicit allowlist (Start, Invalid*, UnknownNames) for fields consumed elsewhere. Do not chase the backend or describer — both were already correct", "refs": ["mendixlabs/mxcli#1328"]}
{"area": "mdl/executor", "date": "2026-10-07", "symptom": "`set $N = 1;` on a microflow/nanoflow/rule PARAMETER ($N: Integer or String) passes `mxcli check` and `exec`; mx check reports [CE7247] \"Parameter 'N' cannot be changed.\" at the Change variable activity", "cause": "Nothing modelled that a Change variable activity cannot target a parameter; the variable-kind tracking treated a primitive parameter like any declared primitive. Found while measuring MDL-SET01's object producers (#1323): the object parameter's CE7247 came back with a different wording, which was the tell that the restriction is on parameters, not on types", "file": "`mdl/executor/validate_microflow_set_object.go` (`checkSetOnObjectVariable` primitive-parameter branch, `setTargetViolations`); rules via `validate_program.go` (check) and `rule_validation.go` `validateRuleSetTargets` (exec, `cmd_rules_create.go`); MDL-SET01 added to `execEnforcedMicroflowRules` in `validate.go`", "insight": "When one measurement returns a different MESSAGE under the same CE code, test the cause the message names — here 'Parameter … cannot be changed' fired for an Integer too, a whole adjacent class. Measure the neighbours before writing the rule: list parameters (`set` = Change list Replace, `add`) and member changes on parameters BUILD, so the rule is 'any non-list parameter', not 'any parameter'. Rules never run ValidateMicroflow: plain check needs a ValidateProgram hook and exec a gate in cmd_rules_create; putting it in validateRule instead would double-print under --references, which runs both", "refs": ["mendixlabs/mxcli#1323"], "ce": ["CE7247"], "rules": ["MDL-SET01"]}
{"area": "mdl-executor", "date": "2026-10-07", "refs": ["mendixlabs/mxcli#1324"], "symptom": "A widget directly inside data view `dvP` that reads `$dvP` OUTSIDE an action — a nested data view's `DataSource: microflow M.F(Gate = $dvP)`, `Visible: $dvP/Name != ''`, `Editable: …`, `DynamicClasses: …`, or a nested list's `database from M.E where [Name = $dvP/Name]` — passes `check` and `exec`, then `mx check` reports `[CE0117] \"Error(s) in expression.\"` at the widget (CE0161 \"Error(s) in XPath constraint.\" for the `where`)", "cause": "MDL-BUTTON02 (the #1324 fix) only looked at action arguments, though every slot evaluated in the widget's enclosing context has the same scope: a container's widget-name variable exists only one data container below it", "file": "`mdl/executor/validate_page_button_context.go` (`checkOwnContainerName` now takes the widget and walks action args, `GetDataSource().Args` / `.Where`, and `ownNameExprProps`)", "insight": "**A scope rule belongs to the context, not to the slot the report happened to use.** The follow-up question that settled it was one probe per slot with a control one data view deeper: five of five slots failed in the own context and all five controls built clean, so the rule is 'anything evaluated in this widget's enclosing context' — enumerate those slots rather than wait for one report each. Two things that would have wasted a build: (1) `Visible:`/`Editable:` arrive in the AST as `VisibleIf`/`EditableIf` strings (dump `w.Properties` before keying on what the author wrote), and (2) text-template parameters (`ContentParams ({1} = $dvP/Name)`) are already refused by MDL-WIDGET24 for an unrelated reason (a template parameter is an attribute name, not a variable path), so they are not a scope case at all. The XPath slot reports a different code (CE0161), which is why the message carries the code per slot. Control: stubbing the data-source and property slots fails the new test with `flagged \"\"`; the `$currentObject` rewrite of all five builds at 0 errors. Repro `mdl-examples/bug-tests/1324-own-data-container-name-outside-actions.fail.mdl`"}
{"area": "mdl/executor", "date": "2026-10-07", "symptom": "`describe structure depth 2|3` / `mxcli structure -d 3` never annotates a page with its data widgets (`Page M.P [DataView<Customer>, …]`), on any project and with a full catalog — every page prints bare. Evora Factory Management: 0 of 54 pages annotated, although CATALOG.widgets holds their data views and grids.", "cause": "structurePages queried `widgets … where ParentWidget = ''`; widgets_data has never had a ParentWidget column (the tree position, added later, is ParentWidgetId + Depth). The query failed and the error was discarded (`if err == nil { … }`), so the annotation was dead code from the initial commit. Every other catalog query in cmd_structure.go swallowed errors the same way (`if err != nil || len(rows) == 0 { return }`).", "file": "`mdl/executor/cmd_structure.go` (`structureQuery`, `structurePages`, `queryCountByModule`, `shortWidgetType`), `mdl/executor/cmd_structure_page_widgets_test.go`", "insight": "A fixed SQL query against a table the builder owns can only fail through drift inside mxcli, never because of the user's project, so swallowing its error converts a schema mismatch into silent absence — the same shape as the depth-1 flow-count casing bug (#717), which a swallowed error also hid. Route such queries through one helper that returns the error; then a column rename fails the first test that runs the command against a real built catalog. That test must build the catalog with catalog.NewBuilder in FULL mode (SetFullMode(true)) over raw page BSON from GetRawUnitFunc — widgets_data is empty in fast mode, and a fast-mode test passes against the broken query because 'no widgets' and 'query failed' both print a bare page. Mock gotcha: the builder dereferences GetNavigation, whose mock default is (nil, nil), so stub it with an empty NavigationDocument. Semantics choice: 'top-level' cannot mean Depth = 0 — real pages wrap content in a layout grid, so a root filter lists almost nothing; list data widgets with no data-widget ancestor instead (walk ParentWidgetId). Measured on Evora after the fix: 45 of 54 pages annotated (181 of 212 with `all`), and the 9 bare pages have no data widget in CATALOG.widgets.", "refs": ["#717"]}
2 changes: 1 addition & 1 deletion .claude/skills/mendix/cheatsheet-errors/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -265,7 +265,7 @@ Run with `-p` for the fullest coverage.
| CE0104 | Action activity is unreachable | Code after RETURN |
| CE0105 | Must end with end event | Missing RETURN |
| CE0117 | Error in expression | Unqualified association path |
| CE0117 | …on a button inside a data container | The button passes the container it sits in by its widget name (`$dvGate` inside `dvGate`). That name is a variable only for containers nested below it; use `$currentObject` there. MDL-BUTTON02 |
| CE0117 | …on a button inside a data container | The button passes the container it sits in by its widget name (`$dvGate` inside `dvGate`). That name is a variable only for containers nested below it; use `$currentObject` there. Same for data-source arguments, `Visible:`, `Editable:`, `DynamicClasses:`, and (as CE0161) a nested list's XPath `where`. MDL-BUTTON02 |
| CE1571 | No argument selected for parameter | A microflow/nanoflow call with a parameter nothing fills — as a `datasource:` **or** an `action:`. Give it an argument (`action: call nanoflow M.NF(P = $value)`), or nest the widget in a data container of the parameter's type. `check -p` reports both |
| CE1571 | …in a control bar | A control bar is **not** row-scoped, so the grid's row does not fill it: pass the grid's selection (`$dgOrders`, with `Selection:` set) or move the widget into a column. `$currentObject` there is MDL-BUTTON01 |
| CE1834 | The 'Page' property is required | Workflow user task without a `page` — `check` flags MDL-WF01 |
Expand Down
5 changes: 4 additions & 1 deletion .claude/skills/mendix/create-page/reference/widgets.md
Original file line number Diff line number Diff line change
Expand Up @@ -1073,7 +1073,10 @@ view `dvOrder`'s object from a data view, list or grid nested inside it. In the
container's own context — directly inside it, through plain containers, or in the
control bar of a grid inside it — the object is `$currentObject`, and
`$dvOrder` is **CE0117** "Error(s) in expression." (`mxcli check` reports
MDL-BUTTON02). The same goes for a list view, gallery or grid read by its own
MDL-BUTTON02). That holds for every slot evaluated there, not only action
arguments: a nested widget's microflow data-source arguments, `Visible:`,
`Editable:` and `DynamicClasses:` (CE0117), and a nested list's XPath `where`
(**CE0161**). The same goes for a list view, gallery or grid read by its own
name from its item or row. A grid's own name *is* valid from its control bar —
that is the selection, above (mendixlabs/mxcli#1324).

Expand Down
2 changes: 1 addition & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
### Fixed

- **`set $Param = …` on a parameter is refused** — a Change variable cannot target a parameter, and mxbuild rejects it with CE7247 "Parameter 'N' cannot be changed." (measured on 11.14.0 for Integer and String parameters in a microflow, a nanoflow and a rule). `check` and `exec` passed it. MDL-SET01 now refuses it for every parameter but a list (`set` on a list parameter is a Change list Replace, which builds), and `exec` enforces the rule. Copy the parameter into a variable first: `declare $Value Integer = $N;`.
- **`check` refuses a button that passes its own data container by widget name** (mendixlabs/mxcli#1324) — `actionbutton btnOwn (Action: call microflow M.F(Gate = $dvGate))` directly inside data view `dvGate` passed `check --references` and `exec`, then `mx check` reported `[CE0117] "Error(s) in expression." at Action button 'btnOwn'`. A data container's name is a variable only for the containers nested below it; in its own context the object is `$currentObject`. Reported as **MDL-BUTTON02** (error, so `exec` refuses it), for data views, list views, galleries and data grids alike, including attribute paths (`$dvGate/Name`) and a control bar inside the container. A grid's own name from its control bar (the selection) and an enclosing container's name from a nested one are not flagged. The flagged set matches mxbuild 11.14.0's CE0117s widget for widget on a ten-button probe page.
- **`check` refuses a button that passes its own data container by widget name** (mendixlabs/mxcli#1324) — `actionbutton btnOwn (Action: call microflow M.F(Gate = $dvGate))` directly inside data view `dvGate` passed `check --references` and `exec`, then `mx check` reported `[CE0117] "Error(s) in expression." at Action button 'btnOwn'`. A data container's name is a variable only for the containers nested below it; in its own context the object is `$currentObject`. Reported as **MDL-BUTTON02** (error, so `exec` refuses it), for data views, list views, galleries and data grids alike, including attribute paths (`$dvGate/Name`) and a control bar inside the container. The rule covers every slot evaluated in that context, not only action arguments: a nested widget's microflow data-source arguments, `Visible:`, `Editable:` and `DynamicClasses:` (CE0117), and a nested list's XPath `where` (CE0161). A grid's own name from its control bar (the selection) and an enclosing container's name from a nested one are not flagged. The flagged set matches mxbuild 11.14.0's CE0117s widget for widget on a ten-button probe page.
- **`set $Obj = …` on an object variable is refused** (mendixlabs/mxcli#1323) — with both variables single objects (e.g. a Reference retrieved from its FROM entity), `set $Cursor = $Next;` passed `check --references` and `exec` and was written as a Change variable action, which mxbuild refuses with CE7247 "Variable 'Cursor' does not have a primitive type". Mendix has no action that reassigns an object variable, so `check` (MDL-SET01, for the objects it can see without a project), `check --references` and `exec` now refuse it and name the alternatives (a sub-microflow that returns the next object, `change $Obj (…)`). `check --references` now types a Reference retrieve from its FROM entity as the object it is. `set` on a list variable stays a Change list Replace (ako/mxcli#949).
- **`RENAME JAVA ACTION` renames the Java class, not just the file** — renaming `M.JA_Old` to `JA_New` moved the source to `JA_New.java` but left `public class JA_Old`, its constructor and its `toString` inside it, which javac rejects (`class JA_Old is public, should be declared in a file named JA_Old.java`). A full build hid it, because mxbuild regenerates the stub in its own copy; `run --local --watch` hot reload and IDEs compile the file on disk. Those three places now follow the rename exactly as mxbuild rewrites them; user code and extra code are left as written. Applies to `mxcli rename java-action` too.
- **`run --local --watch` starts on Mendix 10.24 and 11.6** — it waited the full web-client timeout (5 minutes) for a bundle that had finished in seconds, then failed with `web client watcher timed out`. The rollup runner those versions ship reports its status as `{"code":"SUCCESS"}` rather than the modern-web-bundler protocol mxcli was reading; both are now understood, including that runner's error reports.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
-- mendixlabs/mxcli#1324 follow-up: a data container's own name outside actions
--
-- NEGATIVE TEST (.fail.mdl) — EXPECTED to fail `mxcli check`.
-- `make check-mdl` inverts the exit code: an unexpected pass is a regression of
-- MDL-BUTTON02's non-action slots.
--
-- Symptom: `check` and `exec` were clean, then `mx check` (Mendix 11.14.0)
-- reported, for widgets placed directly inside data view dvP:
-- [CE0117] "Error(s) in expression." at Container 'cVisOwn'
-- and the same for a nested data view's microflow data-source argument,
-- `Editable:` and `DynamicClasses:`, plus CE0161 "Error(s) in XPath
-- constraint." for a nested list's `where`. These slots are evaluated in the
-- enclosing context, where dvP's object is $currentObject; `$dvP` is a
-- variable only one data container deeper (cVisOuter builds clean).

create module G52;
create persistent entity G52.Gate (Name: String(50));

create page G52.VisibilityPage (Title: 'Gate', Layout: Atlas_Core.PopupLayout, Params: ( $Gate: G52.Gate )) {
dataview dvP (DataSource: $Gate) {
container cVisOwn (Visible: $dvP/Name != '') {
dynamictext dtA (Content: 'a')
}
dataview dvMid (DataSource: $Gate) {
container cVisOuter (Visible: $dvP/Name != '') {
dynamictext dtB (Content: 'b')
}
}
}
};
Loading
Loading