Sync ako/mxcli: widget bindings and action slots, nanoflow expression checks, diag invocation accuracy - #1172
Conversation
…ionParameterValue
`call javascript action` with an entity-type parameter wrote
Microflows$BasicCodeActionParameterValue{Argument: "Mod.Entity"} where
Studio Pro stores
Microflows$EntityTypeCodeActionParameterValue{Entity: "Mod.Entity"}.
The Basic shape leaves the Studio Pro entity picker empty and fails the
build with CE0115.
addCallJavaScriptActionAction never looked the action up; it hardcoded
Basic for every parameter, with the bug stated as a comment. It now
resolves the signature through ReadJavaScriptActionByName (already on the
backend interface) and promotes parameters typed
CodeActions$EntityTypeParameterType — the generic `entity <>` slot —
mirroring the Java-action builder, which has drawn this distinction since
#656. A parameter typed to a concrete entity keeps the Basic
shape, and without a backend the prior behaviour stands.
The read, write and DESCRIBE paths already handled the correct type, so
the change is confined to the builder.
Measured on mxbuild 11.6.6 against testdata/expr-checker/minimal.mpr
(which ships NanoflowCommons), two builds of the same script:
fixed -> "The app contains: 0 errors."
faulty -> CE0115 "The arguments that are passed to JavaScript action
'NanoflowCommons.RefreshEntity' do not match the expected
parameters and need to be refreshed."
and the BSON on disk carries $Type
Microflows$EntityTypeCodeActionParameterValue / "Entity" fixed, versus
Microflows$BasicCodeActionParameterValue / "Argument" faulty — the two
snippets the report quotes.
Control: with the type check stubbed out, the new executor test fails with
the reported symptom ("value = *microflows.BasicCodeActionParameterValue,
want *EntityTypeCodeActionParameterValue") while both its controls still
pass. DESCRIBE renders the broken and the correct nanoflow as identical
MDL, so the regression test asserts on the BSON $Type rather than on a
round trip.
Fixes mendixlabs#1137
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ub4kEQmBAN1zk18fjNQppR
`alter page M.P { set 'createFileAction' = microflow M.F on fileUploader1; }`
failed to parse (mendixlabs#995):
line 2:37 extraneous input 'MyModule' expecting {DROP, ADD, SET, INSERT, REPLACE, '}'}
mendixlabs#956 made named action slots writable on CREATE PAGE, but alterPageAssignment
only accepted an action expression for the key `Action`, so retargeting one
slot meant REPLACEing the whole widget and restating every other property.
- grammar: `'<key>' = actionExprV3` and `<key> = actionExprV3`, ahead of the
scalar alternatives as on CREATE
- executor: an action keyed anything but `Action` routes to the new
PageMutator.SetWidgetNamedAction; refused on a column or page-level target,
where the setters would stringify it
- pagemutator: writes Value.Action (the field widgetobj.Builder.SetAction
writes) and refuses a key whose PropertyType is not Action-typed, naming the
widget's action slots. Every WidgetValue carries an Action field, so field
presence would accept an Integer property and change nothing
- MCP backend refuses, as it does for SetWidgetAction
Measured on 11.12.1: retargeting DataGrid 2's onSelectionChange via ALTER
keeps PageSize and passes `mx check` with 0 errors.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YVdoySKj31N5PZmjrAGeEh
) DESCRIBE JAVA ACTION printed `ContextObject: entity <>` for a parameter declared `entity <pEntity> not null`. The stored type holds only a BY_ID pointer to the TypeParameter; javaActionFromGen carried the ID across but never resolved the name, which is all the describer prints. The JavaScript action reader already had this pass. Bare type-parameter references in parameters and the return type are resolved the same way. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011VrrQgJ6aXdEJ8Db1fQx5G
…abs#1033) `currentDeviceType()` in a nanoflow passed `mxcli check` and `mxcli exec`, then failed the build with CE0117 "Error(s) in expression." The mendixlabs#828 fix wired MDL044 to CREATE MICROFLOW only. Separately, `log` message, node and template expressions were never checked for MDL044 in either flow type, and that is where the reported repro puts the call. - checkStmtExprFunctions: the single list of MDL044 expression sites, now including log statements, shared by walkBody and the nanoflow walker - ValidateNanoflow runs MDL044 only. The full microflow rule set would be a false positive here: MDL057 refuses `synchronize`, which nanoflows allow - wired into check (validate_program), the LSP, and exec (validateNanoflowRules in buildNanoflowFromStmt, same allowlist as microflows) Verified on mxbuild 11.13.0: the pre-fix binary writes the repro and the build reports CE0117; the fixed binary refuses it in check and exec. The new sites' positive forms (toUpperCase in a log template, toString([%CurrentDateTime%])) build at 0 errors. The report's workaround `[%CurrentDeviceType%]` is itself CE0117 in both flow types; the skill notes that. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01M1J643ZJw61bNaUSDbZkBj
…s#1024) mendixlabs#1024 reported that `jump to A;` in a boundary-event body was written as a jump NAMED A: [CE0495] "Duplicate name 'A'." at User task 'A', Jump 'A' [CE6680] "The 'Target' property is required." at Jump 'A' This has the same root cause as mendixlabs#1005, and 825873d already fixed it. The report came from v0.20.0, which predates that commit. Measured on a blank 11.14.0 project with native mx check: 825873d^ 2 errors (CE0495 + CE6680, verbatim) 825873d 0 errors main 0 errors The mendixlabs#1005 tests only put jumps in outcome flows, so this adds coverage for the boundary-event body (user task and wait for notification), plus a bug-test script and a finding. Controls: the unchanged test fails with the reported duplicate-name and self-target symptoms against 825873d^. On main it fails the same way only when BOTH mendixlabs#1005 guards are stubbed (jump named after its target, and jumps deduplicated last). Either guard alone keeps it green. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012owAFCYmF2aAUY2S5phmGQ
… duplicate `describe page` on a page with several galleries emitted MDL that mxcli's own `check --references` rejected. mendixlabs#978, third symptom: - duplicate widget name 'template1' (used 4 times) — Mendix requires unique widget names per page (CE0495) The row/col names in the same report were fixed by 587ed31; this one was not covered. `template` and `filter` inside a gallery are child slots of its definition. applyChildSlots stores only the block's children in the slot property and discards the block's name, which is why DESCRIBE synthesises `template1` for every gallery. Measured on 11.12.2: a block authored as `template contentZebra` appears in 0 stored files and describes back as `template1`; four galleries each holding `template template1` pass `mx check` with 0 errors. The check already exempted the enclosing widget's object-list containers; it now exempts its child-slot containers too (definition and every mode), plus the `container <slotName>` spelling that applyChildSlots routes by name. Widgets inside a slot still count, and a standalone `template` (a Forms$DivContainer, which does store its name) is still checked. Control: with the fix reverted, the new test fails with the reported line, `duplicate widget name 'template1' (used 4 times)`. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Qf5Bk8ZMQkwCNEheapPzq4
…ted them A second test project reported all 5 `test` runs as "did not close" while every test passed, reading it as "the command skips the summary record on success too". It does not: `test` returns normally, PersistentPostRun fires, and the session_end is written. The report gave it to the wrong invocation. **mxcli runs mxcli.** Measured from a real run: session_start pid=1071 test t.test.mdl --skip-build -p … session_start pid=1079 -p …/minimal.mpr -c DESCRIBE SETTINGS session_end pid=None session_start pid=1086 -p …/minimal.mpr -c SHOW MODULES session_end pid=None session_start pid=1093 exec /tmp/mxtest-runner-….mdl -p … Three child processes before the first test executes; `new`, `eval`, `tui` and the LSP self-spawn the same way. buildInvocations segmented on "next session_end OR next session_start, whichever comes first", so a child's start closed the parent and the parent's own end — arriving last — landed on whatever was open by then. session_end carried no pid, so pairing by process was impossible. The rule called itself exact "for sequential invocations, which is what an agent loop produces"; the tool itself is what breaks that. Two consequences, both fixed: the parent read as a non-zero exit, and the three children were counted as calls the agent made — inflating that log's `-c` (111) and `exec` (180) counts. session_end now carries its pid and pairing is by process, with the positional rule kept as the fallback so older logs still read correctly. session_start carries parent_pid, and the marker rides the ENVIRONMENT rather than each spawn site: a child inherits it through exec.Command, so one os.Setenv in Init covers all six self-spawn sites and any added later without touching them. A spawned run is excluded from the table and from wall time — its seconds are already inside its parent's — and reported on its own line. Proven by revert on the measured record shape: the positional rule gives Invocations=4 (want 1), Unclosed=1 for a parent whose tests all passed, and Wall=3 instead of 10. Three controls: an old-format log with no pids still pairs positionally, a top-level run carrying a pid is not treated as spawned, and an orphan session_end from outside the window is dropped rather than applied to whichever invocation is open. Closes #629 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MgcYSQrLLbUcnAMaCHpyqQ
…t cannot parse
`mxcli syntax page datasource` printed `MICROFLOW Module.MF($P)`. Measured:
dataview dv (datasource: microflow M.DS_X($State)) -> line 2:15 no viable
alternative
dataview dv (datasource: microflow M.DS_X(State: $State)) -> Syntax OK
Arguments must be named. Reported independently from a test project, found
from the parse error rather than from the doc.
CLAUDE.md points at `mxcli syntax` as authoritative instead of restating
syntax, precisely so it cannot go stale — which makes a wrong entry there the
thing consulted INSTEAD of checking, and puts the cost in the agent loop:
read it, write it, fail to parse, diagnose, retry.
The systemic guard is deliberately not built here. Measured first: 42 of 164
syntax examples fail `mxcli check`, but the large majority are fragments by
design — a microflow body, a widget snippet, an OQL fragment — which are
legitimately not standalone MDL. A blanket "every example must parse" test
would be mostly noise; making it useful needs a way to mark which examples
stand alone. Left in #630 with that measurement.
Closes #630
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MgcYSQrLLbUcnAMaCHpyqQ
fix(executor): entity-type JS action parameters get EntityTypeCodeActionParameterValue
fix(alter-page): SET can address a pluggable widget's named action slot
fix(describe): resolve java action type-parameter names (mendixlabs#1034)
fix(check): a gallery's template/filter block is a slot, not a CE0495 duplicate
A listview that does not say `editable: true` is written Editable false, and its read-only context wins over `editable: Always` on the inputs inside it, including inside a nested data view. Valid BSON, clean mx check, clean build; only the running app shows it. The default is kept: false is Mendix's own (mendixmodelsdk 4.115.0, Pages$ListView.editable defaults to false and _initializeDefaultProperties does not set it). Instead, MDL-WIDGET31 warns on a list view that will be written non-editable while it holds an input not marked `editable: Never`. Editability is read with GetBoolProp, as buildListViewV3 does, so a quoted `editable: 'true'` (written false) warns too. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01266huGoXavWeZvPq8BV7LT
Closes #633. `mxcli test`, `check` and `exec` with no arguments wrote no session_start at all, so `diag loop-report` never saw them: probe before after mxcli test 0 1 mxcli check 0 1 mxcli exec 0 1 mxcli check /nonexistent.mdl 1 1 (control) #617 opened the session from PersistentPreRun, but cobra runs ValidateArgs (and answers --help/--version) before any hook. The session now opens in main() before Execute, named from rootCmd.Find(os.Args[1:]); diag stays excluded. Two things had to move with it: - The close. --help and --version return nil without running PersistentPostRun, so with the earlier open every help lookup would have been left unclosed, which the report reads as a failed run. main() now closes after a nil Execute; os.Exit paths still leave no session_end. - The root's mode. Whoever reaches the singleton first names the session, and PreRun had been recording -c and the REPL as "mxcli" instead of "batch"/"repl", the names invocationVerb falls back to. startSession resolves those, plus "help"/"version" for the root flags. The tests re-run the test binary as main(), the only layer that sees the os.Exit path. Controls: stubbing startSession brings back 0/0/0 while the /nonexistent.mdl control still passes. Moving the close back to PostRun gives 0 session_end for check --help, --help and --version. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FM9DSqkPmhW5KcLtQLgsXn
…scribe `captionparams` on an action button was accepted by `mxcli check` but not written (#632). Two silent drops: - buildButtonV3 had its own copy of the template-parameter resolver that wrote a bare attribute (`[{1} = Title]`) as the literal 'Title'. It now uses the shared buildClientTemplateParams, like dynamictext and grid columns, which also brings SourceVariable, association paths and per-parameter format blocks to button captions. - DESCRIBE printed a button's parameters as `ContentParams:`, which the button builder never read, so describe -> exec dropped them all and mx check reported CE0720. DESCRIBE now emits `CaptionParams:`, and the builder still reads `ContentParams:` for scripts dumped before this fix. MDL-WIDGET04's orphan-placeholder check now covers action/link button captions, so a `{1}` with no parameter is refused at check time instead of failing the build with CE0720. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016kS7YrCUmW7uFQ1PK6zjkf
) ako/TestApp (Mendix 11.14.0), authored in Studio Pro: 30 of its 32 list views are stored Editable false and none of those holds an input; the one list view with inputs (Pages.EditableLIstView) was set to Editable true. Its describe output checks clean, deleting `Editable: true` from it fires MDL-WIDGET31, and describe -> exec preserves the property. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01266huGoXavWeZvPq8BV7LT
…uo4i # Conflicts: # CHANGELOG.md
test(workflow): pin `jump to` inside a boundary-event body (mendixlabs#1024)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01M1J643ZJw61bNaUSDbZkBj
fix(check): run MDL044 over nanoflow bodies and log messages (mendixlabs#1033)
Two defects a second test project turned up, and three more filed with their measurements
fix: action button captionparams bind a bare attribute and survive describe
fix: a command called with the wrong number of arguments writes no session record
fix(check): warn when a list view renders its inputs read-only (MDL-WIDGET31)
AI Code ReviewLet me analyze this pull request against the review checklist. First, I'll look at the PR description to understand what it's about: The PR is titled "Sync ako/mxcli: widget bindings and action slots, nanoflow expression checks, diag invocation accuracy" Looking at the body, it contains several fixes: Page and widget authoring
|
12 commits from the fork's
main, on top of the last sync (c127385). Grouped by theme.Page and widget authoring
d85c65a, fixes CALL JAVASCRIPT ACTION with entity-type parameter produces wrong BSON type (BasicCodeActionParameterValue instead of EntityTypeCodeActionParameterValue) #1137) —call javascript actionwroteBasicCodeActionParameterValue{Argument}where Studio Pro storesEntityTypeCodeActionParameterValue{Entity}, leaving the Studio Pro entity picker empty and failing the build with CE0115. The builder never looked the action up; it hardcoded Basic for every parameter, with the bug stated as a comment. It now resolves the signature and promotes parameters typed to the genericentity <>slot, mirroring the Java-action builder. Measured on 11.6.6: 0 errors fixed, CE0115 faulty, with the two BSON shapes the report quotes on disk. DESCRIBE renders the broken and the correct nanoflow as identical MDL, so the regression test asserts on the$Typerather than on a round trip.328828c, ALTER PAGE SET cannot address named pluggable-widget action slots (createFileAction etc.) — follow-up to #956 #995) —set 'createFileAction' = microflow M.F on fileUploader1failed to parse. Named action slots were made writable on CREATE, butalterPageAssignmentonly accepted an action expression for the keyAction, so retargeting one slot meant REPLACEing the whole widget and restating every other property. The mutator refuses a key whose PropertyType is not Action-typed and names the widget's slots, since everyWidgetValuecarries anActionfield — field presence would accept an Integer property and change nothing. Measured on 11.12.1: retargeting DataGrid 2'sonSelectionChangekeeps PageSize and passesmx checkat 0 errors.captionparams(edfad3f, closes captionparams on an action button passes check and is never written ako/mxcli#632) — accepted bycheckand not written. Two silent drops: the button builder had its own copy of the template-parameter resolver, which wrote a bare attribute as a literal, and DESCRIBE emittedContentParams:, which that builder never read — sodescribe → execdropped them all andmx checkreported CE0720. The shared resolver also brings SourceVariable, association paths and per-parameter format blocks to button captions, and MDL-WIDGET04's orphan-placeholder check now covers them.f275a58, DESCRIBE PAGE output is rejected by mxcli's own check --references (duplicate derived widget names) #978, third symptom) —describe pageon a page with several galleries emitted MDL that mxcli's owncheck --referencesrejected for a duplicate widget nametemplate1.templateandfilterare child slots of the gallery's definition: the block's name is discarded on write, which is why DESCRIBE synthesises one per gallery. Measured on 11.12.2: a block authored with a name appears in 0 stored files, and four galleries each holdingtemplate template1passmx checkat 0 errors. Widgets inside a slot still count, and a standalonetemplate— a real container that does store its name — is still checked.0dc5c0f,1baabaf, List views are written with Editable: false, so every input inside one renders disabled ako/mxcli#631) — a listview that does not sayeditable: trueis written non-editable, and its read-only context wins overeditable: Alwayson the inputs inside it, including inside a nested data view. Valid BSON, cleanmx check, clean build; only the running app shows it. The default is kept, since it is Mendix's own — instead MDL-WIDGET31 warns. Editability is read the way the builder reads it, so a quotededitable: 'true'(written false) warns too. Pinned against Studio Pro-authored content at 11.14.0: 30 of 32 list views are stored non-editable and none of those holds an input; the one with inputs was set editable.checkcatches more before the buildf9a781f, MDL044 write barrier (currentDeviceType()validation) only runs forCREATE MICROFLOW, notCREATE NANOFLOW— the identical invalid expression passescheck, is written, and fails the build withCE0117#1033) —currentDeviceType()in a nanoflow passedcheckandexec, then failed the build with CE0117. The earlier fix wired MDL044 to CREATE MICROFLOW only, andlogmessage/node/template expressions were never checked in either flow type — which is where the reported repro puts the call. One list of expression sites now serves both walkers.ValidateNanoflowruns MDL044 only: the full microflow rule set would be a false positive here, since one of its rules refuses something nanoflows allow. Verified on 11.13.0 both ways; the report's own suggested workaround is itself CE0117 in both flow types, and the skill now says so.DESCRIBE
7c158bc,DESCRIBE JAVA ACTIONdrops the type-parameter name (printsentity <>instead ofentity <pEntity>) — the declaration does not round-trip #1034) —DESCRIBE JAVA ACTIONprintedentity <>for a parameter declaredentity <pEntity>. The stored type holds only a pointer to the TypeParameter and the reader carried the ID across without resolving the name, which is all the describer prints. The JavaScript action reader already had this pass.Workflows
jump toinside a boundary-event body (cb52ae7,JUMP TO <activity>inside a boundary-event body writes a Jump with noTarget, named after its target — so an interrupting boundary event cannot be expressed correctly #1024) — reported as a jump written named after its target (CE0495 + CE6680). Same root cause as Workflow: danglingjump totarget is silently stored as a self-referencing jump (check + exec pass, native mx check fails CE6681) #1005, and already fixed; the report came from a release predating that commit. Measured on a blank 11.14.0 project: 2 errors before the fix, 0 at the fix, 0 on main. The existing tests only put jumps in outcome flows, so this adds boundary-event-body coverage, a bug-test script and a finding. The control is two-sided: on main the test fails only when both guards are stubbed — either alone keeps it green.diag loop-reportaccuracyTwo defects that made the report describe a different set of runs than the agent actually made.
63588cd, closes diag loop-report: mxcli's own child processes are counted as loop calls, and their parent reads as unclosed ako/mxcli#629) — a project reported all 5testruns as "did not close" while every test passed.testcloses its session correctly; the report gave the record to the wrong invocation. Measured: three child processes spawn before the first test executes, andnew,eval,tuiand the LSP self-spawn the same way. Segmentation closed a parent on a child's start, and the parent's own end — arriving last — landed on whatever was open by then. Two consequences, both fixed: the parent read as a non-zero exit, and the children were counted as calls the agent made, inflating that log's-candexeccounts. Pairing is now by pid with the positional rule kept as a fallback, and the marker rides the environment rather than each spawn site, so it covers all six and any added later. Proven by revert on the measured record shape, with three controls.e7862f5, closes A command called with the wrong number of arguments writes no session record at all ako/mxcli#633) —test,checkandexecwith no arguments wrote no session record at all, because cobra runs argument validation and answers--help/--versionbefore any hook. The session now opens inmain(). Two things had to move with it: the close, since--helpreturns without running the post-hook and would otherwise leave every help lookup reading as a failed run; and the root command's mode, since whoever reaches the singleton first names the session and-cand the REPL were being recorded as neither.Syntax reference
page.datasourcedocumented a form that cannot parse (45e0ae8, closes syntax page.datasource documents a microflow-argument form that is a parse error ako/mxcli#630) — it printed a microflow datasource with a positional argument; arguments must be named. Found from the parse error rather than from the doc. CLAUDE.md points atmxcli syntaxas authoritative instead of restating syntax, precisely so it cannot go stale — which makes a wrong entry the thing consulted instead of checking, and puts the cost in the agent loop. The systemic guard is deliberately not built here, and the reason is measured: 42 of 164 examples failcheck, but the large majority are fragments by design, so a blanket "every example must parse" test would be mostly noise until there is a way to mark which examples stand alone.