Repository navigation
[0.30.0] - 2026-11-02
Headline: Catch before the build what fails at build time or in production, and merge Mendix models from the command line. New security lint rules report mxbuild's CE2729 and the page data sources that fail for a role at Production security, lint --baseline reports only new findings, and mxcli git merge runs Studio Pro's own merge engine and lets you review each conflict before it lands. Alongside it: optional page parameters, a configurable microflow layout, --mcp working through Studio Pro's own MCP server alone, and faster catalog refreshes and describes on large projects.
Added
- SEC012 reports mxbuild's CE2729 before a build (ako#936) — a page a user role can reach that shows an attribute, or lists an entity, the role may not read. Reachability is per user role and measured against mxbuild: menu items the role may view, its home page (checked even when it may not view it, but not walked further), show-page actions, snippet calls, and microflows and nanoflows it may execute with their sub-microflows. On JTSBootLogboek with an access-less role it reports the same 27 attribute and entity errors mxbuild does, and nothing on FactoryManagement or JTSBootLogboek as they are, where mxbuild reports none. Association reads, System entities and URL-only pages are not covered yet.
- SEC011: a page data source whose XPath crosses an association the page's roles cannot read (ako#945, #1337, #1471) — at Production security such a page opens to an error dialog (
No access rights for Shop$Account/Shop.Order_Account) while mxbuild,checkandlintwere all clean. The rule checks each association step from the entity it starts on, per user role that can open the page, and reports it as an error with the member to grant. Measured on 11.15: an association is checked only where it is a member (its FROM entity, and its TO entity when the owner is Both), and an XPath inside an entity access rule is not checked at all, so neither is reported. - MPR012 covers every widget measured as CE0582 under the React client (ako#564) — besides the static and dynamic image it now reports the classic drop-down, the legacy data grid and the reference selector a project already holds, each with its replacement (
combobox, Data grid 2). The set is measured on 11.16 with mxbuild, controls included: the navigation tree, menu bar, file manager, scroll container, image uploader and the common input widgets build clean and stay silent. The rule is renamedReactUnsupportedWidget; its ID is unchanged. - DROP over
--mcpuses Studio Pro 11.16'sped_delete_document(ako#1851) — DROP of a constant, enumeration, microflow, nanoflow, page, workflow or business-event service calls the native delete tool. A server without it (Studio Pro before 11.16) gets an error naming the version it needs. mxcli lint --baseline <file>reports only new findings (ako#1328, #1455) — record a baseline with--update-baseline, commit it, and later runs report only the findings it does not have, with an exit code that follows them alone and aN new, M already known, K fixedsummary on stderr. Findings are matched on rule and document, not message, so rewording a rule does not make its findings new; a scoped run (-m,-d,-r) reports new findings but cannot update the baseline.- Lint permission rows name their access rule (ako#1437, #1539) —
permissions()andpermissions_for()rows carryaccess_rule_idandaccess_rule_index, so a rule can count an entity's access rules per setting or list one rule's module roles exactly, instead of guessing by grouping on XPath, default and rights. The catalog schema moves to version 31, so a cached catalog is rebuilt once. - Lint rules can read the data guideline rules asked for (ako#1453, ako#1301) — a microflow's
excludedandapply_entity_access, an enumeration'svalueswith their captions (so a rule can resolvegetCaption(Module.Enum.Value)),constant_values()for the run-configuration values, andproject_settings()(default language, OQL version, Java version, hash algorithm, time zone, rounding, decimal scale, optimistic locking, foreign keys, web client). The catalog schema moves to version 30, so a cached catalog is rebuilt once. - Configurable microflow and nanoflow layout (ako#1832) — a committed
layout-config.yaml(in.claude/or next to the.mpr) chooses howcreate microflow/create nanoflowandmxcli layout flowsplace what a script leaves unpositioned: presethappy-line(the default, unchanged) ortrue-right-false-down, and the optionsif-else: true-right-else-column(ELSE stacked as a column under its decision),case-split: first-right-rest-columns(a CASE's or type split's first outcome on the line, the rest as columns),guard: true-right,wrap: offandmax-row-width. A flow laid out in a non-default style records it as an@mxcli-layout:line in its documentation, which DESCRIBE andcreate or modifykeep using for that flow, so its description stays free of@positionwhen the project style changes.mxcli layout configshows the style in force;mxcli layout flows --style <preset>tries one. Without a config file nothing changes: measured on 137 flows, the geometry and DESCRIBE output are identical to before. - Optional page parameters (#1640, ako#1649) —
Params: ( $Sale: Shop.Sale optional )is Studio Pro's unticked Required (Forms$PageParameter.IsRequired = false, Mendix 11.5+): the page can be opened with no argument, from a menu item for example, where a required parameter is CE1571.create page,alter page … add parametersanddescribe pageall spell it.describe navigationno longer invents(Sale = $currentObject)for a menu item opening such a page, which made its output oneexecrefused (MDL-PAGEARG01) and left the navigation profile unchangeable from MDL. Below 11.5optionalis refused (MDL-VERSION01), and a snippet parameter cannot be optional (MDL087). mxcli git merge <ref>— model-aware merging from the command line (#1669) —git mergecannot merge Mendix models: the.mpris SQLite and every.mxunitis BSON, so two sides changing the model always meet a binaryCONFLICT (content)(measured in the git lab: both files, every time). Studio Pro resolves that with a model-aware merge, and the same engine ships as a command —mx merge BASE MINE THEIRSover the three revisions' project files, which runs on Linux devcontainers too.mxcli git merge <ref>runsgit mergefirst (a merge git resolves itself is annotated likemxcli git commit; a merge stopped on text conflicts resumes once those are resolved and staged), and on a model-file conflict checks the base, ours (a clean checkout of HEAD — never the working tree mid-merge, where the engine chokes) and theirs revisions out into temporary worktrees and hands the three projects'.mprfiles to the engine, mx resolved for the project's Mendix version (PATH, the mxcli cache, the Studio Pro installation): everything the engine auto-resolves is copied back from MINE, hash-checked, staged and committed as the normal two-parent merge commit with the mx_metadata note — and the engine owns the.mpr, so a unit one side added while the other changed existing ones is kept, not dropped, and a deletion it resolved removes the unit file with it; the engine's own-vconflict list (one JSON array per run,ManualResolutionRequiredvsAutoResolved) decides which units the review material and plan template cover, with git's conflicted-file list as the fallback; a conflict the engine reports asManualResolutionRequired(exit 2) is a review step, never a silent choice: the merge aborts leaving the working tree exactly as before, and both sides' changes are printed unit by unit at property level (frommx diff), with a plan template (<mpr>.merge-plan.json, never staged bymxcli git commit) beside the project — the human re-runs with--plan <file>to resolve per unit over the engine's own result (the engine runs again and the plan decides only the units it left open — everything it merged stays merged; each planned unit takesoursortheirswhole, its conflict marker is cleared and the.mprindex repaired from the files; a plan naming more or fewer units than the engine left open is refused; modified-on-both units only, added/deleted/moved goes to Studio Pro), or with--ours/--theirsto take one side of every conflicted unit — hash-gated: when the sides' document sets differ (one side added or deleted a unit) the commit is refused and the merge aborted, because a whole side would orphan the other side's documents — or resolves in Studio Pro (Version Control › Merge Changes Here), which remains the tool for mixed resolutions; any other engine exit (its 129, an mx that will not start) is reported as the engine failing and the merge is aborted cleanly, never steered into a decision. Non-model conflicts (text sources) are left in place like plain git: resolve them,git addthem, re-run the command. Refused while Studio Pro has the project open. Docs: "Working Outside Studio Pro" › Merging branches.- Lint rule CONV021 (CreateChangeThenCommit) — warns when a microflow's Create or Change object activity without commit is directly followed by a Commit of the same object, and suggests the one activity that does both: Commit set on the create/change (Yes, or Yes without events for a commit without events; Refresh in client kept). It stays quiet when merging could change behaviour: another flow into the commit, an error-handler flow between them, or custom error handling on either.
- Expression check E015: a String function's result compared with
empty—if trim($Value) != emptypassescheck,execandmx checkand is always true:trim,toLowerCase,toUpperCase,substring,replaceAllandtoStringreturn''for an empty input, neverempty(measured on the Mendix 10.18.5 runtime).checknow warns, and stays quiet on thetrim($v) != empty and trim($v) != ''guard idiom, where the''comparison decides.
Changed
- SEC011 names the widget whose data source fails on every page — it uses the catalog's new
WidgetName(ako#1879) instead of guessing; before, a page with two data widgets over the same entity was reported as "A data source on page …" with no way to tell which grid to fix. - Concord support is removed;
--mcp-checkand--mcp-runuse Studio Pro's own MCP server — the optional second MCP server (Concord) and the--mcp-concord,--mcp-concord-dialand--mcp-saveflags are gone. PED writes each change to disk as it lands, so there is nothing to save.--mcp-checkruns a project-wideped_check_errors(Studio Pro 11.15+), and--mcp-runstarts the app through themendix_apphandler and prints its URL (11.16+). - The first command on a project says it is building the unit index (ako#1806) — after an upgrade to the
unit-index-2.idxformat, or on a fresh clone, one command opens every unit file to build the index, which on Windows is a visible stall. It now prints one line on stderr (Building the unit index for this project's N units (once; …));-q/MXCLI_QUIETkeep it silent. git doctorjudges onlycore.autocrlfamong the local git config — themendix.commits-since-gcandmendix.lineEndingResetDonekeys are Studio Pro's own bookkeeping (measured on a lab repository), so a repository without them is no longer reported, and--fixno longer writes them.
Fixed
-
The catalog's XPath rows name their widget (ako#1879) —
xpath_expressions.ComponentIdfor a page or snippet data source was a Go byte dump ({0 [57 221 …]}) and nothing tied the row to its widget. It is now a GUID, and the newWidgetId/WidgetNamecolumns (widget_id/widget_nameinxpath_expressions()) name the widget by the idwidgetsgives it. The catalog schema moves to version 32, so a cached catalog is rebuilt once. -
create or replace pagecan clear a data grid column caption (ako#1877) —Caption: ''over a stored caption reported "Unchanged page" and kept it, because the translation carry added the stored text back to an empty header written with no translation. The header is now written as one empty translation, which the carry leaves alone; an empty translation where the stored text has none is dropped, so a Studio Pro empty caption still rewrites as unchanged. This also repairs pages that still carry a leaked column name (#535). -
A custom-content data grid column with an empty caption no longer shows its MDL name as the header (ako#535) — through the generic
pluggablewidget 'com.mendix.widget.web.datagrid.Datagrid'form,column colTriage (Caption: '') { … }(or no caption) wrotecolTriageas the header text, which end users saw; Mendix stores no name for a column. It now writes an empty header, as the keyworddatagridform already did; it builds clean (11.15.0) and Data grid 2 renders it blank. -
--mcp http://localhost:<port>/mcpreaches Studio Pro on a Windows machine with Docker Desktop (#1684, ako#1833) — without--mcp-dial, mxcli dialledhost.docker.internalwhenever that name resolved, taking it to mean "inside a container". Docker Desktop for Windows puts the name in the hosts file, pointing at the LAN address, and Studio Pro listens on loopback only, so the connection timed out (dial tcp <LAN IP>:7782: i/o timeout) on the very machine Studio Pro runs on. mxcli now dials the URL's own host first and trieshost.docker.internalonly when that fails, so the same command still works from a devcontainer.--mcp-dialis unchanged: when given, it is the only address tried. -
--mcp-traceand--mcp-verboselist the PED calls (#1684, ako#1833) — they printed each MDL statement but never a tool call, onexec,-c, the REPL andmxcli mcp capabilitiesalike. A run that sent every write to Studio Pro read as one that sent none, which is how a report concluded thatexec --mcpwrote the.mprinstead. The backend the CLI builds for--mcpnow gets the tracer too, so each statement is followed by its→ ped_…calls. -
A PED tool whose handler throws is reported as a failure — Studio Pro answers
Error executing tool handler: …withisError:false(measured on 11.16.0), which a caller trusting the flag read as success; the MCP client now classifies that text as an error. -
describeand the source catalog no longer decode every document of a type to find one (ako#1850) — describing a snippet decoded every snippet of the project, a nanoflow every microflow and nanoflow, a page every page, and so did resolving the snippets, pages, layouts and flows a description names. A source catalog build describes every document, so it paid that once per document: on FactoryManagement a firstrefresh catalog full sourcetook 33 s and 145 s of CPU, and now takes 19 s and 38 s, with identical text for all 3,718 documents.create nanoflowlikewise decodes only the nanoflows of its own name. -
Refreshing the catalog after a model edit no longer spends seconds on widget definitions (ako#1848) —
refresh catalogre-read every widget package on every run, and on FactoryManagement it took about 3 s of a 5 s refresh on Windows. The step is now skipped when no.mpkor.def.jsonfile has changed since the last refresh by the same mxcli build, and reading a widget's editor rules is no longer quadratic in the size of itseditorConfig.js(Maps' took 0.5 s and now takes 15 ms, with identical rules on 256 widgets). Two widgets that derive the same keyword, such asKeyboardShortcut.mpkanditvisors.KeyboardShortcut.mpk, no longer overwrite each other's definition: the pluggable one keepsKEYBOARDSHORTCUTand the other becomesCUSTOMKEYBOARDSHORTCUT. Measured on a Mac, a refresh after one edit went from 2.8–3.2 s to 1.0–1.4 s. -
MPR012 follows the app's Web UI client (ako#1287, #1226) — a Mendix 11 app can still run the classic client, where the legacy static and dynamic image widgets build clean, but MPR012 warned on each of them that "mxbuild reports CE0582". On the classic client or in migration mode it is now an info-level note that the widget needs converting before the app switches; under the React client it stays a warning.
-
Every
mxcli init --tool <x>installs the bundled lint rules, andlintsays when it runs without them (ako#1292, #1242) — onlyclaudeandopencodewrote.claude/lint-rules/, soinit --tool cursor(or aider, continue, copilot, vibe, windsurf) leftmxcli lintrunning the built-in Go rules alone, silently.lintandreportnow print a warning when no.claude/lint-rules/is found, naming how many rules run instead. -
show references toan entity, attribute or association lists the message definitions that expose it (#1781) — a message definition (the pre-11.15 collection,MESSAGE_DEFINITION_COLLECTION, and the 11.15+ document,MESSAGE_DEFINITION) was not walked for references, so impact analysis called an exposed entity or attribute unused. The entity edges aremapping, the attribute and association edgesmember, as for an import or export mapping. -
A pluggable widget's hidden expression property is stored as Studio Pro stores it (ako#548) — a property the widget's editorConfig hides is written at its declared default, and for an Expression-typed one that default went into
PrimitiveValueas well asExpression(every combo box'scustomEditabilityExpression,PrimitiveValue: "false"). Studio Pro leavesPrimitiveValueempty on all 939 Expression-typed values of ako/TestApp and PedApp; mx check accepted both, so the cost was a widget that differed from Studio Pro's. -
A source catalog build describes a project widget the same way every run (#1799) —
refresh catalog full sourcesometimes wrote a pluggable widget in the genericpluggablewidget '<id>'form instead of its own keyword (vegachart …), and since #1802 an incremental build kept that row until aforce. Each parallel describe loaded its own widget registry, and with one definition that never counts as current (hand-made, or its.mpkremoved), each load rewrote every.def.jsonwhile the other describes read them. The describes now share the build's registry, and a definition is replaced by rename, so no reader sees a half-written file. -
A rewrite gives a stored list the marker Studio Pro writes (ako#1640) — a list an older mxcli stored with the wrong typed-array marker kept it through every later rewrite, because a rewrite copies much of a stored document as bytes (an unchanged pluggable widget's Type and Object, an existing element's unchanged properties). A Data grid 2's
SortBar.SortItemsstayed 3 where Studio Pro stores 2, anddrop pageplus the same MDL was the only way out. Every write now applies the measured owner/key markers to the whole unit, and lists without a measured marker are left as they are. A populated nanoflow parameter list (on a call nanoflow action or a nanoflow data source) is now written with 2, as Studio Pro does. mx check never reported either; the effect was a document that differed from Studio Pro's at the value level. -
MPR v2: a unit written twice in one
modelsdkwrite transaction commits the second write — writing a unit A → B → A in oneWriteTransactioncommitted B. The second write was compared with the committed file rather than with the transaction's own first write, which sits in a temp file until commit, so it matched A and was skipped as unchanged. It is now compared with the pending write. No mxcli command ormodelsdkcall writes a unit twice in one transaction, so this was reachable only by usingWriteTransactiondirectly. -
Integer properties are stored as 64-bit integers, as Studio Pro stores them (ako#1264, #675) — every Integer property mxcli wrote, a page's canvas and popup sizes among them, was a 32-bit BSON integer where Studio Pro writes 64-bit, so an edit of a Studio Pro document changed the width of all its numbers.
mx checkaccepts both. A document an older mxcli wrote is not migrated, and reads accept either width. Reading a mapping's ancestor binding (LevelOfParent) from a Studio Pro document no longer gives level -1. -
checkcatches a template brace the build rejects (ako#1397, #1515) —'Item {number} not found'in a log message, orBody: template '{}'in a REST call, passedcheckand failed the build with CE0712. MDL-TEMPLATE02 now reports mxbuild's code for every template slot, and exec refuses the same texts. A workflow text or caption with an escaped{{1}}is no longer refused. -
checkwarns on areplaceAllpattern that ends in a lone backslash (ako#1397, #1515) — it builds, then fails on every call at runtime. MDL-REGEX01 coversreplaceAll,replaceFirstandisMatch. The replacement is literal text in Mendix, so a backslash there is not flagged;mxcli syntax microflow expressionsexplains both. -
A catalog refresh after a save rebuilds only what the save changed (ako#1804) — every refresh rebuilt every catalog table from every document, about 4.5 s of decoding and inserting on a 4,753-document project, and more on Windows, after any edit. The cache now records which document each row came from. The next refresh starts from the cached catalog and rebuilds only the changed documents and the documents that reference them, reading and decoding only those. A refresh after one edit on that project went from 5.6–8 s to 0.7 s end to end, with or without the source index. It falls back to a full build when a folder, module, settings, security or navigation document changes. Tests compare every table with a forced full build after each of 19 kinds of edit.
-
show referencesfinds flows that branch on an entity (ako#1804) — an inheritance split (split type $x when Module.Entity) names the entity only on its outgoing flows, and the catalog recorded no edge for it, soshow references toandimpacton the entity missed the flow. It is now asplitedge. Found by the new ref coverage trace, which checks that every document a catalog pass reads is reachable through refs. -
mxcli git commitandgit mergesay what they did — the staged-files summary lists what the commit holds, not the files just unstaged, and a merge git refuses to start (a dirty working tree, no identity) reports git's own reason instead ofgit merge side: Updating 5d6b73c..2c3fd47. -
The SessionStart bootstrap script installs mxcli under Git Bash on Windows — it built the asset name from
uname -s(MINGW64_NT-10.0-26200), requested a binary that does not exist, and the session started without mxcli.
Security
golang.org/x/netv0.60.0 -> v0.62.0 for CVE-2026-97045 (GO-2026-6866) and CVE-2026-78661 (GO-2026-6860) — both are ingolang.org/x/net/html, which mxcli does not link: at v0.29.0go list -deps ./cmd/mxclihas nox/net/html, and source-modegovulncheck ./...reports neither as called. Scanners still flag them because the stripped release binary only tells them the module version. The bump also raisesx/cryptov0.58.0,x/sysv0.49.0,x/textv0.43.0,x/syncv0.24.0 andx/termv0.47.0, which x/net v0.62.0 requires. After it,govulncheck ./...(Go 1.27.3) reports nothing called and one module-level entry, GO-2026-5932 (x/crypto/openpgp, unmaintained, no fix), whose package mxcli does not import.
Full Changelog: v0.29.0...v0.30.0