Skip to content

Sync ako/mxcli: filemanager widget, no auto-created module roles, describe round-trip fixes - #1503

Merged
ako merged 84 commits into
mendixlabs:mainfrom
ako:main
Oct 14, 2026
Merged

ako merged 84 commits into
mendixlabs:mainfrom
ako:main

Conversation

@ako

@ako ako commented Oct 14, 2026

Copy link
Copy Markdown
Collaborator

Syncs ako/mxcli:main into mendixlabs/mxcli:main: 42 commits since #1478, landed in the fork through PRs ako#1176–ako#1213. Most fix reported describe → exec round-trip failures on Studio Pro-authored apps and templates.

Behaviour changes

New MDL

describe → exec round-trips

Identity and safety

Other

claude and others added 30 commits October 13, 2026 17:25
…ndixlabs#1404)

Reported: "describe emits `MxObject: \"Entity\"` with no declaration, and
exec writes entity `.Entity`" — mx check CE1613 "The selected entity
'.Entity' no longer exists." at Java action parameter 'MxObject', while
`check` passed. Seen on GenAICommons.ChunkCollection_AddKnowledgeBaseChunk.

MDL declared a type parameter only through an `entity <T>` parameter, so
one bound by the caller's object had no spelling.

- Grammar: `create java action Module.Name<T, U>(…)` declares type
  parameters in order; `entity <T>` parameters still declare theirs.
- describe prints the header only when the entity selectors would not
  reproduce the stored list in its order, so actions that already
  round-tripped describe unchanged. An entity selector bound to no type
  parameter (`entity <>`, which does not parse) or a dangling reference
  is commented out under `-- NOT re-executable:`, as for JavaScript
  actions.
- exec refuses, and check reports as MDL-JA01, an unqualified type that
  is neither a declared type parameter nor Microflow/Nanoflow, instead
  of writing the entity `.Name`.

mx check 11.6.6 on the bug-test script: 0 errors; describe -> exec ->
describe is identical and the re-exec is "Unchanged".

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

Reported: `check -p` and `exec` refuse an enum split that `describe
microflow` produced and Studio Pro 9.24.2 wrote (the FSM template's 28-value
NativeModelCheck_Process): "enum split has 29 branches; at most 16 branches
are supported". `check` without -p passed, so the two modes disagreed.

Which side was wrong: the limit, not the describer. The 16 was
len(splitCaseOrderAnchors), the 4x4 origin/destination anchor-pair table that
once stored each case's order. A split of four or more cases no longer uses
it: the order is stored as origin side + canvas depth
(enumSplitOriginAnchors) and read back by orderedEnumSplitFlows, neither of
which has a limit. Mendix has none either: a 29-branch split written by exec
into a blank 11.6.6 app checks at 0 errors under mxbuild, and Studio Pro
authors such splits. The cap guarded nothing, so it is removed from the
builder and from the -p body validator (with the now-unused helpers).

Round trip on a PedApp copy: exec the bug-test, describe -> check -p passes
-> exec reports "Unchanged microflow" -> re-describe identical, all 28 cases
in written order.

Tests (replacing the two that pinned the refusal):
- TestEnumSplitBuilderAcceptsMoreThanSixteenBranches: builds 28 cases +
  (empty), describes them back in order.
- TestValidateMicroflowBody_EnumSplitAcceptsMoreThanSixteenBranches.
Control: with the cap restored both fail with the reported text.

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

Reported: "describe entity (and describe module ... with all) prints an
index with an empty column list" — `index ( desc);` — which check refuses
with "index on unknown attribute 'desc'", so every statement referencing
the entity failed (Siemens FSM template, ten entities).

The stored index is real, not dangling: Studio Pro stores a segment on the
changedDate / createdDate system member as IndexedAttribute.Type =
ChangedDate / CreatedDate with no attribute behind AttributePointer. The
semantic IndexAttribute had no Type, so the reader dropped it, the
describer resolved the column by attribute ID only, the create and alter
paths could not name the column, and indexToGen hard-coded Type Normal —
any rewrite of such an entity turned the index into an attribute segment
pointing at nothing.

IndexAttribute now carries Type through read and write; describe prints
the member as the entity declares it (`index (ChangedDate desc)`), and
create / alter add index / drop-index matching / reconcileDroppedIndexes
accept it while the entity stores that member. A segment resolving to
neither is described as a `-- index skipped` comment on its own line, so
the statement still parses.

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

The skill's basic-syntax block used placeholder types (`param1: type`,
`returns ReturnType`). With MDL-JA01 those are undeclared type
parameters, so check-skill-mdl-semantics.sh failed the block. Use real
primitive types instead, so the example is one a reader can run.

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

describe printed a Studio Pro text-template parameter as stored and check
refused it:

  ContentParams: ({1} = $currentObject/CollectionIdentifierLabel)
  ✗ … ContentParams parameter {1} is written as
    "$currentObject/CollectionIdentifierLabel" — … Write
    "CollectionIdentifierLabel". … [MDL-WIDGET24]

Which side was wrong: the writer. Studio Pro stores that parameter as
Forms$ClientTemplateParameter.Expression (introduced 9.0.1), and describe
prints the expression verbatim, so the only spelling that re-creates the
same model is the expression itself. The writer instead read `$var/Attr`
as an attribute binding and kept the prefix in the attribute name
(CE1613, #1046), and MDL-WIDGET24 was added to refuse the spelling
rather than fix the write. Rewriting describe to a bare `Attr` would have
changed an Expression into an AttributeRef (a different model, and wrong
for a `$Param/Attr` outside the context object).

isTemplateExpression now treats a variable-rooted member path as an
expression. `$currentObject/Assoc/Attr` stays an association binding,
as #1046 measured it building clean. MDL-WIDGET24 is removed: it has
nothing left to prevent.

Measured on 11.6.6 (blank app): `{1} = $currentObject/Name` and
`{1} = $EntA/Name` on dynamic text and a button caption exec and build at
0 errors; describe -> check -p --references passes -> exec reports
"Unchanged page". Over a Decimal attribute the expression is CE0117 (a
template expression must be a String) — the old no-project check can no
longer warn about that case.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01W3x8Fyvir8kLyPgJs1BDmF
Reported: "Describe of a doc comment loses a markdown `*` bullet" — a
documentation line starting with `* ` came out of describe without the
` * ` gutter, and replaying it stripped the bullet marker: "Silent drift
of documentation text", one marker per describe/exec cycle.

Eleven describers printed documentation with "/**\n * %s\n */\n", which
gutters the first line only, and the entity attribute used the inline
`/** %s */`. The parser strips one leading `*` per line, as javadoc does,
so an unguttered bullet line lost its marker. They now call
outputJavadocIndented; the attribute keeps the inline form only for one
line that does not itself start with `*`.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01W3x8Fyvir8kLyPgJs1BDmF
fix: Java action type parameters are expressible and round-trip (mendixlabs#1404)
mendixlabs#1405)

Reported: describe emits catalog order, and check refused the output of
two mutually recursive microflows: "microflow ModA.MF_A references
microflow ModA.MF_B before it is created (statement 3), and the project
does not have it" — no order of the two statements passes, so mutual
recursion could not round-trip without a hand-written stub.

A call is stored by name, so it can be written before its callee; the
hazard was a later statement failing and leaving it dangling, which the
#1408 journal removes. So:

- A run under the journal (plain `mxcli exec`) resolves a CALL
  MICROFLOW / CALL NANOFLOW against every flow the script declares
  (Executor.SetForwardFlowCalls; enterScriptFlows fills scriptFlows from
  the whole program, saved and restored for nested scripts).
- --continue-on-error, the REPL and -c cannot roll back and still refuse
  it, saying so and giving the remedy: create the callee first, or a stub
  then `create or modify`. With -p the pre-flight refuses before anything
  is written; otherwise the statement fails with the same hint.
- check accepts it: it cannot know how the script will run, so it takes
  the plain-exec rule, and exec refuses in the modes that cannot roll
  back. MDL-ORDER01 skips flow-call targets; validateForwardDefRefs
  applies the run's mode. `mxcli diff` predicts exec's mode.
- Only flow-call targets: a page button's flow, IN QUEUE, parameter
  types, grants and the rest keep the ordering rule.

Case 5 of 955-forward-definition-order.fail.mdl moves to
1405-forward-flow-calls.mdl. Measured on Mendix 11.6.6 (expr-checker
copy): A<->B microflows and nanoflows in either order, exec then mx check
0 errors; describe -> exec -> describe identical.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01W3x8Fyvir8kLyPgJs1BDmF
fix: describe and write an index on a system date member (mendixlabs#1401)
…pping root (mendixlabs#1407)

Three describe outputs that `mxcli check` could not parse:

  format (decimalPrecision: -1)   -> extraneous input '-'
  DecimalPrecision: -1 (text box) -> "DecimalPrecision must be a
                                     non-negative integer" [MDL-WIDGET18]
                                     + [MDL-WIDGET42]
  root (Wrapper) (import mapping) -> extraneous input '('

Which side was wrong, per part:

- DecimalPrecision -1: the grammar and check. -1 is the value Studio Pro
  stores for "no fixed precision", so describe printing it is correct.
  propertyValueV3 gains a signed number (`MINUS NUMBER_LITERAL`), so -1
  is a plain value rather than falling through to the generic expression
  alternative (which is what MDL-WIDGET42 then refused), and both
  validators accept -1 (still refusing anything below it). Measured on
  11.6.6: a page with both spellings execs and builds at 0 errors, and
  describe -> check -p --references -> exec reports "Unchanged page".
  The existing input_formatting test asserted -1 is refused; it now uses
  -2.

- root (Wrapper): the describer. "(Wrapper)" is the generated item of an
  array of primitives, a marker like "(Object)"/"(Array)" that is never a
  member name. schemaRootClause now drops it too; the writer already
  steps from an array into its Wrapper (arrayItemOf), so a mapping over a
  root primitive array described with no root clause is rebuilt at
  "(Array)|(Wrapper)" again.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01W3x8Fyvir8kLyPgJs1BDmF
fix: gutter every line of a described doc comment (mendixlabs#1428)
…1403)

Reported: "Describe emits a `show page` argument taken from the page's own
parameter, which check refuses (MDL-PAGEARG01)". `describe page` of a
GenAI Showcase wizard page prints `show page M.Next(Task = $Task)` on a button
outside any data container; `check` refuses it, so the page cannot be
re-created (12 occurrences in the 2026-10-12 round-trip runs).

Which side was wrong: the check and the writer, not the describer. Studio Pro
stores this button as an EXPLICIT Forms$PageParameterMapping {Argument "",
Variable: Forms$PageVariable{PageParameter: Task}} — the parameter bound by
name, which needs no context object (ako/TestApp WorkflowCommons
TaskAssignmentHelper_UserTask_Reassign; Snip_UserTask_Detail for the
SnippetParameter twin). DESCRIBE reads that mapping correctly. mxcli only knew
the empty, inferred mapping (#296), so it concluded no argument can be stored
outside a data widget (#1029) and refused the spelling.

Now a show page argument naming an entity parameter of the page or snippet
being built is accepted wherever it sits and written as Studio Pro writes it
(9.7+, where PageSettings has ParameterMappings; older projects still refuse).
The context object stays inferred, so existing pages write exactly as before.
gen's NewPageParameterMapping stamps $Type Forms$FormCallArgument
(LayoutCallArgument) — mxbuild cannot load a page carrying it — so the writer
sets Forms$PageParameterMapping explicitly.

Measured on mxbuild 11.6.6, blank app, the new bug-test (page and snippet):
  fixed                         -> The app contains: 0 errors.
  mapping forced back to empty  -> 2x CE1571 "No argument has been selected
                                   for parameter 'Item'", at btnOpen/btnNext
describe -> check -p -> exec of both documents: "already in sync".

Roundtrip harness: two ako/TestApp allowlist entries struck
(TaskAssignmentHelper_UserTask_Reassign, _Retarget no longer break exec).
Snip_UserTask_Detail now passes exec too and exposes unrelated getput drift
(conditional-visibility ModuleRoles, a date picker ReadOnlyStyle, a data view
source mapping marker), so its entry moves from exec to getput rather than
being struck. 1029-showpage-arg-without-context.fail.mdl now uses
$currentObject, which nothing at the page root binds; its old $SomeRef is
exactly this issue's shape. Skills and `mxcli syntax` updated.

Tests:
- TestValidateShowPageArguments_DocumentParameterIsBoundByName (check)
- TestBuildShowPage_DocumentParameterArgumentIsAnExplicitMapping (exec)
- TestClientActionToGen_ShowPageWritesNamedParameterMapping (BSON)
Control: with the builder/writer reverted and the check-time exemption
removed, all three fail (MDL-PAGEARG01 "there is no context object at all";
"0 mappings written, want 1").

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

describe page on the FSM template's NativeModelCheck.NativeShadowModel_Overview
emitted `DataSource: selection grid1` for a data view listening to a
legacy Forms$DataGrid that describe only writes as a "NOT re-executable"
comment. check -p and exec refused the page:

  widget 'dataView1': selection DataSource references 'grid1' but no
  widget with that name exists on this page

Which side was wrong: the describer. check is right that a selection
data view needs its source on the page; describe produced a reference
to a widget it had itself dropped. The data view cannot be written
without its source, so it is now commented out with the grid, under a
"NOT re-executable" line naming the source it lost — the rest of the
page stays re-executable, and what re-running drops stays visible.

describeWidgetBody (page and snippet bodies) records which widgets come
out only as comments. A widget whose selection source is among them is
rendered and emitted line by line as a comment, and everything inside
it counts as dropped too, so a second data view selecting from a list
nested in the first is commented as well. A dependent can precede its
source in document order, so a pass that emitted a selection of a
widget it later dropped is rendered again; a body without that case is
rendered once.

Tested on PedApp with a list view retyped in the BSON to Forms$DataGrid
(same-length string, byte replace): describe -> check -p --references
passes -> exec succeeds; before the fix both refused with the message
above.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01W3x8Fyvir8kLyPgJs1BDmF
fix: resolve forward microflow/nanoflow calls in a run that rolls back (mendixlabs#1405)
…endixlabs#1468)

ALTER PAGE INSERT/REPLACE of a Data grid 2 column took its entity context
from EnclosingEntityForChildren, a BSON-only walk. A microflow or nanoflow
data source stores no entity in the page, so the column's Attribute stayed
unqualified and was written with no AttributeRef: mxbuild failed with "An
attribute is required when column sorting is enabled" (plus CE0402 on the
cell's ContentParams) on flow-sourced grids only.

Both column paths now use alterEntityContext, which falls back to the
flow's return entity as every other ALTER PAGE widget path already did.

Verified on Mendix 11.14.0: the bug-test script gives 6 errors with the
fix reverted and 0 with it.

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

A new page, microflow or nanoflow in a module with no module roles created
<Module>.User ("Auto-created default role for mxcli document access") and
granted the document to it: a security role nobody chose, open to every user
role later mapped to it. A new document now starts with no allowed roles in
every module, and MOVE maps roles to same-named target roles without creating
any.

Exec, check --references (grant resolver) and MDL-SEC21 each simulated the
default; all three are removed so check keeps predicting exec. Roles an earlier
mxcli created are left in place and `create module role M.User` still adopts
them. Example scripts that granted to <Module>.User without creating it now
create the role.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EZuJHmm9r3UfYh19LS6QAj
fix: create accepts an enum split of more than 16 branches (mendixlabs#1416)
…ndixlabs#1473)

A Change object activity that mixes a reference-set Add or Remove member
with other members had no MDL spelling: `add … to $Obj/Assoc` (#1288)
covers only a sole member, so describe printed the Add as `=` with

  -- WARNING: Test.A_B (Add) stored as Add/Remove, not a set assignment;
  re-executing this line replaces the set — split it into `add`/`remove
  … $X/<Assoc>` statements

and re-executing it turned the append into a set assignment.

- grammar/visitor: CHANGE members take `=`, `+=` (Add) or `-=` (Remove)
  via a changeMemberAssignment rule; CREATE keeps `=` only, so
  `create … (Assoc += $X)` is a parse error rather than a silent Set
- builder: each member's Type comes from its operator; the CE0033
  reference-set check moves into addChangeObjectAction, and
  `add … to` / `remove … from` now route through it unchanged
- describe: members print with their operator, and the WARNING is gone;
  a sole Add/Remove still prints as `add … to` / `remove … from`

Verified on 11.12.4: the mixed Set/Add/Remove activity passes mx check
with 0 errors; with the guard stubbed, `+=` on a Reference gives CE0033.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NSY78DBhLeC1eAX3oC5LkV
fix: show page may pass the page's own parameter by name (mendixlabs#1403)
…ndixlabs#151)

MDL could not create a file manager, which file-upload pages (e.g. Excel
import) need. `pluggablewidget 'Forms$FileManager'` had no definition, the
bare `filemanager` was refused as MDL-WIDGET25, and describe printed a stored
one as a NOT re-executable comment, so describe -> exec dropped it.

    filemanager fileManager1 (Label: 'File', Type: Upload, MaxFileSize: 10,
                              AllowedExtensions: '.xlsx', ShowFileInBrowser: false)

- grammar: FILEMANAGER token (enumerated widget type, still usable as a name)
- builder: buildFileManagerV3; omitted properties take the Model SDK's
  defaults (mendixmodelsdk 4.116.0: Both, 5 MB, any file, false)
- writer: widgetToGen case + the InputWidget null slots live on Mendix 10/11
- describe: prints a re-executable line, non-default properties only
- sdk/pages: ShowButton -> ShowFileInBrowser (the metamodel's name), + Label

Verified on Mendix 11.14.0: exec + `mxcli docker check` gives 0 errors;
control — the same widget in a non-FileDocument data view gives CE6810, so
mx does inspect it. describe -> exec reports "Unchanged page".

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

Reported: describe of a published REST service drops the service-level
authentication parameter list (the Siemens FSM template declares a `Token`
query parameter that makes its `$Token` authentication microflow valid), and
re-executing the output fails MDL-REST04 "authentication microflows can only
take an HttpRequest and an HttpResponse".

Rest$PublishedRestService.Parameters (Mendix 7.17+) is now part of the model:
read with each element's $ID, written from the model (no longer carried as an
unauthored key), and stated in MDL after the authentication microflow:

  Authentication: (basic, microflow Shop.Authenticate (
    $Token: header 'X-Api-Token', $Tenant: query,
    $Lang: query 'lang' description 'UI language, defaults to en'))

An unlisted parameter other than the request/response is a query parameter
under its own name; the type is always the microflow parameter's. MDL-REST04
(exec and check --references) refuses an unknown $Param, the request or the
response, a kind other than query/header, and a duplicate. Unstated
authentication keeps the stored list merged over the microflow's
(mergeOperationParameters); each parameter keeps the stored $ID of the element
with the same MicroflowParameter binding. describe prints the list only when it
differs from the default, so existing output is unchanged.

Studio Pro reference: FSM template (marketplace 204718)
NativeRestAPI.DocumentShare stores one query parameter `jwt` bound to $Token,
with the operation parameters' five properties. Verified on 11.6.6: mx check 0
errors; the same script with Parameters not written gives CE0336 on both
services; describe -> exec on a fresh copy -> identical describe; re-exec
reports the documents unchanged.

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

The #1404 change added two lines to the java-actions skill without
regenerating its section index, so 'make skill-index --check' reports
it stale on main and every 'make build' rewrites it in place.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01W3x8Fyvir8kLyPgJs1BDmF
fix: store a $var/Attr template parameter as an expression (mendixlabs#1406)
…#1399)

Reported: "describe page/snippet prints a nanoflow data source or action
without its argument mapping", so round-trip checks fail with CE1571 on a
missing parameter such as NativeInstanceItem (Mendix 9.24.2 FSM template).

The mapping is empty in the source too: Mendix fills it from the enclosing
repeater. That repeater's datasource is Studio Pro 9's pluggable "Database"
source, CustomWidgets$CustomWidgetDatabaseSource (constraint rows, an
IndirectEntityRef path, a sort bar), which describe did not know. The generic
pluggable reader then dropped the unsupported result outright, so the
repeaters printed no DataSource and the nested widgets lost their context.

Read: the source describes as the existing `database from` spelling -
`database from $Param/Assoc/Entity` for a path (the List View form), its
constraint rows as the XPath they mean. On 9.x the start parameter is on the
WidgetValue, not the source (sourceVariable is 10.0+). An unsupported
pluggable datasource is now kept, so its comment shows.

Write: on a 9.x project a pluggable widget accepts `database from $ctx/…`
and writes a CustomWidgetDatabaseSource with Studio Pro's five keys, its WHERE
parsed back into rows (anything a row cannot hold is refused), the parameter
on the WidgetValue. Mendix 10 converts every such source into an XPath source
(mx convert 10.12.19), so 10+ keeps refusing the form as before.

FSM template, Snippet_NativeInstanceForm: describe -> check (0 CE1571, was
8) -> exec on a fresh copy -> mx check 9.24.30: 0 errors -> re-describe
identical. An edited repeater rebuilt by the writer is byte-equal in its
datasource to Studio Pro's.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01W3x8Fyvir8kLyPgJs1BDmF
Reported: "Describe of a merge/decision cannot be re-created (#923):
WARNING lines, layout drift". `describe microflow
OpenAIConnector.EmbeddingsRequestMapping_POST` prints a decision with empty
branches and a `merge shared1;` nothing joins; `mxcli check` refuses it with
MDL-FLOW02 "Every merge needs at least one incoming path".

Cause: the decision "api key type?" is a Microflows$RuleSplitCondition whose
rule (Rule_Configuration_KeyTypeToUse) returns an enumeration; its flows carry
Bearer_Token / API_key / (empty). The describer recognised an enumeration split
only as an expression `$var`, so this one fell through to the boolean `if`
emitter, which found no true/false flows. Which side was wrong: both — MDL had
no spelling for it (`case` took a variable or attribute path only), and the
describer could not have printed one.

Syntax: `case` now takes a rule call, spelled exactly as an `if` on a rule
spells it:

  case OpenAIConnector.Rule_Configuration_KeyTypeToUse(Configuration = $Configuration)
    when Bearer_Token then ...
    when API_key then ...
    when (empty) then ...
  end case;

- grammar: enumSplitSource | functionCall; EnumSplitStmt.Condition.
- builder: the split condition goes through buildSplitCondition, so it is a
  RuleSplitCondition (an expression naming a rule is CE0117).
- describer: `case <rule call>` when a rule split's flows carry enum cases.
- coverage: where the rule's return enumeration resolves (exec, and check -p
  for a new flow), a missing value is refused as the CE0079 mxbuild reports,
  and a value the enumeration lacks is refused; `case` on a Boolean rule is
  refused (write `if`). Unknown rule/enumeration: not judged.

Second symptom (layout drift on re-describe): real, but not "@Curve becoming
@caption 'empty'". After drop + create from DESCRIBE, three @Curve lines and
one @anchor(from: top) were lost — general, not rule-specific (reproduced on
a plain `case $Color` and on an `if`):
- the if / case / split type builders applied a statement's annotations with
  applyAnnotations, which never records @Curve for applyFlowCurves; they now
  use applyPendingAnnotations, as the if-branch loop already did;
- an if's `@anchor(from:)` is the exit side of its closing merge (#767); when
  that merge is declared on its own (`merge shared1;`) the rebuilt if has no
  merge to put it on. The merge declaration now carries `@anchor(from: X)`.

Proven on marketplace module 220472 (Mendix 11.12.5):
  describe -> check -p: passes -> exec on a copy: "Unchanged microflow";
  drop + create from the describe -> re-describe: identical; the rebuilt
  split stores RuleSplitCondition + RuleCallParameterMapping + 3 enum cases.
  mx check of the module project (it lacks GenAICommons/Atlas): 650 errors
  before and after, the identical error set, none in this microflow.
  Blank 11.12.5 app + bug-test 1414-case-on-enumeration-rule.mdl:
  0 errors; describe -> check -p -> exec "Unchanged". Coverage control: the
  same script without the API_key branch, built with the check disabled ->
  mxbuild 1x CE0079 "The 'API_key' condition value should be configured…";
  with it, check -p and exec refuse it.
Roundtrip harness (PedApp, TestApp): passes, no allowlist entry changes state.

Tests: TestCaseOnRuleCall_ParsesBuildsAndDescribesBack,
TestCaseOnRuleCall_CoverageAgainstTheReturnEnumeration,
TestSplitCurvesSurviveDescribeExecDescribe, TestMergeDeclarationCarriesItsExitSide.
Control: visitor not setting Condition -> "Condition is nil; the rule call was
dropped"; describer without the rule branch -> prints
`if Mod.KeyTypeToUse(...) then end if;`; split builders on applyAnnotations ->
"@Curve(...) lost after exec"; merge declaration without the exit side ->
no "@anchor(from: top)".

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01W3x8Fyvir8kLyPgJs1BDmF
fix: comment out a data view whose selection source describe cannot emit (mendixlabs#1415)
…ixlabs#1474)

TestNanoflow_GrantRevoke and TestNanoflow_GrantIdempotent granted to
RoundtripTest.User, which only existed because creating the nanoflow
auto-created it. Create it explicitly, as a script now has to.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EZuJHmm9r3UfYh19LS6QAj
fix: let check take back DecimalPrecision -1 and a primitive-array mapping root (mendixlabs#1407)
claude and others added 25 commits October 14, 2026 06:32
…44 (mendixlabs#151)

It was reported as MDL-WIDGET34, the input-binding rule, which
TestRuleIDHasOneOwner and TestWidgetRuleIDsAreNotReused refuse: one ID
raised from two files. The messages and checks differ, so it takes the next
free number on main.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BrnskoDgU7F6hnHjkrQywD
fix: "statement 1 was rolled back", not "were" (mendixlabs#1408 follow-up)
)

Reported: "describe layout prints an empty body for a layout that wraps
another layout; the output is refused with `declares no placeholders at
all`" (ConversationalUI.Layout_MasterBase on Atlas_Core.Atlas_Default).

Such a layout stores its tree in Content.LayoutCall, a Forms$LayoutCall
to the outer layout whose Forms$FormCallArgument entries hold the
placeholders. Content.Widgets is []. This was measured on the Studio
Pro-authored Conversational UI 7.3.1 package. No local fixture had a
nested layout.

- gen bound WebLayoutContent's call to "FormCall", which is a page's key.
  It now uses "LayoutCall" (storage-name override, read and write).
- pages.Layout carries LayoutCall. layoutToGen writes it with the page
  code's layoutCallToGen, and refuses it on a native layout.
  checkLayoutPlaceholders counts placeholders inside the arguments.
- MDL: `Layout: Module.Outer` in the create layout header. The body is a
  page body: `placeholder X { ... }` fills the outer X, and bare widgets
  fill Main. The argument builder is shared with pages.
- MDL081/MDL083 count the argument blocks. Exec refuses an argument the
  outer layout does not declare. --references checks that the outer
  layout exists.
- describe layout and diff-local print `Layout:` and one block per
  argument.

The rewrite goes through UpdateRawUnit/canon.Reconcile. A rewrite of the
Studio Pro-authored layout keeps all seven stored $IDs. An identical
rerun of an mxcli-authored one is elided.

Verified on mx 11.6.6:
- the describe output exec'd on a fresh project checks with 0 errors,
  and re-describing it gives identical output;
- a new layout on Atlas_Default plus a page that uses it checks with
  0 errors and builds a PAD;
- control: a page binding an undeclared placeholder of the nested
  layout fails with CE1613.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01W3x8Fyvir8kLyPgJs1BDmF
fix(pages): bind the Image widget to an uploaded image with `Image: $currentObject` (mendixlabs#1476)
feat: describe and write layouts built on another layout (mendixlabs#1411)
fix(backend): carry index GUID when declared index ID misses (mendixlabs#1470)
fix(executor): resolve excluded-twin names to the live document everywhere (mendixlabs#1469)
fix(pagemutator): accept set Image / DefaultImage on legacy image viewers (mendixlabs#1464)
Fix entity overlap in domain models by measuring box height
fix(pages): write one EntityRefStep per hop for multi-hop association sources (mendixlabs#1467)
fix(pages): write a tab page's Visible:, refuse what it cannot store (mendixlabs#1465)
Re-executing the unchanged `describe layout` of a Studio Pro-authored
layout reported "Replaced" and rewrote the unit: layoutToGen writes the
canvas as the constant 1280 x 800, and none of PedApp's 22 Atlas layouts
stores that (1198, 1204, 1000, 800, 550, 500 x 600). UpdateLayout now
carries the stored CanvasWidth/CanvasHeight, read at either BSON integer
width, as pages (#541) and snippets (#705) already do. A new layout
still gets 1280 x 800. Found while proving mendixlabs#1411.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01W3x8Fyvir8kLyPgJs1BDmF
Brings in mendixlabs#1478 (search finds documentation and log
messages). Conflict in CHANGELOG.md [Unreleased] resolved by keeping
both the Changed (mendixlabs#1474) and Fixed (mendixlabs#1477) entries.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01U6JPskrhXuCjJVz4JRtcdX
fix(visitor): strip quoted identifiers that leaked into stored expressions and XPath
fix: keep a layout's stored canvas size on rewrite
…endixlabs#151)

main took MDL-WIDGET44 for the tab page property rule (mendixlabs#1465)
while this branch was open; TestRuleIDHasOneOwner would refuse the merge.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BrnskoDgU7F6hnHjkrQywD
Sync mendixlabs/mxcli main into ako/mxcli
Unmodified starting point for mirroring mendixlabs/mxcli issues into the
fork and triaging them; adapted for mxcli in the next commit so the diff
shows what changed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01945Pfw7pcoAjvd2HNQJWyK
Skill: fill in the config (label-only), limit triage to mirrors so the
fork's own tracker is left alone, ground "spirit" in CLAUDE.md, the ADRs
and docs/11-proposals, use docs-wiki/bug-patterns as cluster vocabulary,
and define urgent by the invariants that lose data or break opening a
project (GUID re-mint, $ID renumbering, wrong $Type, dropped properties).

mirror.py:
- refuse any non-GET to the upstream repo in code; the routine's session
  holds upstream credentials, so the prompt alone is not a guard
- stop re-posting the "upstream closed, work in progress" comment on
  every run once a mirror is labelled upstream-closed
- title mirrors [up#N] instead of [#N], which names a different fork issue

Checked against a fake gh serving fixtures (create, refresh, closed,
deleted, busy, rerun, guard); not yet run against live upstream.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01945Pfw7pcoAjvd2HNQJWyK
feat(pages): FILEMANAGER widget for the classic file manager (mendixlabs#151)
Add issue-triage skill for mirroring and classifying upstream issues
A cloud session checks each repo out at /home/user/<name>, so it cannot
attach both ako/mxcli and mendixlabs/mxcli, and unauthenticated calls to
api.github.com are refused from it. The routine therefore cannot read
upstream and write the fork in one run.

mirror.py needs no model, so it moves to an hourly workflow on the fork,
where github.token reads the public upstream and writes fork issues. That
token also keeps ai-issue-review.yml from firing once per new mirror,
which a personal token would do. The job only runs in ako/mxcli, since
this file travels upstream with fork PRs; a manual run defaults to
--dry-run.

The skill now only triages: it checks the workflow's last run and
reports when mirroring has stalled, and never calls upstream.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01945Pfw7pcoAjvd2HNQJWyK
Mirror upstream issues from GitHub Actions, not the triage routine
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.

2 participants