Repository navigation
Sync ako/mxcli: Mendix 9.24 nightly and pluggable widgets, describe module with all, JS action regeneration - #1457
Conversation
… sections Add a separate test-mendix-9 job to the nightly workflow running the integration suite against Mendix 9.24.30.51827. It is not a `test` matrix entry because the `nightly` release job needs `test`, and 9.x still has known writer bugs; continue-on-error keeps a red run from failing the workflow. The job installs libgdiplus (mxbuild 9 needs System.Drawing) and pins JAVA_HOME to the runner's JDK 17. Fold it into the matrix once it is green. nightlyMatrix in doctype_version_gating_test.go mirrors nightly.yml: add 9.24.30.51827, and the 11.14.0 / 11.15.0 entries it was missing. Gate the doctype statements that mxcli correctly refuses on 9.24: - 06c-rest-client-mendix10: whole script 10.1+ (consumed REST clients) - 22-published-rest-service: PART 2 onward 10.0+ (registry floor) - 30-module-jar-dependencies: whole script 10.0+ (JarDependencies) - 34-chart-widget: view entities and every chart page bound to them 10.18+ - microflow-document-properties: the URL / DROP URL statements 10.6+ - 14-project-settings: JavaVersion split into its own 10.8+ alter; the 10.6/10.11/10.21 runtime settings split out of Example 1.5 as 10.21+ Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01W3x8Fyvir8kLyPgJs1BDmF
describe business event service wrote ServiceName and EventNamePrefix as a hand-rolled `'%s'`, so a value with an apostrophe (`Bob's orders`) came out as a statement that does not parse, and re-executing the describe output failed. Both go through mdlQuote, which writes the literal for the describe language. cmd_businessevents.go is added to the hand-rolled-literal source scan; the new test re-parses the describe output under mdl 0 and mdl 1 and compares the values, and fails with the reported parse error when the fix is reverted. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01P65SqmwwvbWdJVRwhYiMQw
No issue filed: found running the doctype suite on Mendix 9.24.30. Every page, snippet or alter page holding a pluggable widget failed to write: failed to create page: CreatePage: insert: unit ...: this Mendix 9.24.30 project cannot store CustomWidgets$WidgetValueType.DefaultType (Mendix 10.15.0+): the property did not exist yet in this version... The widget Type is copied from the embedded 11.6 templates, whose every CustomWidgets$WidgetValueType carries six keys Mendix introduced after 9.24 (mendixmodelsdk 4.116.0 WidgetValueType.versionInfo): SetLabel 10.5, IsLinked / IsMetaData 10.14, DefaultType 10.15, ActionVariables 10.21, AllowUpload 11.8. The property floor drops the empty ones and refuses a value: DefaultType is "None" everywhere, and IsLinked / IsMetaData / SetLabel / ActionVariables hold values on the combo box, dropdown sort and data grid filters, so they were next in line. The Type is the widget package's schema, not anything the statement said, so at template load (LoadWidgetTemplate, BuildFilterWidget) every CustomWidgets$ key the project's version does not declare is dropped, whatever it holds, using the floor's own version data (new canon.PropertyDeclared). The floor is unchanged and still refuses values in the widget's Object. codec.Encoder.OmitKeys cannot do this: the Type is decoded from raw bytes and the encoder passes clean fields through without consulting it. The 9.24 result equals Studio Pro 9.24.30's WidgetValueType key set (a Studio Pro-authored 9.24.30 app); an unreadable version and 11.8+ keep the template unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01W3x8Fyvir8kLyPgJs1BDmF
ci: run the integration suite nightly on Mendix 9.24 (non-gating)
fix: escape a business event service's ServiceName in describe
…ndix9 fix: write pluggable widget Types in the shape the project's Mendix version declares
…bs#1441) Reported: `marketplace update 109515 --to 5.2.1` on an untouched NanoflowCommons refused with refusing to update: 44 element(s) have been changed locally and the update would discard them: JAVASCRIPT_ACTION Base64Decode and `marketplace diff` showed "Locally modified (44 of 50 elements)". The finished-reference cache kept only the model: isModelFile dropped javascriptsource/ and javasource/ as "never read". DESCRIBE JAVASCRIPT ACTION and DESCRIBE JAVA ACTION read the action source beside the .mpr and print a "source not available" placeholder when it is missing. A cache miss compared real code with real code; a cache hit compared the placeholder with the project's code, so every action read as edited. - isModelFile keeps javascriptsource/ and javasource/. - The entry name carries a format segment (refEntryFormat "f2"), so a model-only entry written before this fix is never served; old entries age out through the existing prune. - Element.Conclusive treats the omitted-source placeholder as inconclusive, so a missing source file can only ever be Unknown (which still needs --force), never "changed locally". The gate is otherwise unchanged; the GUID transplant path is untouched. Test: TestCachedReferenceDescribesActionSourcesLikeAFreshOne diffs the same untouched module against a fresh reference (miss) and the cache-restored one (hit); both must be Unchanged. With the old filter the hit reports both actions modified (unknown with the new gate). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01W3x8Fyvir8kLyPgJs1BDmF
…laration When two modules declare the same artifact at different versions (mendixlabs#1450: ExcelImporter commons-lang3 3.18.0, CommunityCommons 3.20.0), `mx sync-java-dependencies` runs Gradle, whose conflict resolution vendors only the newest. UnvendoredJarDependencies checked each declaration, so the older one was "missing" forever: --check failed, the sync deleted the jar and errored "still missing", and run --local re-synced on every start. Collapse declarations to the newest version per group:artifact, compared the way Gradle orders versions, before checking vendorlib/. sync-java-deps lists the dropped declarations as "superseded" instead of "missing". Measured on 11.12.1 (mxcli new + two modules): vendorlib/ receives commons-lang3-3.20.0.jar only; the pre-fix binary prints the reported error verbatim, the fixed one passes --check and the sync. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PjL2dUU7gkzBzz9SeMru7H
mendixlabs#1429) Reported: "Describe javascript action keeps EXTRA CODE only as a comment; re-running `create or modify` overwrites a hand-restored `.js`" — an edited body regenerated the file from the statement alone, deleting the EXTRA CODE helpers and the import list the user code calls. WriteJavaScriptSourceFile now reads the existing .js and carries its import list (multi-line and semicolon-less statements included) and the BEGIN/END EXTRA CODE section into the regenerated file, as Studio Pro does and as the Java writer already does (#705). No interface change. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01W3x8Fyvir8kLyPgJs1BDmF
…labs#1441) TestNoBareHomeSetenv refuses a bare Setenv("HOME"): os.UserHomeDir reads USERPROFILE on Windows, so the override would leave the real ~/.mxcli in use. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01W3x8Fyvir8kLyPgJs1BDmF
…1422) Reported: "check accepts `change $E (...)` / `$E = empty` where `$E` comes from `retrieve ... limit 1` (a list under mdl 1); mx check then fails" with CE1613 "The selected attribute 'List of M.Ent.Attr' no longer exists.", and after changing `limit 1` to `first`, `create or modify` reported "spliced: 1 replaced" while the CE1613 remained. - Builder: addChangeObjectAction refuses a change over a list variable instead of qualifying the member through "List of …". - Check: new MDL-RETRIEVE03, the reverse of MDL-RETRIEVE01 — a database retrieve without the object range, then `change $E` (CE1613) or a `$E/Attr` path in any later expression (CE0117). Promoted to exec. `set $E = empty` over the list is a Change list Replace and builds (measured on mx 11.6.6), so it is not reported. - Splice: a stored change member qualified through a list type is not spliceable (like #1418's stored import without entity): mdl 0 rebuilds the flow, repairing it; mdl 1 refuses with the reason instead of keeping the stored bytes. describe adds a -- WARNING for that member. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01W3x8Fyvir8kLyPgJs1BDmF
) Reported: "`describe module <M> with all` omits whole document types" -- nanoflows, snippets, JS actions, JSON structures, import/export mappings, task queues, image collections -- with no error. On PedApp it emitted 0 of FeedbackModule's 11 nanoflows (20 of its 49 documents). The WITH ALL branch was a hand-written section per document type covering 13 types, each wrapped in `if err == nil`, so a failed list or describe was skipped silently too. - One ordered registry of describable document kinds (describe_module_all.go), each rendered through describeByKind, the dispatch extracted from execDescribe, so the dump holds exactly what each document's own describe prints. Order is dependency-first: enumerations, constants, consumed OData, domain model, leaves (regex, images, icons), structures and mappings, connections and consumed services, task queues, Java/JS actions, rules, microflows, nanoflows, layouts, snippets, building blocks, pages, menus, workflows, published services, scheduled events, agent-editor documents. - A type-blind ListDocumentUnits pass prints `-- not describable: <kind> M.N` for any module unit no entry emitted (page templates today). - Read-only reports (building block, icon collection) are emitted commented out, so the dump still parses. - A list or describe failure prints `-- describe failed: <what>: <err>` and fails the statement after the rest of the module is written, as the output line limit does (mendixlabs#1425). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01W3x8Fyvir8kLyPgJs1BDmF
fix: keep a JavaScript action's imports and EXTRA CODE when its body changes (mendixlabs#1429)
fix: keep action sources in the marketplace reference cache (mendixlabs#1441)
…ixlabs#1422) TestPedAppRoundTrip_RetrieveRange swapped `first` for `limit 1` under `mdl 1;` on a microflow that then reads `$CurrentUser/Name` — a member path over a list, CE0117, which MDL-RETRIEVE03 now refuses. The test now asserts that refusal (nothing written), then makes the list form valid by reading the name from `$currentUser` and rebuilding with drop + create (the change activity shares annotation n1 with the retrieve, so the splice refuses to replace it). Range storage, describe, PutGet and the headerless control are unchanged. The write-microflows skill's complete example retrieved `$Order` without a range and used it as an object; it now says `first`. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01W3x8Fyvir8kLyPgJs1BDmF
The RETRIEVE03 example fix added three lines to a skill body that was exactly at the limit, failing TestLargeSkillsWereSplit (703 > 700). Same content, folded into the existing comment line and the where clause. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01W3x8Fyvir8kLyPgJs1BDmF
…arses Found while fixing mendixlabs#1421 (PR #1169), related to mendixlabs#1404. Symptom: `describe javascript action` on an action with type parameters printed `NanoflowCommons.GetGuid<TypeParameter>` and `entity <>`, which do not parse ("mismatched input '<' expecting {FOLDER, '('}"), so `describe module FeedbackModule with all` did not parse either. - Drop the `Name<T>` header: as for Java actions, a type parameter is declared by its `entity <T>` parameter. - Shapes MDL has no form for — a parameter typed by T with no `entity <T>` parameter (#1404), an entity selector bound to no type parameter (null TypeParameterPointer), or stored type parameters that are not the declared ones in order — are printed commented out under `-- NOT re-executable:`, so the script parses and re-running it leaves the action unchanged. - exec wrote `list of T` as a list of the entity `.T` (the JavaScript twin of #1183); it now uses listOfTypeParameter for parameters and returns. Ten javascript action entries are struck from each roundtrip allowlist. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01W3x8Fyvir8kLyPgJs1BDmF
fix(sync-java-deps): check the Gradle-resolved version, not every declaration
fix: refuse a list retrieve used as an object, and repair one already stored (mendixlabs#1422)
…ls (#1164) The test runner attached its ON ERROR handler only to CALL MICROFLOW and CALL NANOFLOW, so an error raised by CALL JAVA ACTION escaped the generated test microflow: an @throws test was reported ERROR instead of PASS, and an unexpected error was ERROR instead of the usual "exception during execution: ..." FAIL. Both runners now wrap every call-shaped activity the grammar gives an onErrorClause: call microflow / nanoflow / java action / external action / web service / workflow / rest service, send rest request, and execute database query. The match is anchored at the statement start, so a LOG whose text mentions a call is no longer treated as one. Verified live on Mendix 11.12.1 (--local --db-type hsqldb) against the same project: unfixed runner ERROR/PASS/ERROR, fixed PASS/PASS/FAIL. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BDw5ToBMR2AVJomKZy6KWW
fix: describe JavaScript actions with type parameters as parseable MDL
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01W3x8Fyvir8kLyPgJs1BDmF
The exemption covered JavaScript actions with type parameters describing as unparseable MDL; #1170 fixed that, so the module is now held to the same parse assertion as the others. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01W3x8Fyvir8kLyPgJs1BDmF
fix(test): @throws catches errors from CALL JAVA ACTION and other calls
fix: describe module … with all emits every document type, and reports failures (mendixlabs#1421)
AI Code ReviewReview SummaryThis PR syncs 14 commits from a fork to implement several improvements to mxcli, primarily focused on making the Critical IssuesNone found. Moderate IssuesNone found. Minor Issues
What Looks Good
RecommendationApprove. The PR delivers high-quality, well-tested improvements that address significant reliability gaps in mxcli's core functionality. The describe module enhancement is particularly noteworthy for its thorough approach to completeness verification. All changes follow established patterns and include appropriate test coverage. The minor maintenance concern with the document type registries is adequately mitigated by existing tests. Automated review via OpenRouter (Nemotron Super 120B) — workflow source |
…ixlabs#1444) Reported: "Commands without `-p` silently pick a stray `.mpr` in the working directory" -- `mxcli describe module M` run in a directory holding a stray .mpr proceeds against it with only a "Using project" notice. Auto-detecting the sole .mpr in the working directory stays: it is how init, new, theme and the LSP resolve a project, and `cd app && mxcli ...` depends on it. The defect was that the guess read exactly like an explicit choice. The notice now names the absolute path, says the project was auto-detected, and how to choose another: Using project: /abs/dir/App.mpr (auto-detected in the current directory; pass -p to use another) With several .mpr files nothing is picked (as before), but stderr now lists them and asks for -p instead of leaving only "--project (-p) is required". That note respects -q / MXCLI_QUIET. The -p value set by auto-detection is still the bare file name, so nothing downstream sees a different path, and no exit code changes. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01W3x8Fyvir8kLyPgJs1BDmF
…ndixlabs#1225) Reported: `revoke ModA.User on ModA.Tag (write (Tag_GuestGroup))` answered "No access rules found matching ModA.User on ModA.Tag" (now "nothing to revoke") with exit code 0, and the association stayed ReadWrite, while `write (Label)` on the same rule worked. The revoke resolved member names through EntityMembers, which yields attributes only, and qualified anything else as `Module.Entity.Member`. An association's MemberAccess is stored as `Module.Association`, so the ref matched nothing and the backend's 0-modified count was reported as an already-revoked right. GRANT's association enumeration (FROM side via ParentID, both ends for OWNER Both, cross-module Both, inherited) moves into accessRuleAssociations, and REVOKE resolves names against it, so both writers agree on which rule carries an association and on its stored reference. A member the rule cannot carry — a typo, or the TO end of a Default-owner association — is now refused, as GRANT refuses it (#758), instead of the silent "nothing to revoke". A known member whose right is already gone keeps the idempotent Unchanged notice. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01W3x8Fyvir8kLyPgJs1BDmF
…xlabs#1408) Reported: "`exec` applies statements one at a time and cannot roll back." A statement that failed at write time left every earlier statement written - a created module and entity stayed, and a `drop` ahead of the failing `create` was not undone. exec is now all or nothing. A before-image journal (modelsdk/mpr/ journal.go) copies the .mpr (and any -wal/-journal) at the first write, from guardWrite, the choke point every write passes before it changes anything, and each mprcontents unit file the first time the run writes, replaces or deletes it. On failure the executor is closed (deferred writes land at Close) and the copies are put back, removing created files and directories. The write path itself is unchanged, so canon.Reconcile, write elision and identity carry behave exactly as before. Works for MPR v1 and v2. Not covered: --continue-on-error (deliberately per-statement), the REPL and -c, and generated .java/.js source beside the model. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01W3x8Fyvir8kLyPgJs1BDmF
fix: name the auto-detected project and refuse an ambiguous one (mendixlabs#1444)
fix: revoke matches association members, and refuses a member the rule cannot hold (mendixlabs#1225)
AI Code ReviewReview SummaryThis PR synchronizes multiple changes from a fork, addressing several distinct issues and improvements. The changes are generally well-implemented with appropriate test coverage. However, the PR combines multiple unrelated concerns, which violates the scoping guideline. Critical Issues
Moderate Issues
Minor Issues
What Looks Good
RecommendationApprove with the note that for future feature development (non-sync PRs), changes should be more narrowly scoped to single concerns. As a synchronization PR combining multiple fork PRs, the scope is understandable, and each individual fix is correct and well-tested. The improvements address real gaps in functionality and maintain consistency with project architecture and guidelines. Automated review via OpenRouter (Nemotron Super 120B) — workflow source |
fix: exec applies a script all or nothing (mendixlabs#1408)
AI Code ReviewCritical Issues
Moderate Issues
Minor Issues
What Looks Good
Recommendation Automated review via OpenRouter (Nemotron Super 120B) — workflow source |
Syncs
ako/mxcli:mainintomendixlabs/mxcli:main: 14 commits since #1448, landed in the fork through PRs ako#1160–ako#1171.Mendix 9
continue-on-error, so a red 9.x run never blocks the nightly release; it joins the matrix once green. 10.x-only doctype sections are gated, and the nightly-matrix mirror in the gating test also gains the 11.14.0 / 11.15.0 entries it was missing.WidgetValueTypekeys introduced after 9.24, so every page holding a pluggable widget failed to write on 9.24.describe module with all (#1421)
Code actions
create or modifyoverwrites a hand-restored.js#1429), as Studio Pro and the Java writer already do. Before,create or modifyoverwrote a hand-restored.js.@throwscatches errors fromcall java actionand every other call-shaped activity, not only microflow and nanoflow calls (mxcli test: @throws cannot catch an error from CALL JAVA ACTION (only CALL MICROFLOW/NANOFLOW get ON ERROR) ako/mxcli#1164).Check
changeand member paths over a list retrieve (retrieve … limit 1undermdl 1;is a list), which was CE1613 at build time (Check acceptschange $E (...)/$E = emptywhere$Ecomes fromretrieve ... limit 1(a list under mdl 1); mx check then fails, and a splice does not clear it #1422). The builder refuses it as well, and tests and the write-microflows skill keep list-of-one coverage valid.Marketplace and Java dependencies
marketplace updateon an untouched module no longer reports its JavaScript actions as locally changed (marketplace update --torefuses without--force, citing local edits nobody made #1441). The reference cache droppedjavascriptsource/andjavasource/.sync-java-depschecks the Gradle-resolved version (sync-java-deps requires a declared-but-unresolved dependency version, then rewrites vendorlib/ without it —run --localstalls on "Resolving 1 managed Java dependency" #1450). When two modules declare different versions of the same artifact, only the newest is vendored. The older declaration read as "missing" forever, sorun --localre-synced on every start.Describe
ServiceNameandEventNamePrefixare escaped, soBob's ordersround-trips