Skip to content

v0.29.0

Choose a tag to compare

@github-actions github-actions released this 30 Oct 17:09
· 883 commits to main since this release

[0.29.0] - 2026-10-30

Headline: Write what the project's own Mendix version stores, and commit it the way Studio Pro does. Every write is now checked against the element types and property shapes of the project's Mendix version, so a statement 9.x or 10.x cannot store is refused by check -p instead of leaving a project Studio Pro cannot open, and mxcli git commit / git doctor keep a plain-git history in the shape Studio Pro expects. Alongside it: workflows, pages and Java actions gain the settings a rewrite used to reset, alter microflow edits inside loop bodies, the debugger works end to end, and commands on large MPR v2 projects open only the files they need.

Added

  • mxcli git commit -m … -p app.mpr — commit the way Studio Pro does (#1669) — Studio Pro's Commit is more than git commit: it keeps the ContentsHash index in step, stages exactly the files a Mendix repository ships, and attaches the mx_metadata note. The new command is that sequence in one line: mismatched hashes are repaired from the files on disk first (MISSING and ORPHAN entries have no automatic repair and refuse the commit), everything is staged and the files Studio Pro never commits (project-settings.user.json, *.mpr.lock, *.mpr.bak, and the project's own .mendix-cache/, deployment/, theme-cache/, .mxcli/ beside the .mpr — a folder of the same name further down, such as resources/deployment/, is committed) are unstaged again, git commit runs with the caller's message and identity, and the mx_metadata note is computed from the new commit and attached, so Studio Pro can version-check the revision and show what changed. --push pushes the branch and the notes ref together (git push alone loses the note the next time Studio Pro fetches); --dry-run reports what it would commit without committing — and without repairing anything, hash drift included, because a dry run writes nothing: it stages into a copy of the index, so what the user had staged is left as it was. The commit is refused while Studio Pro has the project open (.mpr.lock) — honouring the same MXCLI_ALLOW_STUDIO_PRO_OPEN=1 override as every write — and during an unresolved merge, which git add -A would otherwise commit with its conflict markers. Docs: "Working Outside Studio Pro" › One commit, all the steps.
  • mxcli git doctor [-p app.mpr] [--fix] — audit a repository for everything a plain-git history misses, and repair it (#1669) — a repository committed only with git drifts from what Studio Pro expects in four ways, each invisible on its own: the local git config Studio Pro writes is absent (core.autocrlf=False keeps a checkout from rewriting line endings inside .mxunit files on Windows; mendix.commits-since-gc and mendix.lineEndingResetDone are its own bookkeeping), the remote is SSH which Studio Pro cannot use (HTTPS only), commits have no note under refs/notes/mx_metadata (history shows (unknown), the version check fails), and a malformed note Studio Pro cannot read at all. doctor reports all four in one read-only pass and exits 1 while an issue remains, so it can gate CI. --fix sets the config keys, rewrites SSH remote URLs (git@host:org/app.git, ssh://…) to HTTPS — judged as configured, so a url.<base>.insteadOf rule in the user's git config (https → git@ is a common one) is reported rather than "repaired" on every run — and backfills notes: computed from the model with -p (the same engine git note uses), or a template note with a --version ModelerVersion where the model at a commit cannot be read (MPR v1 history, a revision without the .mpr); with neither, the newest valid note in history supplies the version. Notes Studio Pro wrote are never touched, with or without --fix; --fix does not push. A clone whose notes ref lacks the remote's notes — git clone never fetches refs/notes/*, so every fresh clone and CI checkout — is reported as exactly that, with the fetch command, rather than as a history of unannotated commits; --fix fetches the notes first, as a fast-forward only. Docs: "Working Outside Studio Pro" › Auditing a repository.
  • Workflows say what Studio Pro lets them say — a user task takes a task name '…' clause (what the task inbox shows, until now always the caption), and the workflow's display and description and a task's name and description take template parameters, display 'Approval for {1}' with ({1} = $WorkflowContext/Title), in CREATE and ALTER. A timer boundary event takes caption '…', and a non-interrupting timer can recur: repeat every 1 day at most 3 times. alter workflow … set (Annotation: '…') sets the workflow's note, and … on <activity> an activity's. A describe → create or modify that would still reset a setting MDL cannot state is refused, naming the setting, instead of reporting "Modified workflow".
  • MDL-WF20: a user task whose page cannot complete one of its outcomes — Mendix adds no buttons to a task page, so a page that only shows the task passes mx check and the build, and every instance then waits at the task forever. check now warns per task, listing the outcomes no complete task button, or button whose microflow reaches set task outcome, can complete.
  • alter microflow edits inside a loop body, and alter … replace body of replaces just the body (ako#1693) — create or modify (under mdl 1) and alter microflow splice an activity inserted, dropped or moved inside a loop body, where they used to refuse and point at replace loop, which re-mints every element ID in the loop. replace body of <loop> is the explicit fallback. A fragment's @annotation can now attach a statement to a note already in the flow by its text (ako#1695), and the decision an alter … insert produced can be dropped or replaced again (ako#1694).
  • Return value mappings on a button's flow call (#977) — call microflow M.Get returns (Brand = $ActionReturnValue/Brand, $s = $ActionReturnValue) writes and describes Studio Pro's Return value mappings (Mendix 11.5), and a rewrite keeps stored ones.
  • alter app security (AdminPassword: '…') (ako#1259) — CE0129 "Administrator password has not been set" could only be resolved in Studio Pro.
  • create java action … extra code $$ … $$ (ako#1702) — the third part of a .java file Studio Pro keeps when it regenerates one. describe java action prints it, so a helper method travels with the action to another project. The written file now also has the class javadoc mxbuild writes (ako#1700), so a rewrite no longer leaves a pair of unrelated VCS diffs.
  • Page and widget settings that had no MDL spelling — a rewrite used to reset each of these to a default: a page's own DesignProperties: ( … ), typed against the native theme on a native page (the web one turned a native colour option into a custom colour); a dynamic text's NativeTextStyle: Heading3; a tab page's Badge: (ako#1653); a text area's NumberOfLines: and Autocomplete:; a responsive layout's scroll-container LayoutMode: Sidebar (ako#1703), which made a copied Atlas sidebar layout a Headline one; a show page action's Number of pages to close; and an import mapping call's input object and commit mode, import from mapping M.IMM($Json) (Parameter: $Obj, Commit: No) all;, without which a mapping with an input object could not be called at all.

Changed

  • docker check checks the project as stored by default, and says which CE0463 mxcli fix widgets clears (ako#568, #646) — this changes the default. Until now docker check ran mx update-widgets on its temporary copy before mx check, so it validated a widget-normalised model: a project whose stored widgets were stale (CE0463 "The definition of this widget has changed") got "0 errors" and exit 0, and then mxcli run --local and MxBuild, which build the project as stored, refused it. Now the verdict and the exit status are the stored project's. When the stored project has CE0463, the same copy is re-checked after mx update-widgets and each CE0463 is labelled stale widget definition — mxcli fix widgets fixes it or persists after update-widgets, with the mxcli fix widgets -p <app>.mpr command and the error count after the resync. A project without CE0463 is checked once, as before. --update-widgets restores the old behaviour (check only the normalised copy); --no-update-widgets is still accepted, and checks as stored without the re-check. Measured on Mendix 11.13.0 with a blank app whose Data grid 2 package gained a property: the old default reported 0 errors, the new one 11 errors, all labelled stale; after mxcli fix widgets it reports 0. The project is still never modified. docker build's pre-build check is unchanged and still runs on a normalised copy.

Fixed

  • playwright check no longer fails a page checked by the app's own host name behind a proxy (ako#1800) — with https_proxy set and an app checked as http://frontend.local:8180/ (an /etc/hosts name for 127.0.0.1), a page that rendered failed on two console errors, WebSocket connection to 'ws://frontend.local:8180/mxdevtools/' failed: … Unexpected response code: 502, while the same check on 127.0.0.1 passed. The runtime serves its dev channel on that name; the 502 came from the proxy, because Chromium sends ws:// to the HTTPS proxy and no_proxy listed only the loopback names. The browser that playwright check, run --page-check and run --screenshot start now reaches a host that resolves to this machine directly. No console error is ignored: the channel connects, and a genuine console error still fails the page.
  • Published OData entity sets, OData actions and older domain models have the property names Studio Pro writes (ako#1805) — on a Mendix 11.14.0 project mxcli fix property-sets reported seven MIXED names and could fill none of them. An entity set published without stating its modes was written without ReadMode, InsertMode, UpdateMode and DeleteMode, and an action whose microflow takes no parameters without Parameters; they are now written, as the values Mendix already read the missing ones as (read from the database, changes not supported, no parameters), so the service behaves as before and describe now prints the four modes. The domain-model names (LocalizeDate on a DateTime attribute, the four system-attribute flags on an entity without a generalization) are not left out by any current statement: they belong to entities an older mxcli wrote, which every later write of the domain model kept as they were. Both are now filled from values measured with mx convert on 10.24.24, 11.13.0 and 11.15.0, on the next write of the document or by mxcli fix property-sets. Run mxcli fix property-sets once to complete what an earlier mxcli wrote.
  • A retrieve over a self-reference is the one object Mendix makes it, and a list use of it is refused by check -p (ako#1793) — for a Reference from an entity to itself (M.Order_Parent), create microflow typed retrieve $P from $Order/M.Order_Parent as a list of M.Order unless $P was later used as an object, so $n = count $P; passed check -p and exec and failed mx check with CE0013 "Input variable 'P' must be of type 'List'.". Mendix follows a self-reference from its FROM end: a Reference gives the one object, a Reference set the list. The full build, alter's splice and check -p now take that one rule, from the entity and from a specialization of it alike, and check -p and exec's pre-flight report MDL-RETRIEVE01 for the one object a Reference gives from its FROM entity — a self-reference or not — taken by an aggregate (CE0013), a list operation other than range (CE0097), a loop (CE0100) or a list parameter (CE0117). set on a self-reference's object is refused like set on any object (CE7247); the Change list Replace it used to be written as passed mx check only because mx check does not type a Change list's variable. Measured on 11.13.0: before, count $P passed check -p and exec and was CE0013; now check -p refuses it, and the object uses and the Reference set's list uses build at 0 errors.
  • Pages, navigation, module settings, scheduled events, database connections, business events, REST clients and view entities are refused by check -p below what their write needs (ako#1811) — the same sweep found document shapes the writers produce in a type or property newer than the project, with no check-time gate or with one below it: snippet parameters (9.21), alter page … add parameters (9.4), page variables (10.20, their default value), action: complete task (9.19) and the other workflow button actions (9.0.2), a menu item Icon: (9.20), progressive web app settings (9.0.3), a native profile's home page (9.4), a not-found page (10.13), JAR dependencies (10.0) and their exclusions (10.12), create scheduled event (9.12), create database connection (10.0; with a query, 10.12), create business event service (9.24), and on a consumed REST service basic authentication (10.2), any operation (10.4: its method and response handling), query parameters (11.0) and an OpenAPI document (10.21). create view entity was gated at 10.18, where view entities arrived, but mxcli stores the query in a separate source document that is a 10.21 type: on 10.18 to 10.20 check -p passed it and the write refused it. Its floor is now 10.21. Each floor is pinned to the type and property data the write refuses on; the sdk/versions rows that said otherwise (database connector 10.6, business events 10.0) now hold it, and mx 9.24.30 loads a business event service mxcli writes.
  • A workflow parameter, a user task and the other workflow parts an older Mendix stores differently are refused by check -p, not part-way through exec (ako#1811) — past the workflow's own 9.0.2 (#1809), what a workflow states has later floors, which the write enforced and no gate named. Measured on 9.x projects whose recorded version was set below each floor: every workflow with a parameter is refused below 9.18 (Workflows$Parameter.Name) and every user task below 9.19 (Workflows$UserTaskOutcome.Value); a jump to needs 9.0.5, a call workflow activity 9.6 (with parameter mappings 9.18), an overview page 9.11, an annotation 9.15 and an event sub-process started by an interrupting notification 11.10. create workflow and alter workflow now report each with MDL-VERSION01. The user task row of sdk/versions said 9.0.0, so show features on 9.0 to 9.18 listed user tasks the write could not store.
  • Loops, show page with arguments, queued calls and execute database query are refused by check -p below their Mendix version (ako#1811) — the sweep behind the previous entry listed every $Type the converters construct against the Model SDK's type data and found more flow activities the write refused with no check-time gate: loop … in $List and while (9.0.4: before it, a loop stores its list on itself), show page … (Param = $Value) (9.7), call microflow/call java action … in queue (9.10), execute database query (9.24) and its connection ( … ) arguments (11.1). They are gated in create/alter microflow and nanoflow like the previous entry's. The sdk/versions rows for the database query said 10.6 and 11.0; they now hold the type floors, 9.24 and 11.1, and mx 9.24.30 loads the action, so show features on 9.24 lists it.
  • send rest request, range, call external action, transform … with and find/filter … where are refused by check -p below their Mendix version, not part-way through exec (ako#1811) — none of them had a check-time gate, so mxcli check -p passed them and the write refused them ("this Mendix 9.24.30 project cannot store Microflows$RestOperationCallAction (Mendix 10.1.0+)") after the statements before it, or, with --continue-on-error, ran on without that microflow. check -p and exec's pre-flight now report MDL-VERSION01 naming the statement and the version, in create microflow/nanoflow and in what alter microflow/nanoflow inserts, wherever it sits: find/filter … where (or a condition that is not Member = value) 9.0.3, call external action 9.19 (with arguments 10.2), range 9.24, send rest request 10.1 (with arguments 10.2, a result variable 10.4, body $Var 10.5) and transform … with 11.4. Each floor is the type's introduction in the Mendix Model SDK, pinned by a test to the data the write refuses on. send rest request is gated at 10.1 although mx 9.24.30 loads the action: the consumed REST service it calls is a 10.1 type too, so a 9.24 author cannot set one up. A find on a String the statement declares is a String find(), not a list operation, and is not gated.
  • create workflow is refused by check -p on Mendix 9.0.0 and 9.0.1, not part-way through exec (ako#1809) — the workflows basic floor was 9.0.0, while Workflows$Workflow arrived in 9.0.2 (the Mendix Model SDK's version data), and only alter workflow applied it. On 9.0.0 or 9.0.1, create workflow and create or modify workflow passed check -p and the write then refused them ("cannot store Workflows$Workflow (Mendix 9.0.2+)"). The floor is now 9.0.2, pinned by a test to the same data the write refuses on, and all three statements report MDL-VERSION01 in check -p and exec's pre-flight. alter workflow's hint said "upgrade your project to Mendix 9.12+", a version the gate never stood for; it names none now, and the message states the floor.
  • A catalog refresh on an MPR v2 project opens only the files that changed (ako#1804) — every refresh read every .mxunit of the project, 4,753 files on a large app, after every save; on Windows each open is scanned by the virus scanner. mxcli now keeps a compressed copy of the units in .mxcli/cache/unit-contents.db and reads a unit from its file only when the file's size, modification time or recorded hash has changed, so a refresh after a save opens one file or none. The store is git-ignored and safe to delete.
  • Changing one entity no longer re-describes its whole module in the source index (ako#1803) — an incremental source rebuild treated an entity as changed whenever anything in its module's domain model did, so one new attribute re-described every entity of the module and every document using any of them: 346 documents for an unreferenced entity in a 154-entity module of a 4,753-unit app. An entity's basis is now its own subtree, and the same edit re-describes 1. A change to an association or annotation still re-describes the module's entities.
  • Workflow actions an older Mendix does not have are refused by check -p, not part-way through exec (ako#1787) — notify workflow (10.0), get workflows and get workflow activity records (10.4), and the other workflow actions of a microflow (call workflow, set task outcome, open user task and open workflow 9.0.2, workflow operation abort and restart 9.2, pause and continue 9.3, get workflow data 9.10, retry and unpause 9.12) had no check-time gate: mxcli check -p passed the script, and the write then refused it ("this Mendix 9.24.30 project cannot store Microflows$NotifyWorkflowAction (Mendix 10.0.0+)") after the statements before it, or, with --continue-on-error, ran on without that microflow. check -p and exec's pre-flight now report MDL-VERSION01 naming the statement and the version, wherever the action sits: a branch, a loop, an error handler. Each floor is the type's introduction in the Mendix Model SDK, and a test pins the sdk/versions rows to the same data the write refuses on (canon.TypeIntroduced), so the two cannot disagree. Measured on 9.24.30: before, check -p passed and exec refused at write time; now check -p reports each, exec writes nothing, and App.mpr is byte-identical. On 10.24.24 the script executes as before, with 0 errors.
  • alter microflow and alter nanoflow are version-gated like create (ako#1787) — no version gate reached what an insert or replace splices in, so mxcli check -p passed an alter microflow … { insert after … begin $Wfs = get workflows for $Obj; end; } on 9.24.30, and the write refused it ("cannot store Microflows$GetWorkflowsAction (Mendix 10.4.0+)"). What an alter inserts is now gated as the same statements in a create microflow or create nanoflow are: the workflow actions, send email (11.13), a lock or unlock naming its workflow (10.0) and synchronize unsynchronized (9.4). check -p and exec's pre-flight report MDL-VERSION01, and exec without the pre-flight refuses the statement before reading the stored flow. A drop, or an insert of nothing gated, is not affected.
  • mxcli debug enable, status and break no longer say continuing and stepping arrive "in a later slice" (ako#1813) — they work since ako#1782; enable now names debug break, and status/break name debug continue. The debugger proposal's protocol table now records the measured shapes: add_breakpoint requires condition and continue takes {debug_id}.
  • mxcli debug break sets the breakpoint, and no mxcli debug command reports success when the runtime refuses it (ako#1782, #1676) — debug break without --if sent add_breakpoint with no condition, which Mendix 11.13 and 11.15 (both measured) refuse with {"message":"Missing property: condition","status":2} and register nothing; mxcli printed "Breakpoint set", debug breaks listed it, and the microflow ran to the end without pausing. The runtime answers a refused debugger request with HTTP 200, and mxcli read only the HTTP status. condition is now always sent (empty for an unconditional breakpoint), and every debugger command fails with the runtime's message when the reply's status is non-zero — a bad --if expression, an unknown activity or microflow, a variable that does not exist, or a session token the runtime no longer knows (with the mxcli debug enable remedy). Verified on a running 11.15 app: the breakpoint now pauses the flow, and paused, step and continue drive it to the end.
  • mxcli debug prints the runtime's reason for a refused request without the usage text after it (ako#1782) — once a debug subcommand's arguments are accepted, an error is the runtime refusing a request or no flow being paused, not a usage mistake, and thirty lines of flags buried the reason. A missing argument or unknown flag still prints the usage.
  • mxcli debug inspect prints a String or enumeration variable's value instead of {} (ako#1782, #1676) — inspect asks the runtime's get_object, which takes only an object variable and refuses a primitive ("The variable 'Greeting' is not a Mendix object."); the refusal's empty result was printed as the value. A primitive's type and value are in the paused flow's state already, and inspect now prints them from there; an object variable still shows its members.
  • mxcli debug continue resumes the paused flow instead of leaving it paused (ako#1782, #1676) — it sent continue with no debug_id, which the runtime refuses ("Missing property: debug_id", measured on Mendix 11.13 and 11.15), and printed "Continued." while the flow stayed paused until continue --all. It now resumes the paused flow by its debug_id: the only paused one, or the one named with the new --flow <debug_id>, as step and inspect already did.
  • Rebuilding a catalog that has a source index describes only what changed (ako#1790) — once a project had a source index (refresh catalog full source), every later rebuild described every document again. That includes the rebuilds search and show references start after each save. On a 4,753-unit app that was 38 s per rebuild, and much longer on Windows before #1789. A rebuild now re-describes the documents whose unit changed (an entity's unit is its module's domain model) and the documents whose references name one of them. The rest are carried over from the cache. After adding one attribute, the same app rebuilds in 11 s: 3,525 rows reused, 193 described, and the index is byte-identical to a forced full rebuild. A change outside the described documents (a folder, a module, the project settings, a Java action), a cache written by another mxcli build, and force still describe everything. The cache records each unit's content hash and container, so a document moved into another folder is described again too.
  • Commands on an MPR v2 project open only the files they need (ako#1791) — on a 4,753-unit app, show modules opened 4,511 unit files, describe microflow 2,996 and describe page 3,176, in every process. The commands read every folder to build module paths and every microflow to name or find one, and show modules decoded every page, snippet, flow, constant and Java action just to count them. Each open is cheap on macOS and is scanned by Defender on Windows, so this added time to every command an agent runs. The unit index in .mxcli/ now also holds each unit's name and Excluded flag, keyed by content hash like its type, so it never goes stale. Folders and microflows are named from the index, a microflow is found by name and read by ID, and show modules counts from unit types. The same commands now open 509, 93 and 274 files, with output identical across three projects and all 229 page describes. The index moves to unit-index-2.idx, so an older mxcli on the same project keeps its own unit-types.idx instead of the two rebuilding each other's.
  • refresh catalog full source reads each unit file once instead of millions of times (ako#1789) — the source index describes every document, and each microflow describe found its microflow, and every microflow it calls, by listing every microflow of the project from disk. On a 4,753-unit MPR v2 app one refresh opened 7,532,649 .mxunit files: 5.5 minutes on macOS, and 90 minutes or more on Windows, where Defender scans every file open. The catalog build now keeps the units it reads in memory, and a describe reads its microflow and the microflows it calls by ID. The same refresh opens 4,753 files and takes 31 s, and its source text is byte-identical. The rebuilds that show references and search run on a project with a source index are the same build, so they speed up too.
  • Moving a Java or JavaScript action to another module takes its source along — move java action A.X to B moved the document and left javasource/a/actions/X.java behind (likewise a JavaScript action's .js). The build still succeeded, but the app called a generated stub that throws "Java action was not implemented", and describe printed a placeholder body that re-executing would have stored. The source now moves to the target module's directory, with a Java source's package line rewritten exactly as mxbuild regenerates it; an occupied destination refuses the move before anything changes.
  • Writing an MPR v1 project through the Go library no longer hangs — on a project whose contents live in the .mpr (Mendix below 10.18), codec.Store.SaveUnit, FlushUnits and modelsdk.Model.Flush never returned: the write transaction read the stored unit through the reader's single pooled connection, which the transaction itself held. It now reads through the transaction. mxcli exec writes through a different path and was not affected.
  • Re-running an OData client script from a checkout at another path no longer rewrites the client (ako#1499) — a relative MetadataUrl such as ./contracts/f1-metadata.xml is stored as the absolute file:// URL of the checkout that ran it, and create or modify consumed odata service replaced that URL with its own on every run. The same unchanged script, run from another machine, a moved folder or a git worktree, rewrote every client: 5 of the 20 writes of a full re-exec in the reported project. The stored URL is now kept when it names the same project file, and the contract is read from this checkout's file. The stored form stays absolute: mx check accepts a relative value, but it also accepts a dead absolute path, so it shows nothing about what Studio Pro's metadata editor accepts. describe prints a stored URL inside the project directory as ./contracts/…, so a script generated from the model carries no machine path. Measured on 11.13.0: the same script run after moving the app, and from a copy, reported the client unchanged (before: "Modified OData client"), with mx check 0 errors.
  • On an MPR v1 project, a unit write updates Unit.ContentsHash with the contents (ako#1159) — below Mendix 10.18 the unit contents live in the .mpr, and every write replaced a unit's Contents but left its ContentsHash describing the old bytes; an inserted unit got an empty hash. mx check and MxBuild never read the column, so nothing failed, but Studio Pro reads it to detect what changed. Studio Pro fills it on every v1 unit with the same formula as v2, base64(SHA-256(Contents)): 308 of 308 units of a fresh 9.24.30 app. Update, insert and the transactional write now store it. mxcli fix hashes covers v1 too: it compares each hash with the contents in its row, and --repair rewrites the ones an earlier mxcli left stale. exec and docker check still verify v2 only, because on v1 that reads every unit. Measured on 9.24.30: create entity plus create microflow left 2 stale rows before the fix and none after, mx check 0 errors, and fix hashes --repair repaired the stale copy.
  • Workflow activities and events an older Mendix does not have are refused up front (ako#1774) — mxcli check -p and exec's pre-flight report MDL-VERSION01 before anything is written, naming the clause: wait for notification below 10.0, wait for timer below 10.6, a boundary event timer of no kind below 10.14, boundary event [non] interrupting timer below 10.20, and lock workflow or unlock workflow naming a workflow below 10.0. Each floor is the type's own introduction in the Mendix Model SDK, and the write refuses the type itself too. Before, a workflow with wait for notification was written to 9.24.30, and the project did not load ("TypeCacheUnknownTypeException … Workflows$WaitForNotificationActivity"). Measured on 9.24.30 (refused, and the project is byte-identical) and on 10.24.24 and 11.13.0 (written, 0 errors, as before).
  • A type newer than the project is refused instead of making the project unloadable (ako#1774) — the version data held property floors only, so no write compared an element's own $Type with the project's version. exec reported success, and then mx check and Studio Pro could not load the project: "TypeCacheUnknownTypeException: The type cache does not contain a type with qualified name …". This shipped four times (#1754, #1209, #1163, #1771), each fixed for its one type. Every type's introduction version is now taken from the Mendix Model SDK (modelsdk/canon/mendixmodelsdk_type_versions.json, extracted from mendixmodelsdk 4.117.0 by gen_type_versions.go). Every unit write refuses an element whose type the project does not have yet, wherever it sits in the unit, naming the type, its version and the project's: "this Mendix 9.24.30 project cannot store Microflows$GetWorkflowsAction (Mendix 10.4.0+)". The check runs after the older shapes are written, so the parallel split's end marker and user tasks are still lowered, not refused. Measured on 9.24.30: a microflow with get workflows made the project unloadable before. Now the write is refused, and the project is byte-identical afterwards, with 0 errors. 23 Studio Pro-authored projects from 9.1.1 to 11.16.0 hold no type the floor refuses. The SDK's deletion versions are not enforced, because a type it renames keeps its storage name.
  • Documents written to Mendix 10.24 below 10.24.24 have the property names Studio Pro writes (ako#1505, #1595) — on 10.24.18 a nanoflow re-created with drop and create lacked ExportLevel and ReturnVariableName, so mx diff and Studio Pro's change view threw "Objects with ID … do not have the same properties". A new microflow lacked the aggregate's four expression properties, a delete's error handling, a while loop's caption and a retrieve's XPath constraint, and a new entity its image properties. mxcli fills these from a table of values measured with mx convert, and the table had been measured from 10.24.24 up only; that version was then treated as the one that introduced each property. Measured again on 10.24.0 and 10.24.18, 94 of its rows now apply from 10.24.0, including open user task, REST operation call, timer boundary event and business event service properties. Run mxcli fix property-sets to complete the documents an earlier mxcli wrote.
  • mxcli fix property-sets no longer reports a project complete when an element lacks a property the others of its type carry (ako#1505) — on 10.24.18 it said "Every unit already has the property names Studio Pro writes" for a nanoflow without ExportLevel while mx diff threw on it, because it knew only the properties its measured table holds for the project's version. Studio Pro writes the same property names on every element of a type, so the command now also lists each name some elements of a type carry and others lack, as MIXED <type>.<key> with the units concerned, and exits 1. It does not fill those: no value measured on that Mendix version says what to write.
  • An alter microflow fragment can name a member of a list operation's, a Java action's or a self-reference retrieve's output (ako#1777) — alter microflow … { insert after … begin change $First (Status = 'Seen'); end; }, with $First = head $Lines in the stored flow, was refused with nothing written: "cannot qualify member "Status": the entity its variable holds is not known here". The same for a member of any stored filter, sort, tail, union, intersect, subtract, range, head or find output, of a Java action's result, or of a retrieve over a self-reference, though create microflow types them all. A fragment now starts from those types, taken from the same code the full build uses: a list operation's output has its input list's type (an element of it for a head or find), a Java action's result its declared return type (for a list of its type parameter, the list it is given), and an aggregate the primitive the full build declares. A retrieve over a self-reference holds the object the reference holds (the list, for a reference set): the stored retrieve records no direction, and Mendix follows it from the FROM end — measured on 11.13.0, a change on it builds and a count of it is CE0013. A name two origins give different types stays untyped, its member refused. Measured on 11.13.0: seven alters refused before; all written after, mx check 0 errors, describe → exec "Unchanged".
  • User-task features an older Mendix cannot store are refused up front (ako#1771) — mxcli check -p and exec report MDL-VERSION01 before anything is written, naming the clause. The floors are group targeting (targeting groups …) 11.2, a multi-user task 9.22, decide by majority or threshold 10.0, await all users 10.2, and decide by microflow 10.3. Before, group targeting was written on 10.24 and failed mx check with CE1859. The others were refused only at write time, after the statements before them were written. Measured on 9.24.30 and 10.24.24 (each refused, nothing written) and on 11.13.0 (all written, 0 errors).
  • alter workflow … set (Targeting: …) changes a user task's targeting on Mendix 9 to 11.1 (ako#1771) — it reported "Altered workflow" and changed nothing. Before 11.2 a user task stores its targeting under another key, and the edit only replaced a key that was already there. The edit now replaces the stored targeting, whichever key holds it, and the write stores it in the version's shape. Measured on 9.24.30 and 10.24.24: describe shows the new targeting, with 0 errors. 11.2 and later are unchanged.
  • A workflow with a user task no longer makes a Mendix 9 or 10.0–10.11 project unloadable (ako#1771) — exec reported success, then mx check and Studio Pro could not load the project at all: "TypeCacheUnknownTypeException: The type cache does not contain a type with qualified name Workflows$SingleUserTaskActivity". mxcli wrote a user task as a SingleUserTaskActivity or MultiUserTaskActivity, types introduced in 10.12. Before 10.12 both are a Workflows$UserTask, as Studio Pro 9.24 stores it. Its completion says which kind it is, and holds a multi-user task's rule, participants and, from 10.2, await all users. That is what is written now, for create, create or modify and a task alter workflow inserts. describe reads a multi-user task in that shape, and create or modify no longer resets its rule silently. What the older type cannot hold is refused, naming the task: a multi-user task below 9.22, decide by majority or threshold below 10.0, decide by microflow below 10.3, await all users below 10.2, and a boundary event on a user task below 10.14. Measured on 9.24.30 and 10.11.0: 0 errors, mx convert changes nothing, and describe → exec reports the workflow unchanged. Converted up to 10.24, the tasks are the ones mxcli writes there directly. 10.12 and later are unchanged.
  • A user task keeps its targeting on Mendix 10.12 to 11.1, including 10.24 (ako#1771) — every user task mxcli wrote there failed mx check with CE1859 "XPath constraint is required", including tasks targeted by a microflow and multi-user tasks. exec and mxcli check were clean. mxcli stored the targeting as UserTargeting, which Mendix introduced in 11.2. The older versions read the task as having no targeting at all, and mx convert replaced it with an empty XPath constraint. Before 11.2 the same fact is a UserSource holding the same value, measured by converting Studio Pro-authored 9.x workflows up to 10.24 and 11.13. That is what is written now, for users by XPath, users by a microflow, and no targeting. Group targeting is 11.2 and is refused below it, naming the task. Measured on 10.24.24: 0 errors, mx convert changes nothing, and describe → exec reports the workflow unchanged. 11.2 and later are unchanged.
  • A Timeline enumeration the widget hides is refused when set to a non-default value, instead of failing the build (ako#1762, #1668) — a hidden pluggable-widget property must hold its default, or mx check reports CE0463 "The definition of this widget has changed". mxcli check already refused this for the one Timeline hide rule it could read (groupEvents: false), but it read only 2 of the 6 (widget describe timeline: "2 of 6 editor hide-rules recognized"). So customVisualization: true, groupByDayOptions: 'dayMonth' and groupByKey: 'day', groupByMonthOptions: 'monthYear' passed check and exec and then failed mx check. The editorConfig's other rules sit in shapes the extractor did not read: a switch on groupByKey with case/default arms, and a ternary that terser folds into the switch(…) head, where the head's paren was taken for a conditional group. Both are read now, as are fall-through and grouped labels, and an if(…) head written the same way (HeatMap's playground). Timeline is 6 of 6 (3.1.1, 3.2.2 and 3.2.3), and each hidden enumeration gets the verdict groupEvents already had: an MDL-WIDGET10 error when set to anything but its default, which exec refuses before writing; a warning at its default; written at its default when left out. describe no longer prints a hidden property that holds its default, so its own output re-checks without warnings. MDL-WIDGET10 now says "is none of "day", "month"" for a default arm instead of "matches". The warning the reporter saw on hidden title/description/timeIndication came from a hand-written fallback table that listed only the text templates of that same hide. Measured on Mendix 11.13.0 with Timeline 3.2.2, one widget per variant: all six hidden variants were CE0463 before, all three controls 0 errors, and check now refuses exactly those six. The corrected pages build with 0 errors under --no-update-widgets, and describe → exec reports them unchanged. The File Uploader shape of ako#258 (a ternary-wrapped hidePropertiesIn gating createImageAction/createFileAction on uploadMode) was already read via the else-if chain support of #1583, and is now pinned by a test against File Uploader 2.5.0.
  • An activity edited by create or modify keeps its element IDs (ako#1761, #1667) — re-running create or modify microflow|nanoflow with only a filter's expression changed replaced the activity: the activity, its action and its operation all got new $IDs, while a decision whose condition changed the same way kept its own. check, mx check, diff and describe all looked fine, but Studio Pro compares and merges by $ID, so it showed the filter as one activity removed and another added, and a teammate's edit of the same activity on another branch became a conflict. An activity replaced by one of the same kind (the same action) is now edited in place: it keeps its $ID, its place in the flow and its size, and so do its action and every element under it that pairs up — a member change by its attribute or association, an argument by its parameter, a nested element by its key and type. A removed member's $ID goes onto no other member; a different operation (a find where a filter was) is new. This covers a filter or find expression, a retrieve's where, change and create member values, call arguments and any other same-kind edit, at top level, in a loop body and on an activity with an error handler, and alter … replace by one activity of the same kind does the same. Several adjacent activities each changed in their own place are each edited so; before, the run was replaced as a whole. exec now tells the two outcomes apart: spliced: 1 edited in place for a node that kept its $ID (an activity, a decision's condition, a return's value), replaced with new element IDs for one that did not.
  • The error handlers of two consecutive activities no longer share a row (ako#1768, #1672) — without @position a custom error handler is a row 130 px under its activity, running right, and the main line carries on one spacing after the activity however long that row is. When the next activity had a handler too, its row started at its own x, inside the first: two REST calls with three-element handlers stored the first handler's end event and the second handler's first activity both at 680;330. Neither check saw it — layout flows --dry-run re-derives with the same builder and called the flow already laid out, and MPR008 did not compare end events. A handler's row now occupies the lane under the main line the way a guard's branch already did, so whatever reaches down into that lane — the next activity with a handler, a decision, a loop — waits for it to clear, and a plain activity does not. The reported flow keeps $First, its row and $Body where they were and places $Second at 790;200, its row at 790..1110;330. The measurer counts the row too, so a loop or a branch holding handler rows is sized to them: a loop whose last handler row reached past its last activity was built 110 px wider than it was measured. Measured on 182 flows built from the 02* and 06 doctype examples: 2 move — each an IF that followed a handler row lying under its split — and none gains an overlap; mx check 0 errors on 11.13.0 before and after.
  • A @position on a later statement no longer moves the statements before it onto it (ako#1768, #1672) — declare $A String = $Url; @position(840, 200) declare $B String = $A; stored both declares at 840;200, and MPR008 reported the overlap. The start event is placed one spacing left of the first statement when that statement carries @position, but the builder took the first statement that had one, wherever it was, and the unplaced statements before it follow the start. Only the first statement moves the start now, as alter's patch and the syntax help already assumed: the flow above starts at 200;200 with $A at 360;200 and $B at 840;200. The syntax help and the language reference now say what happens to the statements before a placed one.
  • MPR008 reports an activity drawn on an event (ako#1768, #1672) — the overlap rule compared activities, loops, decisions and merges only, so an error handler's activity lying on another handler's end event at 680;330 passed, and with one more statement in that handler it reported one of the two overlaps, the one that happened to be activity on activity. Start, end, error, break and continue events and type splits are compared too, each with its stored size (an event that stores none is taken as Studio Pro's 20×20, not as an activity). On the 292 flows of the Studio Pro-authored PedApp and ako/TestApp fixtures, and on 182 flows built from the 02* and 06 doctype examples, the wider rule adds no finding.
  • A find in a loop an alter microflow writes is stored as a find by attribute, not as a find by an invalid expression (ako#1769, #1673) — alter microflow … { replace loop … with begin … end; } whose loop body held $Line = find $Lines by Code = $Lookup/Code;, with $Lines retrieved over an association before the loop, stored Microflows$FindByExpression with the bare text Code = $Lookup/Code: check --references and exec reported nothing, describe printed find $Lines where …, and mx check failed CE0117 "Error(s) in expression." create microflow stores the same find as Microflows$Find on MyModule.OrderLine.Code. Two causes. The guard that refuses a member a spliced fragment cannot qualify (ako#891) applied on the fragment's main line only, not in a loop, while loop or error handler inside it, and so did not cover replace body of loop either: there any find, filter, sort, aggregate or change member was written bare. And the fragment did not know what a stored retrieve over an association holds, nor a stored loop's iterator. Both are now typed as create microflow types them: from the association's TO end a retrieve yields the list of its FROM entity, from the FROM end the one TO object. So the reported alter writes Microflows$Find, and what still cannot be typed (a self-reference, a list operation's output) is refused with nothing written, wherever the statement sits. Measured on Mendix 11.13.0: CE0117 before; Microflows$Find on MyModule.OrderLine.Code, mx check 0 errors, and describe → exec "Unchanged" after.
  • A navigation profile's application title and icon can be set and are described (ako#1756, #1664) — the title (Studio Pro's Application title, Mendix in a new project) is what the runtime puts in every browser tab as Mendix - <page title>, and it had no MDL form: create or modify navigation Responsive app title 'EWO' … failed with "extraneous input 'app'", describe navigation printed neither it nor the Application icon, and create or modify translations … for en_US refuses the source language, so every generated app kept "Mendix" in the tab. create or modify navigation now takes app title '<text>' and app icon <Module.Collection.Image> (app icon none clears it), in any order with the other clauses; app and icon stay usable as names. The title is written as the project default language's item, the title's other languages kept; the icon is resolved like any image reference, by check --references and before exec writes; an omitted clause keeps the stored value; describe prints both. A native profile has neither, so both are refused there.
  • A test whose earlier @expect fails is no longer reported PASS on the after-startup runner (ako#1753) — every Docker run and --local --legacy-runner logged a verdict line per assertion, and the result was taken from the last one: a failing first assertion followed by a passing second reported PASS, with the failure message erased, and mxcli test exited 0. A failure now sticks, and the test reports the first one, plus how many more failed. The endpoint runner (--local, --attach, --watch) was not affected.
  • Two tests that assign the same variable with a lower-case call or create run on the after-startup runner (found with ako#1753) — that runner compiles every test into one microflow and renames each test's variables, but it recognised only an upper-case CALL or CREATE. $r = call microflow …, the spelling mdl 1 and fmt write, was left as it was, so two tests assigning $r declared it twice and injection failed with MDL063 (CE0111 from mxbuild). 8 of the 16 test files in mdl-examples/ hit this under Docker or --legacy-runner. A variable first assigned by retrieve, declare or a list operation was not renamed until ako#1775 (next entry).
  • Two tests that define the same variable with retrieve, declare, a list operation or any other activity run on the after-startup runner (ako#1775) — that runner joins every test into one microflow and renames each test's variables, but which ones it renamed came from a pattern that recognised only $x = call … and $x = create …, in any case. A variable defined by retrieve, declare, a list operation or aggregate, send rest request, execute database query or a loop iterator kept its name, so two tests defining it declared it twice and injection failed with MDL063 or MDL052 (CE0111 "Duplicate variable name" from mxbuild); setup-annotation.test.mdl and the #1103 example still did, under Docker or --legacy-runner. The names now come from the parsed test body through the definition MDL063 itself counts, including those nested in an if branch, a loop or an error handler. The endpoint runner (--local) gives each test its own microflow and was never affected.
  • exec names every file it writes beside the model (ako#1618, #1634) — create java action … as $$ … $$ stores the action in the model and writes its body to javasource/<module>/actions/<Name>.java (a JavaScript action's to javascriptsource/), but exec printed only "Created java action: …", so a workflow that commits the model (.mpr and mprcontents/) left the body behind and a clone got an action with no logic. Each file written, moved (rename java action) or removed (drop java action, drop javascript action, the source folders drop module deletes) is now named on its own line as a project-relative path, e.g. Wrote javasource/probe/actions/JA_Hello.java (Java action body; commit it with the model), and a run that changed any ends with Files changed outside the model: N (javasource/, …). Stage them with the .mpr. A rewrite that leaves a file's content as it was does not touch it and is not reported.
  • create published odata service on Mendix 9 and 10.0–10.3 is refused up front, naming the real reason (ako#1163) — it was refused at write time for "ODataPublish$PublishedODataService2.IncludeMetadataByDefault (Mendix 10.8.0+)". That property was not the problem: the whole document mxcli writes is a 10.4 type, and Mendix 9.24 cannot load a project that holds one. Before 10.4 a published OData service is a different document that mxcli does not write. check -p and exec now report MDL-VERSION01 before writing anything.
  • A page passing $currentObject to a microflow or nanoflow is written on Mendix 9 and on 10.0–10.9 (ako#1163) — call microflow M.F(P = $currentObject) on a button or a data grid column was refused with "cannot store Forms$MicroflowParameterMapping.Expression (Mendix 10.10.0+)". An argument held as an expression arrived in 10.10. Before it, Studio Pro stores no argument for the enclosing object and infers it. Without the refusal, 9.24 could not even load the page, because the mapping had no Variable. Below 10.10 that argument is now left out, and a page parameter argument is stored as before. Any other expression argument is refused, naming the parameter.
  • A constant override in a configuration is written on Mendix 9 and on 10.0–10.8 (ako#1163) — alter settings constant @M.C value '…' in configuration 'Default' was refused with "cannot store Settings$ConstantValue.SharedOrPrivateValue (Mendix 10.9.0+)". Before 10.9 an override's value is a flat Value string; Studio Pro 9.24 does not know the nested form and empties the override on load. A new override is now written flat there, and an existing one is updated in its own shape. A private value has no form before 10.9 and is refused, naming the constant.
  • A workflow written to Mendix 9.24 has Studio Pro's property set (ako#1163) — its two state-change event handlers (WorkflowOnStateChangeEvent, UsertaskOnStateChangeEvent, removed in 11.0) were completed only from 10.24, where they were measured. mx convert 9.24.30 adds both as well, and Mendix's merge engine refuses to compare documents whose property names differ (#1373).
  • Workflows are written on Mendix 9 and on 10.0–10.20 (ako#1163) — every workflow was refused with "cannot store Workflows$Workflow.PersistentId (Mendix 10.21.0+)". Workflows exist from 9.0, but the persistent ID on a workflow, its activities, outcomes and boundary events arrived in 10.21. The activities' and outcomes' IDs were never refused, and mx convert 9.24 stripped them. mxcli mints these IDs itself, so below 10.21 it no longer writes them, for create, create or modify and the activities alter workflow inserts.
  • A workflow with a parallel split no longer makes a Mendix 9 or 10.0–10.20 project unloadable (ako#1163) — exec reported success, then mx check and Studio Pro could not load the project at all: "TypeCacheUnknownTypeException: The type cache does not contain a type with qualified name Workflows$EndOfParallelSplitPathActivity". mxcli ends every path with that marker, because from 10.21 the workflow engine runs a path only up to it, but the type itself arrived in 10.21. Before 10.21 Studio Pro stores a path as its activities and nothing more (measured on a Studio Pro 9.4 app). Below 10.21 the marker is no longer written, for create, create or modify and a path alter workflow inserts. Measured on 9.24.30 and 10.12.19: 0 errors. On 9.24, describe → exec reports the workflow unchanged. 10.21 and later are unchanged.
  • Aggregates in microflows are written on Mendix 9 (ako#1163) — every count, sum, average, minimum and maximum on a 9.x project was refused with "cannot store Microflows$AggregateAction.AggregateFunction (Mendix 10.0.0+)". The version data had the aggregate's function introduced in 10.0, which belongs to the fold reduce added. Studio Pro 9.24 stores it on every aggregate. An aggregate over an expression, reduce, all and any are Mendix 10.0 and are still refused below it, now naming the fold.
  • Pluggable widgets keep the Type of their return types on Mendix 9.0–9.19 (ako#1163) — the version data mxcli vendors from the Model SDK had a return type's Type introduced in 9.20. That version belongs to AssignableTo, which the data left out. A widget schema written to a 9.0–9.19 project therefore lost the Type of every return type and kept an AssignableTo that version does not have. A return type that reached the property floor was refused with "cannot store CustomWidgets$WidgetReturnType.Type (Mendix 9.20.0+)". Studio Pro 9.1 and 9.4 store Type on every return type and no AssignableTo. The same mistake gave a group box's caption the 8.10 version of its header mode. That row changed nothing mxcli writes, because the caption is stored under another name and mxcli supports no version that old.
  • Pages on Mendix 9.24 and 10.x are read and written in the version's own shape (ako#1209) — several page properties are stored differently below a version: a design property's value before 10.2, a data view's page parameter before 10.0, its editability before 10.7, and a text box's placeholder before 10.11. describe read only the newer form, so on 9.24 a describe → exec round trip removed every design property and placeholder. Writing any page with a data view or a design property was refused, and alter styling on a stored design property reported success and changed nothing. Every write now converts to the project's form, and describe reads both. What the older form cannot hold is refused, naming the widget: a Spacing or colour design property, conditional editability, a placeholder with parameters.
  • A text box's KeyboardType survives describe → exec (ako#1209) — the keyboard a native app opens had no MDL spelling and was written as Default, so a rewrite reset it. textbox t (Attribute: Email, KeyboardType: EmailAddress) (Default, NumberPad, DecimalPad, EmailAddress, PhonePad, URL); an unknown value is MDL-WIDGET18.
  • A button's workflow action survives a rewrite (ako#832) — describe printed no action for Show user task page, Show workflow admin page or Call workflow, so re-executing a page turned them into no action (on ako/TestApp's TaskInbox and TaskDashboard, 2 of 6 user-task buttons each). They are now open user task [with (AssignOnOpen, OpenWhenAssigned)], open workflow with (DefaultPage: M.Page) and call workflow M.WF [with (…)]. complete task takes with (ClosePage, Commit) instead of always writing both true. open workflow without a page is CE1834 and is refused.
  • Workflows written to Mendix below 11.3 load and pass mx check (ako#1754) — every workflow had a stored start and end activity. On 10.24 and 11.0 the project no longer loaded (TypeCacheUnknownTypeException … StartWorkflowActivity). On 11.1 and 11.2, mx check reported CE6671 and CE6689. Below 11.1, activities also carried layout keys those versions do not declare. The main flow's start and end are now implicit below 11.3, and the header annotation is the workflow's own note.
  • A pluggable widget's enumeration setting is written as the key the widget declares (ako#854) — gallery g (…, onClickTrigger: Double) stored Double, and the Gallery compares its trigger with single and double exactly, so the row action never ran on a click while mx check reported 0 errors; a data grid's pagination: 'Buttons' was the same. A key in another case is now stored as the key, under both language versions, by create page and alter page … set alike. A value the widget declares no key for (onClickTrigger: triple) is refused under mdl 1; as MDL-WIDGET08, naming the keys, and check -p reports it. Migrating a script: without the header it is still written as given, and warns MDL-V1-WIDGETENUM.
  • alter page … set (Action: …) on <gallery> writes the row click, and an ALTER that leaves a gallery's row click ambiguous is MDL-WIDGET36 (ako#854) — Action: reached the setter for a built-in widget's click property, which a pluggable widget does not have, so it was refused ("has no on-click action") while set ('onClick': …) wrote the same slot. Action: and onClick: now reach a pluggable widget's click slot (a gallery's or data grid's onClick). And no ALTER spelling applied MDL-WIDGET36, so a row click on a single-click selection was written and mx check failed the page ("The item click action is ambiguous"); the gallery is now judged as the SET leaves it — refused under mdl 1;, written with the MDL-V1-GALLERYCLICK warning under mdl 0, as create page does.
  • A data grid filter's OnChange:, a barcode scanner's onDetect: and a combo box's onEnterEvent: / onLeaveEvent: / onChangeFilterInputEvent: are written and described (ako#854) — these widgets' built-in definitions had no mapping for those action slots, so the action was refused under mdl 1; (MDL-WIDGET37 / MDL-WIDGET06) and dropped with a warning under mdl 0, and describe printed no filter action — a Studio Pro-authored one survived a rewrite (the per-property patch of ako#832 kept it) but could not be restated, retargeted or removed. A filter's action is OnChange: on all four filters; the other slots are named by their own key, in every combo box mode.
  • Describe → exec keeps an input widget's "On enter" and "On leave" actions, and MDL can write them as OnEnter: / OnLeave: (ako#1743) — the focus-in and focus-out actions every input stores (OnEnterAction / OnLeaveAction on a text box, text area, date picker, check box, radio buttons and drop-down) had no MDL property, describe page never read them, and the writer hard-coded an empty action into both. Re-executing the description of a Studio Pro page deleted every one: on 11.13.0 a page with seven of them came back from describe with none, exec reported "Replaced page", and mx check still reported 0 errors. OnEnter: and OnLeave: take the action forms OnChange: takes (the stored names are accepted as the same properties), describe page prints them, and describe → exec now reports "Unchanged page". "On enter" is focus entering the widget; the Enter key is a text box's OnEnterKeyPress:.
  • A text box's "On enter key press" action has an MDL property, OnEnterKeyPress:, and survives describe → exec (ako#1743, #1662) — every spelling of it (OnEnterKeyPress:, OnEnterKeyPressAction:, OnEnterKey:) was reported as a property mxcli does not write on a textbox and dropped, describe printed nothing and mx check passed, so a search box that searches on Enter could not be generated; and the writer hard-coded an empty action into Forms$TextBox.OnEnterKeyPressAction, so re-executing the description of a Studio Pro text box deleted its enter-key action. OnEnterKeyPress: now takes the action forms OnChange: takes, the stored name is accepted as the same property, and describe page prints it back. A text area has no such slot in Mendix's metamodel (only Forms$TextBox does), so there the key is refused with that reason instead of being written nowhere.
  • check refuses a combo box's custom-content type without the content (ako#1744, #1663) — optionsSourceAssociationCustomContentType: 'yes' (or 'listItem', or the database mode's optionsSourceDatabaseCustomContentType) written with no content block passed check and exec, and mx check reported CE0642 "Property 'Custom content' is required." at Widgets container 'Custom content' of combo box. It is now MDL-WIDGET53, and exec refuses it too, with no project needed. A content block under the type 'no' is refused the same way — the editor hides the content then, and mx check reported CE0463 — and a block with the type left out writes 'yes'. describe page names content stored under 'no' in its NOT SHOWN note rather than printing a block that would re-execute as shown. Measured on Mendix 11.13.0 with Combo box 2.5.0: the type alone, CE0642 before and refused now; a block with the type left at 'no' CE0463, with the type left out (written 'yes') 0 errors, mx update-widgets changing nothing.
  • A combo box's custom content can be written, and describe page prints it back (ako#1744, #1663) — custom content is how a combo box shows more than one attribute per option, and mxcli widget describe combobox listed it as a widgets property, but combobox cbPerson (…) { optionssourceassociationcustomcontent { dynamictext … } } was refused by check -p with MDL-WIDGET26 "optionssourceassociationcustomcontent is not a container of combobox, which declares no containers", and exec failed on it. Each option-source mode now has its slot: optionssourceassociationcustomcontent in association mode, optionssourcedatabasecustomcontent in database mode, its widgets bound to the option entity. The other mode's block, or any block on an enumeration combo box, is refused (MDL-WIDGET30) naming the slot the mode does have, and the slot's key written as a value is MDL-WIDGET27. describe page prints the mode's content as that block instead of a NOT SHOWN note, so describe → exec reports "Unchanged page", and a create or modify that changes the combo box restates the content instead of being refused (ako#1648 still refuses for content outside the described mode). widget describe marks the two slots "association mode only" / "database mode only". Measured on Mendix 11.13.0 with Combo box 2.5.0: the reported page and a database-mode combo box with content, mxcli docker check --no-update-widgets 0 errors; mx update-widgets changes nothing in the written page; describe → exec "Unchanged page"; the combo box's label changed in the description → "Replaced page", content kept, 0 errors. A static-mode combo box's per-value content (staticDataSourceCustomContent) is not covered: the static values themselves have no MDL spelling yet.
  • check -p --references types the result of call microflow, so a Hidden attribute read on it is reported (CE1406) (ako#1701, #1654) — $Request = call microflow AddOn.Request_GetFirst(); declare $Secret String = $Request/Secret; in an app module passed check and exec while Secret was a Hidden attribute of a Protected (add-on) module, and mxbuild refused it: [CE1406] "Create variable activity 'Create String variable' is not allowed to use attribute 'AddOn.Request.Secret' because app modules cannot reference hidden elements in add-on modules." The same read through a parameter was reported. The variable-typing walk the --references member checks share typed parameters, create, retrieve and loop iterators but not a call's output; it now takes the callee's object or list return type — the script's own definition of the callee first (a create or modify changes what exec writes), else the stored microflow. The member-existence check gains the same reach: a CHANGE on a call result naming a member the returned entity does not have now reports CE1613 instead of passing. Measured on Mendix 11.13.0: a declare, an end event and a change on the result, and a change on a loop over a returned list, each refused on the Hidden attribute; the Usable one beside it is 0 errors.
  • check -p --references reports CE1406 for a retrieve, create or create list of a Hidden entity of a Protected module (ako#1699, #1652) — a flow in an app module that ran retrieve $l from AddOn.Internal, $o = create AddOn.Internal or $l = create list of AddOn.Internal on a Hidden entity of a Protected (add-on) module passed check and exec, and mxbuild refused it: [CE1406] "Retrieve object(s) activity 'Retrieve list of Internal from database' is not allowed to use entity 'AddOn.Internal' because app modules cannot reference hidden elements in add-on modules." (likewise "Create object activity", "Create list activity"). The check judged an entity only as a parameter or return type; it now judges the entity these three activities name, in microflows and nanoflows, and exec refuses the script before writing it. Measured on Mendix 11.13.0: each shape refused against a Hidden entity, 0 errors against a Usable one and inside the add-on; an association retrieve that reaches a Hidden entity over a Usable association names only the association and is not refused.
  • A pluggable widget's text-template parameter across an association builds, and describes so it re-executes (#1494) — fieldset fs (legend: '{1}', legendParams: ({1} = M.Parent_Child/Label)) passed check and exec and failed the build with [CE1365] "Attribute 'M.Child.Label' is not an attribute of entity 'M.Parent'.": the parameter was written with its attribute but without the association hop, so mxbuild read the attribute against the widget's own entity. The same writer serves an object-list item's template (an Accordion group's headerText) and a Data grid 2 column's dynamic text, and all three failed; on Mendix 11.12.4 they now build with 0 errors. describe page printed such a parameter as the bare attribute ({1} = Label), which re-executed into CE1613 "The selected attribute 'M.Parent.Label' no longer exists"; it now prints M.Parent_Child/Label. The companion report, an expression parameter ({1} = if 1 = 1 then 'A' else 'B') stored as an attribute name (#1495), was fixed in v0.25.0 (#823) and is now pinned by a test.
  • alter entity … add event handler adds a second handler for the same event (#1540) — add event handler on before commit call M.Second($currentObject) failed with "event handler already exists for Before Commit" when the entity already had one, though Studio Pro allows several handlers for one event and create or modify entity with two on before commit clauses stores both (measured on 11.12.4: two Before Commit handlers are 0 errors, a void-returning one is CE7247, so the check does look at them). A handler is now identified by its moment, event and microflow: another microflow adds a second handler, the same microflow again is still a duplicate. The same key had made add event handler if not exists … call M.New skip — reporting "already exists" — whenever any handler for the event existed, and drop event handler on before commit remove whichever handler was stored first. drop event handler now takes call M.Mf to say which one; without it, a drop that matches several handlers is refused and names them.
  • A bare create or modify module role M.R; keeps the role's description (ako#1218) — the "make sure this role exists" statement reported Modified module role: M.R and erased the stored description: a statement without description was written as an empty one. An omitted description now keeps what is stored, as an omitted user role property already did, and the run reports Unchanged; description '' clears it. Grants were never affected, and describe → exec was not, since describe prints the description. Measured on Mendix 11.13.0: mx check is 0 errors with and without the fault, so only the stored description showed it.
  • check refuses what mx check would, before anything is written — each of these passed check -p and exec and failed afterwards:
    • a widget property the widget does not take, such as Caption: on a text box (written with no label) or Visible: on a snippet call (ako#1698); MDL-WIDGET07 judged every widget against the union of all widgets' properties;
    • a member named like one on its generalization chain, CE0069 (ako#1645); a find, filter, sort or aggregate by a member the entity lacks, and an aggregate by an inherited attribute, CE1613 (ako#1646); $Var/member for a member the entity lacks, id included, CE0117 (ako#1697);
    • a page parameter named Context, CE7247 (ako#1651); an XPath [%UserRole_X%] naming no user role, a CE1613 that also hides every CE0161 elsewhere; a SET that leaves a date picker Custom with no pattern, CE0493 (#977);
    • a Java action parameter or return type it cannot store: a StringTemplate parameter written as a String, an unreturnable type written as Boolean (MDL-JA03), a StringTemplate on a JavaScript action (MDL-JSA01), a bare enumeration stored as an entity (ako#935), and a text-template or document argument to call java action bound as text, CE0115 (ako#1451).
  • Statements write what they say —
    • retrieve $C from $O/Ord_Cust with the association's short name is stored with its module, not as .Ord_Cust (ako#1690); where Total > @Shop.C_Limit parses, and a constant in an XPath constraint is no longer dropped on write or describe (ako#1685);
    • log … with ({1} = …) and send email over a variable send its value, not the text $Msg, and show message and validation feedback no longer shift the stated parameters by one (ako#1683);
    • an if without else inside an error handler keeps its false case, CE0079 (ako#1455); a change next to a find … where with a bare member no longer reads the find as changed too (ako#856);
    • a view entity accepts STRING_AGG beside GROUP BY (ako#1650), and types a CASE column from its first branch, so the declaration MDL031 asked for no longer fails CE6770;
    • a routine header without parentheses is one error at the mistake, not three after it.
  • Renames, moves and drops update what refers to them — rename constant and a cross-module move constant rewrite @Module.Const in expressions, and rename enumeration a value inside a longer expression (ako#1671); move enumeration rewrites parameters, variables and return types, not only attributes (ako#1674); rename attribute no longer rewrites every unrelated XPath constraint with a line break in it, 147 units in 21 modules on one project (ako#1506); drop entity removes the association's member access from the entity at the other end (ako#1449); and moving an OWNER Both association's TO end reconciles the entity access of both modules, CE0066 (ako#819).
  • More pages survive describe → exec unchanged — a read-only data view stays read-only, and alter page … set (Editable: …) accepts a 10.7+ data view; text box, text area, date picker and radio buttons keep ReadOnlyStyle (#977); a microflow data source keeps its FormValidations (#1729); a stored DisabledDuringExecution false is kept on pluggable widgets and layouts (#549); texts keep their stored language order, and a multi-placeholder page its placeholder order (#1704); a dynamic text's customDateFormat with a quoted literal is escaped both ways (#977); a button passing a grid's or combo box's selection keeps that argument, where it gave CE1571; a pluggable widget's text-template parameter across an association describes as the path; a data grid filter's Placeholder and Adjustable are written by create page (ako#1652); and an action with no target yet is described as a note instead of output that does not parse (#1728). describe fragment prints flow arguments and whole actions.
  • diff-local shows every change (ako#1512) — a settings change, a grant on an entity or domain model, and any unit without an MDL renderer now print a hunk or the changed fields instead of "(no changes)"; --ref a...b compares against the merge base instead of reading every unit at .b; a moved .mxunit is one unit deleted and one added; and it runs from outside the project's git repository.
  • Lint and refs — a published OData service's read and authentication microflows are references, so refs, impact and QUAL004 no longer call them dead (ako#1639); neither is a microflow started only from the navigation (ako#1484); and a finding's location no longer repeats the module (at M.M.NAV_Home).
  • The local dev loop —
    • run --local no longer hangs for minutes, silently, behind an idle Gradle daemon from an earlier run (ako#1340); a session-start hook no longer deletes a live run's run-local.json, and run status gives the stop reason, the trial licence's run-time limit included (ako#1542); --ensure-db no longer provisions an empty cluster on the port while the system cluster is down, which booted the app healthy against no data (ako#866);
    • mxcli test --local builds when the app's after-startup microflow applies entity access, CE0114 (ako#1654); import … link links, where it reported success and linked nothing on every Mendix version (ako#1454);
    • playwright check waits for a data source to render before its verdict (ako#845), fails an unknown /p/ URL instead of passing the home page (ako#998), signs in on each host it checks (ako#1216), reports a page the role may not open as access denied instead of signing in again, and no longer waits out --wait on the sign-in form.
  • Smaller fixes — show version reports MPR v1 for an MPR v1 project (ako#1553); a catalog query no longer strips Catalog. inside a string literal, so a module named Catalog can be filtered on (ako#1450); a user task's group XPath targeting is read back instead of dropped, and its targeting XPath is checked against the user entity; an interrupting boundary path ends with an End, as Studio Pro writes it.

Full Changelog: v0.28.0...v0.29.0