Skip to content

Sync ako/mxcli: page round trips, combo box database mode, text-template params across associations, log with stacktrace, event handlers - #1646

Merged
ako merged 43 commits into
mendixlabs:mainfrom
ako:main
Oct 24, 2026
Merged

ako merged 43 commits into
mendixlabs:mainfrom
ako:main

Conversation

@ako

@ako ako commented Oct 24, 2026

Copy link
Copy Markdown
Collaborator

Syncs ako/mxcli:main into upstream: 27 commits from fork PRs ako#1655–ako#1661. They work through seven clusters on the fork's triage board, one commit per defect. Each has a test that fails with the reported symptom when its fix is reverted. Two of those commits are a gofmt and its revert, which net to nothing.

Fixes #1139, #1250, #1494, #1495, #1514, #1519, #1540, #1543, #1583, #1617, #1618, #1628 and #1631.

describe → exec round trips on pages (#1628, #1631)

On a Studio Pro-authored 11.14 app, describe → check over all 68 pages gave 16 CE1571 reports on pages mx check passes. This brings it to 0.

  • A flow argument that is a widget's selection is kept (13 of 16). An example is a grid's control-bar button passing the grid.
    • Before, describe dropped UserList = $grid2, and exec had no way to write it back.
    • It is now stored the way Studio Pro stores it, Variable {Widget}, and ALTER resolves widget names too.
  • check judges a list widget's own row click in its row (3 of 16). Row actions on a list view, data grid or gallery need no argument mapping.
  • Combo box custom content, which MDL cannot author, is no longer lost:

Pluggable widgets (#1583, #1519, #1494, #1495, #1618)

Microflows (#1543, #1139, #1617)

Domain model (#1540)

  • alter entity add event handler can add a second handler for the same event.
    • A handler is now identified by moment, event and microflow.
    • add … if not exists checks for that exact handler.
    • New: drop event handler … call Mod.Mf names which handler to drop. Without it, a drop that matches several handlers is refused.

Diagnostics (#1250, #1514)

  • A view CASE column is typed from its first branch, and numeric ones are widened. MDL031 used to demand String(200) where Mendix derives String(8).
  • A routine header without parentheses is refused with one error, at the mistake.

Docs

  • The silent-property-drop wiki page is synced, and the changelog has entries for the text-template parameter fixes

claude and others added 30 commits October 23, 2026 12:50
A button passing a widget's selection to a microflow or nanoflow - a grid's
control-bar button, or a button passing a combo box's selection - is stored
by Studio Pro as a Forms$PageVariable naming only the widget. Describe left
it out, so check reported CE1571 on pages that build (13 of the 16 reports
describe -> check gives on TestApp), and exec wrote the call with no
argument at all: rebuilt from the description, the nine pages give 13
CE1571 on mx check 11.14.0.

Describe now prints `Items = $grid` for a Widget-only variable on a flow
call or flow data source. The builder binds `$name` that names a widget of
the document to the Widget slot (after parameters and page variables, which
win on a clash), and the writer stores it there with Expression "", as
Studio Pro does. In-place describe -> exec of the affected pages: 0 errors,
where the pre-fix binary gave 2 CE1571. Two TestApp pages now keep GetPut
and are struck from the allowlist.

Refs #1560, mendixlabs#1628

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017jEcJZDio1jrTrWtVzagjY
A list view's `Action:` and a data grid's or gallery's `OnClick:` fire per
row, and the row fills the flow's parameter in: Studio Pro stores the call
with no parameter mappings (TestApp's ConflictedWorkflowDefinitionView,
ConflictResolution and WorkflowDefinition_Overview grids). The CE1571 rule
judged that action against the context around the widget, as for a button,
and refused pages mxbuild builds: measured on 10.24.25, a list view, a data
grid and a gallery with no argument give no CE1571, a button outside every
data context does.

The widget's own "Action" slot is now judged in the context its data source
puts in scope; every other slot (a gallery's onSelectionChange) and the data
source itself stay on the enclosing context. Show page has had the same rule
since #552. Describe -> check over all 68 TestApp pages: 16 CE1571 before
the two #1560 fixes, 0 after.

Refs #1560, mendixlabs#1628

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017jEcJZDio1jrTrWtVzagjY
…another entity

`alter page … replace cbBooking with { combobox cbSale (… from M.Sale …) }`
kept cbBooking's custom content inside cbSale: txtOptionName, bound to
Booking.Name. A same-kind replace keeps what the statement does not state
(mendixlabs#1247), and describe never prints a combo box's custom
content, so it was always kept. With the content type 'yes' mx check
reported CE1365 at a widget the author never wrote; once describe printed
the type, the replace turned it 'no' and the stale widget stayed stored,
hidden.

The stored Type says which data source content is built for
(DataSourceProperty). When the merge would keep that content while the
data source now names another entity, exec and check -p refuse, naming the
widgets: drop them first in the same statement, or drop + insert. check
counts drops earlier in the script, since it reads the stored document.
A replace that keeps the entity keeps the content, as before. Measured on
11.13.0: the reported replace is refused and nothing is written; with the
drop first, cbSale has no content and mx check is 0 errors; a same-entity
replace keeps it, 0 errors.

Refs #1563, mendixlabs#1631

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017jEcJZDio1jrTrWtVzagjY
describe printed a combo box with custom content as if it had none, so its
widgets were invisible until mx check named one. MDL cannot author custom
content (MDL-WIDGET26), and the NOT SHOWN note for a slot holding widgets
was only computed for generic pluggable widgets.

A combo box whose widgets properties hold widgets is now described with a
`-- NOT SHOWN:` comment naming the property and each widget, what keeps
the content (this page, the combo box unchanged, or `alter page … set`) and
what loses it (a changed combo box: CE0642; any other project). The output
still parses, and describe -> exec of the repro page is still "Unchanged".

Refs #1563, mendixlabs#1631

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017jEcJZDio1jrTrWtVzagjY
…e event

`alter entity M.E add event handler on before commit call M.Second($currentObject)`
failed with "event handler already exists for Before Commit" when the entity
already had a Before Commit handler, though Studio Pro allows several handlers
for one event and `create or modify entity` stores two `on before commit`
clauses (mendixlabs#1540, #1438).

ALTER keyed a handler on (moment, event) alone. Its identity is (moment,
event, microflow): another microflow is a second handler; the same microflow
again is still refused, which is what keeps a re-run from wiring it twice.
The same key had made `add event handler if not exists … call M.New` skip
with "already exists" whenever any handler for the event existed, and
`drop event handler on before commit` remove whichever handler was stored
first. DROP now takes `call Mod.Mf` to say which one; without it, a drop that
matches several handlers is refused and names them.

Measured on Mendix 11.12.4 with mx check: two Before Commit handlers are
0 errors (via the fixed ALTER, and via the old binary's create or modify); a
void-returning Before Commit handler is CE7247, the control showing the check
inspects handlers; the same microflow twice is also 0 errors.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SLbjUJghMMwekH4y5VEtBZ
make lint (fmt-check) fails on main with this file under go1.27.3, the
toolchain go.mod and CI pin. Whitespace only.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SLbjUJghMMwekH4y5VEtBZ
A pluggable widget's text-template parameter bound across an association
(`legendParams: ({1} = Mod.Parent_Child/Label)`) was resolved to the right
attribute and the right steps, and the serializer threw the steps away:
SerializeColumnClientTemplateParameter wrote AttributeRef.EntityRef null,
so mxbuild read the attribute against the widget's context entity:

  [CE1365] "Attribute 'Repro.Child.Label' is not an attribute of entity
  'Repro.Parent'."

The same serializer writes widget-level templates, object-list item
templates and Data grid 2 column templates; on a blank 11.12.4 app the
previous build fails CE1365 on all three (a Fieldset legend, an Accordion
group headerText, a dynamic-text column) and this one has 0 errors.

mendixlabs#1494, #1348.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LCd38DLXnG1FLJGs1eM6LD
`describe page` printed a pluggable widget's (and a Data grid 2 column's)
template parameter over an association as the bare attribute, `{1} =
Label`, which re-executes against the widget's own entity: CE1613 "The
selected attribute 'Repro.Parent.Label' no longer exists."

The core describer had learned the `Assoc/Attr` path and its pluggable
twin had not — the drift upstream #977 found in the same pair, which
shared a helper and kept the loop duplicated. The core reader now
delegates to the pluggable one, which gains the association path and the
trimmed expression, so there is one reader of a template parameter.

mendixlabs#1494, #1348.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LCd38DLXnG1FLJGs1eM6LD
mendixlabs#1495 (#1349) reported, against v0.24.0, an
expression parameter stored as an attribute path with its spaces
stripped: CE1613 "The selected attribute
'Repro.Parent.if1=1then'A'else'B'' no longer exists." v0.25.0 fixed it
(#823); main stores the Expression and builds clean.

The test takes the reporter's own spelling through the visitor and the
parameter builder. Stubbing either guard reproduces one half of the
reported message: token text instead of source text gives
`if1=1then'A'else'B'`, and a builder that binds every value gives
`M.Job.if 1 = 1 then 'A' else 'B'`.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LCd38DLXnG1FLJGs1eM6LD
…tion

The regression script for mendixlabs#1494 and #1495 — a Fieldset,
an Accordion group and a Data grid 2 column bound over an association,
and an expression parameter — plus the finding record.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LCd38DLXnG1FLJGs1eM6LD
…xlabs#1617)

DESCRIBE MICROFLOW printed a Show page action whose NumberOfPagesToClose
is "1" exactly like one that closes none, and no statement could write
it: the reader never read the key, the writer hard-coded "", and the
show page `with` list ignored every key but title, location and modal.

- The setting is `show page M.P with NumberOfPagesToClose = <expr>`,
  beside the title override. It is read, written and described; unstated
  stays "" (closes none). A literal that is not a whole number is refused
  (mxbuild 11.12.5: 1.5 and a Boolean are CE0117; 0, -1, 2 and
  `$Count + 1` build clean).
- Any other `with` key is MDL-V1-PROP / MDL-V1-PROPVALUE (an error under
  `mdl 1;`, a warning without it). `location` and `modal` were read into
  PageSettings that no writer emits; Mendix deleted the location in 8.0.
- The reporter's `show page M.P (NumberOfPagesToClose: 1)` is an argument
  for a page parameter of that name. check --references (and exec) now
  refuse an argument naming no parameter of the page, as the flow calls
  already did (#953): mxbuild fails it with CE1613, and is case-sensitive
  about it. The message names the `with` spelling.

Control: with the fix reverted the new tests fail with the reported
symptom (describe prints `show page X;`, the writer stores "").

Fixes #1550

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JQpG3nKq4SvijUdrXJRGSX
… ones

MDL031 refused a CASE column over a String(8) attribute declared
String(8) — "declared as String(8) but OQL expression returns
String(200) … Fix: change to 'SortKey: String(200)'" — and following
the fix was CE6770 "View Entity is out of sync with the OQL Query".
exec runs the same check, so only --no-check could write the
declaration that builds (#1294, mendixlabs#1250).

inferCaseType returned the first branch it could type. Without the
project a bare attribute is untyped, so in

  case … then l.Code when … then '0' + l.Code else '0000' end

the concatenation in the second branch decided. A grid of one-column
views, each declared at every candidate type and checked with mx check
11.12.5 (one accepted declaration per view), gives Mendix's rule, and
it differs per kind:

  string CASE   the FIRST branch's type, length included:
                then l.Code else l.Name (8, 100)   -> String(8)
                then l.Name else l.Code            -> String(100)
                then '0000' else l.Code            -> String(200)
  numeric CASE  widened over ALL branches, Integer < Long < Decimal:
                then 1 else 2.5                    -> Decimal
                then l.IntV else l.LngV (and back) -> Long

So "first branch decides" alone would have kept the numeric twin of
the bug: `then 1 else 2.5` was typed Integer, refusing Decimal and
suggesting Integer (CE6770).

inferCaseType now applies both rules, and a branch it cannot type
leaves the result unknown wherever that branch could decide it. The
project-aware path types each branch against the project instead of
falling back to the static inferrer, so with the source entity present
the fix names String(8); a string CASE inherits its first branch's
source attribute and with it the exact-length rule, and the refusal
calls it a CASE column rather than a pass-through one. A string
literal there is the derived String(200) (`'abc'` builds only as
String(200)), not its own length.

Control: with oql_type_inference.go reverted, oql_case_type_test.go
fails on the reported case with the reported message. Full run on
11.12.5: the bug-test applied by exec with checks on is 0 errors in
mx check; the declarations the old hint gave (String(200), Integer)
are CE6770 on both views, and check -p now refuses them suggesting
String(8) and Decimal.

The mdl-entities skill and the view-entity syntax help said a
string-returning CASE is always String(200); both now give the
measured rules.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NH2drvgJU9wb8xgK2wXaxe
…mendixlabs#1618)

`datagrid dg (…, itemSelection: Single)` and `gallery g (…, itemSelection:
None)` passed check, exec and mx check and stored the value as the
property's PrimitiveValue, with Selection left at the default — no
selection on the grid, and no way to turn a gallery's default one off.
The mapping pass read only the `Selection:` keyword; the explicit-key pass
skipped "already handled" names by the mappings' source keywords, not
their property keys, so the key fell through to SetPrimitive.

- `Selection:` and the property's key (or an alias) are one property:
  both are read by the mapping pass and stored in Selection. The by-key
  grid's widget object is now identical to the keyword-spelled one.
- A value that is not None/Single/Multi, or two different values under
  the two spellings, is refused by exec and by check (MDL-WIDGET50).
  mxbuild 11.12.5 accepts Selection "Bogus" at 0 errors, so nothing after
  mxcli catches it.
- A definition constant the script overrides by key on a widget that
  does not declare the property (`advanced` on Gallery 3.4.0) is refused
  by exec, and by check -p from the installed package (MDL-WIDGET01).
- The Gallery click-ambiguity rule (MDL-WIDGET36) reads the key too:
  `itemSelection: None` with an onClick builds at 0 errors now, where the
  pre-fix build failed mxbuild with "The item click action is ambiguous".

Control: with the fix reverted the new tests fail with the reported
values (Selection "None", PrimitiveValue "Single" for dgSingle; Selection
"Single", PrimitiveValue "None" for galNone).

Fixes #1552

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JQpG3nKq4SvijUdrXJRGSX
…he mistake

`create microflow Tmp.F returns Boolean as $b begin … end;` reported

  line 1:23 missing '(' at 'returns'
  line 1:31 missing ':' at 'Boolean'
  line 4:2 extraneous input 'return' expecting the start of a statement

and `… Tmp.F $l: List of Tmp.Item returns …` "missing ')' at 'returns'"
after the first; the report asked for one message (#1396,
mendixlabs#1514). An earlier fix hinted the first error, but the
other errors stayed.

They come from ANTLR's recovery, not from the input: it inserts the
`(`, reads `returns` as a parameter name, lets the type swallow the
body up to the first `;` and closes the statement there. By the third
error the parser is back at program level (its rule-context stack is
just ProgramContext), so nothing in the error listener can tie that
error to the header any more.

So the grammar now accepts the bare header — `(LPAREN params? RPAREN |
params)?` on create microflow, nanoflow and rule — only so the builder
can refuse it, the pattern positional call arguments use (#569). The
refusal is the only error, sits on the token the `(` should precede,
and quotes the header back with them added:

  line 1:23 a microflow header needs its parameter list in parentheses,
  even when it is empty — they are not optional: write `create microflow
  Tmp.F ($l: List of Tmp.Item) returns Boolean as $b`. See: mxcli syntax
  microflow

The regex hint is removed: with the grammar accepting the shape it
could only fire alongside a second, unrelated error on the line, and
then it would have repeated the refusal.

Controls: the original code fails the new test with the three reported
errors; with the grammar change kept and the refusal stubbed out it
fails with 0 errors, so the grammar alone would have accepted a new
spelling. The test also requires the statement after the bad one to
parse.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NH2drvgJU9wb8xgK2wXaxe
…setting written as an argument

Synthesizes the two fixes for the board's silent-property-drop cluster
(mendixlabs#1617, #1618): a property reachable by its keyword and
its key must be one name to every reader, and a setting the author wrote
as a call argument is a dangling reference to close, not only a property
to add.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JQpG3nKq4SvijUdrXJRGSX
A hide inside `A ? (…) : B ? (…hides…)` or `A ? (…) : B && hide(…)` was
never lifted: its guard's boundary is the outer ternary's `:`, which
parseGuard reads as "one fragment of a larger expression". Combo box
selects its option source this way —

  "context"===t.source ? (…) : "database"===t.source ? (…) : "static"===t.source && hide(…)

— so nothing the database or static source hides was known to be
hidden, and a database-mode combo box kept the template's values for
`optionsSourceType` and `selectAllButtonCaption` (CE0463,
mendixlabs#1583).

- elseChainTerms: an else-if branch's rule carries its own condition
  plus the negation of every earlier branch in the chain; an unreadable
  earlier condition fails the whole chain (no rule, the safe direction).
- groupGuard / groupElseChain: an else-if GROUP's condition is the one
  bounded by the outer `:`, not the whole chain, and its hides carry the
  chain's negations.
- An else-branch hide inside a guarded group (`G ? (…, C ? hide(a) :
  hide(b))`) carries G when the enclosing chain reads in full; otherwise
  its rule is the one lifted before.

Blast radius, measured by dumping every lifted rule of every widget
package in testdata/ and in blank 11.12.5 and 11.15.0 projects before and
after: only Combo box (22 rules replaced by 71) and TreeNode (+1, exact)
change. Every changed Combo box rule was checked against the
editorConfig by hand. One consequence outside database mode:
`selectedItemsSorting` no longer reads as hidden in association mode,
where the editor shows it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01J3X7yMohe1vQkLMqSGHj8e
unmappedHiddenResets skipped every property mapped in ANY mode of the
definition, while the mapping loop it complements visits only the mode
being built. A property mapped in another mode was written by neither:
a database-mode Combo box hides `optionsSourceType`, which only the
association mode maps, and kept the template's "enumeration" against a
declared "association" — the last difference between mxcli's widget and
the one `mx update-widgets` leaves, and the CE0463 (mendixlabs#1583).

The function now takes the selected mode's mappings.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01J3X7yMohe1vQkLMqSGHj8e
…endixlabs#1583)

  combobox cbCode (source: 'database', optionsSourceDatabaseDataSource: microflow Mod.GET_Parents,
    optionsSourceDatabaseItemSelection: 'Single', optionsSourceDatabaseCaptionAttribute: Name,
    optionsSourceDatabaseValueAttribute: Name, databaseAttributeString: Code)

check reported "3 issues: 0 errors, 3 warnings" (MDL-WIDGET06 "recognized
but not yet persisted" for the datasource and the item selection), exec
"Created page", and mx check "[CE0463] The definition of this widget has
changed" at Combo box 'cbCode'.

combobox.def.json had no database mode. The datasource had no write
path, the explicit-key pass stored the Selection-typed item selection as
a PrimitiveValue, and the caption and value attributes, linked to the
unwritten datasource, bound to the data view's entity.

- def: a `database` mode, selected by naming its datasource
  (`hasDataSource:optionsSourceDatabaseDataSource`; the generic
  `datasource:` clause stays the association mode's). `Attribute:` is the
  target attribute, `CaptionAttribute:` the option caption, `OnChange:`
  the mode's own `onChangeDatabaseEvent`.
- engine: a Selection-typed key goes through SetSelection; a selection
  mapping reads its own key before `Selection:`.
- check: the combo box's mode is the engine's own selectMode, not a
  restatement. MDL-WIDGET39 judges a database-mode target against String,
  Integer, Long, Enumeration, also when written as
  `databaseAttributeString:`. MDL-WIDGET16 requires the caption and, when
  a target is set (and the selection is not Multi), the value attribute.
- describe: a database-mode combo box prints its datasource under its
  key, `Attribute:`, `CaptionAttribute:`, the value attribute, caption
  type/expression and a non-Single selection by key; its settings are no
  longer named as not reproduced. The generic `DataSource:` it printed
  before re-executed as an association-mode combo box.
- widget describe: the generated example reads the unconditioned mode's
  constants only — the new mode's `source: database` had narrowed the
  combo box example for database mode while it was written as context.

Measured on fresh 11.12.5 and 11.15.0 projects (Combo box 2.5.0), with
`mxcli docker check --no-update-widgets`:
- the reported script: CE0463 on main, 0 errors with this change; the
  written widget is field-for-field what `mx update-widgets` makes of it;
  describe -> check -> exec reports "Unchanged page" and 0 errors.
- the check rules, one combo box per row: Boolean, DateTime, Decimal,
  AutoNumber and Binary targets are CE7247 "Only attributes of type
  String, Integer, Long, Enumeration are allowed here."; a target without
  a value attribute is CE0642 "Property 'Value' is required."; no caption
  is CE0642 'Caption'. check now reports exactly those, and the controls
  (no target, Multi, expression caption, caption by key) are clean on both.
Combo box 2.6.2, which the reporter used, was not available here.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01J3X7yMohe1vQkLMqSGHj8e
…ties (mendixlabs#1519)

  alter page … { insert after dg column(Name) {
    column (Attribute: Kind, Caption: 'Kind') { dropdownfilter ddf (attributes: [Status]) } } }

stored attrChoice "auto" with an empty `attr`, check passed, and
describe printed a bare `dropdownfilter ddf` — item 2's symptom on the
ALTER path, which #1422 fixed for CREATE and named as not done here.

buildColumnSpecFromAST built a column's filter with BuildFilterWidget
from the widget id and name alone, so no filter property was read. It
now builds the filter through buildWidgetV3, as CREATE does, so the
filter's definition applies.

That path registers the filter's name, which exposed a second gap: a
column REPLACE excluded only the target from the name scope, and
ContainedWidgetNames read a column reference only as a data view footer,
so a replacement reusing its filter's name was refused as a duplicate.
The column REPLACE now excludes what the column contains, and
ContainedWidgetNames falls back to the grid column.

Measured on a fresh 11.15.0 project: INSERT then REPLACE store linked +
Tmp.Item.Status, then Tmp.Item.Kind; 0 errors with --no-update-widgets;
describe prints `dropdownfilter ddf (Attributes: [Kind])`.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01J3X7yMohe1vQkLMqSGHj8e
…endixlabs#1519)

Item 3 of the report: `set (ColumnWidth: manual, Size: 220)` made the
other columns collapse. That is the widget's semantics, not a write
defect: Data grid 2's columnWidth getter (Datagrid 3.4.0, Datagrid.mjs)
renders manual as `${size}fr`, autoFill as `1fr` and autoFit as
`minmax(<minWidthLimit>px | min-content | auto, auto)`, so Size: 220
takes 220 shares against one per autoFill column.

`mxcli syntax page column`, both quick references and the create-page
skill called Size pixels (the syntax example was `Size: 150`). They now
say it is a weight, use `Size: 2` in the examples, and document the pixel
option — `ColumnWidth: autoFit, minWidth: manual, minWidthLimit: N` —
which writes, round-trips and builds at 0 errors on 11.15.0.

The other half of item 3 (switching back to autoFill gave CE0463) was
fixed in #1422.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01J3X7yMohe1vQkLMqSGHj8e
…dixlabs#1519)

Item 1 of the report — `column (Caption: '', ShowContentAs:
customContent)` gave CE0463 on v0.26.0 — no longer reproduces: d152835
(#1125) writes the empty header template Studio Pro writes, and its unit
test covers `column (Caption: '')`. This adds the reporter's shape as an
MDL regression case.

Measured on a fresh 11.15.0 project with --no-update-widgets: 0 errors
with main; a binary built at d152835^ reports CE0463 at Data grid 2 'dg'
on the same script.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01J3X7yMohe1vQkLMqSGHj8e
…xlabs#1543)

`LOG ERROR NODE 'X' 'failed' WITH STACKTRACE;` was a parse error
("mismatched input 'stacktrace' expecting '('"), so every log written
through MDL stored IncludeLatestStackTrace = false, an error handler
could not log the trace, and describe dropped a box ticked in Studio Pro
on the round trip.

The model field, the modelsdk writer and the reader already carried the
flag; only the language surface and the --mcp map were missing:

- grammar: `logStackTrace: WITH STACKTRACE` after the template
  parameters (`WITH (` vs `WITH STACKTRACE` is decided on the second
  token); STACKTRACE is in the keyword rule, so `$latestError/Stacktrace`
  (System.Error's attribute) still parses
- visitor, AST, builder: LogStmt.IncludeLatestStackTrace ->
  LogMessageAction.IncludeLastStackTrace
- describe prints ` with stacktrace` only when ticked (unticked is what
  an absent clause builds), so existing describe output is unchanged
- MCP backend sends includeLatestStackTrace when ticked

No version gate: the Model SDK's LogMessageAction.versionInfo has no
entry for includeLatestStackTrace. Measured on 11.12.4: mx check reports
0 errors with the flag in a microflow and a nanoflow (control: a nanoflow
log with an undefined template variable reports CE0109 on that activity).

Control: with only the AST field the new test fails with the reported
parse error; with the grammar but no builder line it fails
"IncludeLatestStackTrace = false, want true".

Mirror: #1441.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YV8Q5TCsXSkkDqf2Sku4qr
…bs#1139)

mendixlabs#1139 asked for Studio Pro's "Disable activity" in MDL.
It is `@excluded` on the statement, and since mendixlabs#1328 the
flow builder keeps it on activities in microflows and nanoflows
(re-verified on 11.12.4: the reporter's repro now describes back with
@excluded and stores Disabled = true).

Two gaps remained:

- Only ActionActivity has Disabled (Model SDK: ActionActivity.disabled,
  on no other type), so `@excluded if $b then ... end if;` -- or on a
  loop, while, return or raise error -- passed check and exec and was
  dropped; the branch the author meant to switch off still ran. Check
  now warns MDL095 (a warning, as MDL042 is for the same drop shape of
  @caption on a loop), from checkUnknownAnnotations so it covers
  microflows, nanoflows and error-handler bodies.
- `mxcli syntax microflow layout` said @excluded is a DOCUMENT
  annotation that does not go on a statement. It now documents both
  meanings; the write-microflows skill links a "Disabled activities"
  section in reference/control-flow.md.

Control: with the rule ID but no check, every case of the new test
fails "want one MDL095, got []".

Mirror: #1282.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YV8Q5TCsXSkkDqf2Sku4qr
fix: pluggable-widget-engine — combo box database mode, ALTER column filters, column Size docs (mendixlabs#1583, mendixlabs#1519)
The file was gofmt-clean. The local `make lint` that flagged it ran
/usr/local/go/bin/gofmt, an older install than the go1.27.3 toolchain `go`
switches to (GOTOOLCHAIN=auto), and the two disagree on this file; CI's
go1.27.3 fmt-check then rejected the "fixed" version. This restores the
base content.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SLbjUJghMMwekH4y5VEtBZ
…2nnrm

# Conflicts:
#	mdl/executor/widget_engine.go
ako and others added 13 commits October 23, 2026 22:15
…ues-detxhx

fix: log ... with stacktrace, and warn when @excluded lands on a decision or loop
A combo box whose options come from a database source (Studio Pro "Source:
Database") had no mode in the definition. Its data source and item
selection were "recognized but not yet persisted" (MDL-WIDGET06), describe
printed the source as the association mode's `DataSource:`, and exec
rebuilt TestApp's two such combo boxes as context-source, single-selection
ones: mx check 11.14.0 CE0642 x4 and CE0115 x2 (a list parameter fed a
single selection).

The definition gains a database mode, selected by
optionsSourceDatabaseDataSource: it writes source 'database', the data
source and the item selection (`Selection:` or the property's own key,
default Single), and the caption/value attributes now bind against that
source's entity instead of the enclosing one. describe prints a
database-source combo box by those keys. The example generator no longer
reads the new mode's constants, as the validator never does for a widget
not built in it.

Rebuilt from the new description, the two TestApp pages build at 0 errors;
a fresh 11.13.0 project round-trips byte-identically, and #1488's own
script builds at 0 errors where it was CE0463.

Refs #1647, #1488

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017jEcJZDio1jrTrWtVzagjY
…content

`create or modify` of a page whose combo box the statement changes rebuilt
the combo box from its template and deleted its custom content, which MDL
cannot author, while the content type stayed 'yes': "Replaced page", then
mx check CE0642 "Custom content is required". An unchanged combo box
survives through the pluggable passthrough.

When the passthrough declines a stored combo box that holds custom content
and the rebuild is a combo box again, exec refuses, naming the widgets:
change it with `alter page … set` (which keeps the content), or drop the
content first and write the combo box without its content type. check -p
refuses the same rewrite up front, next to the #1622 guard. Measured on
11.13.0: unchanged → "Unchanged page"; changed → refused by both, nothing
written; the remedy → 0 errors. The describe note says so.

Refs #1648

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017jEcJZDio1jrTrWtVzagjY
Resolves the CHANGELOG conflict: v0.28.0 was released in between, so this
branch's entry moves to the new [Unreleased] section.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LCd38DLXnG1FLJGs1eM6LD
CHANGELOG.md conflicted: main cut [0.28.0] and its newest fixes landed in
that section's Fixed list, where this branch's entry also sat. The fix is not
part of 0.28.0, so its entry moves under [Unreleased] / Fixed; main's side of
the hunk is kept as is.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SLbjUJghMMwekH4y5VEtBZ
fix: misleading diagnostics — MDL031 on CASE columns, and a parenless routine header
…2nnrm

# Conflicts:
#	.claude/skills/mendix/write-microflows/SKILL.md
fix: write and describe a pluggable text-template parameter across an association (CE1365)
…-trip-gaps-ibgvit

main fixed the combo box's database mode for mendixlabs#1583
(#1488) while this branch fixed it for #1647. The two
converged on the same mode switch; main's version is kept whole, as it also
lifts the editorConfig else-chain and resets hidden properties of another
mode, which this branch did not. Resolution: main's combobox.def.json,
widget_engine.go, widget_describe.go, cmd_pages_describe_bykey.go,
cmd_pages_describe_pluggable.go, widget_registry_test.go and the skill
reference; this branch's duplicate tests dropped; #1563's hidden-content
read kept in cmd_pages_describe_parse.go. The definition had auto-merged into
two database modes with no conflict marker.

Re-measured on the merge: TestApp's two database combo boxes, rebuilt from
their description, build at 0 errors (mx check 11.14.0), so #1647 is fixed
by main's commit; the #1647 finding and bug-test now say so.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017jEcJZDio1jrTrWtVzagjY
CHANGELOG.md conflicted again: both sides added an entry to [Unreleased] /
Fixed. Both are kept.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SLbjUJghMMwekH4y5VEtBZ
fix: alter entity add event handler adds a second handler for the same event
fix: silent-property-drop cluster — show page pages-to-close, selection by widget key
Fix the describe-round-trip-gaps-pages cluster (#1560, #1563) and its follow-ups (#1647, #1648)
@ako
ako merged commit 12594f3 into mendixlabs:main Oct 24, 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.

MDL syntax to disable/enable individual activities in microflows and nanoflows

2 participants