Skip to content

v0.31.0

Latest

Choose a tag to compare

@github-actions github-actions released this 09 Nov 21:59
· 381 commits to main since this release

[0.31.0] - 2026-11-09

Headline: Publish your app's microflows as an MCP server, and keep secrets off the command line. create published mcp service writes the MCP Server document Agent Editor starts at runtime, and agents gain tool approval, display text and streaming. The secret run flags and --constant take @env:VAR and @file:path, and the brain can be started before the project and edited in place. Alongside them come a long run of fixes that make check --references catch more of what the build would refuse, and mxcli test --local runs that start clean every time.

Added

  • create published mcp service publishes the app's microflows as an MCP server (ako#2020) — the model configuration of MCP Server 5.x: an Agent Editor 2.6 publishedMCPService document that AgentEditorCommons starts at startup through MCPServer.CreateMCPServer and AddTool. create [or modify], alter … set / add tool / drop tool, describe, list published mcp services and drop, with tool name ( Microflow: …, Description: …, Title: …, Schema: … ) blocks and the OAuth settings (AuthenticationEnabled, AuthenticationMicroflow, AuthorizationServerUrl, Scopes). The server is served at <app URL>/<EndpointPath>/mcp; ServerName, which Studio Pro stores but its editor does not show, defaults to the document name and is kept when omitted on create or modify. A rewrite keeps the stored layout, each tool's uuid and every key mxcli does not model; describe → exec of the Studio Pro 11.16 sample writes nothing. check reports what CreateMCPServer would refuse at startup (MDL-MCPSERVER01): a reserved or sub-path EndpointPath, Scopes without an authorization server, an authorization server without an authentication microflow, a tool without a microflow.

  • Tool approval, display text and streaming on an agent (ako#2021, ako#2022) — Agent Editor's human-in-the-loop and streaming settings now have MDL. A tool or mcp service block takes UserAccessApproval: HiddenForUser | VisibleForUser | UserConfirmationRequired, DisplayTitle and DisplayDescription. A knowledge base block takes MinSimilarity and IsVisible. The agent takes EnableBackendStreaming and EnableUIStreaming; streaming is stored on the agent, not the model. describe agent prints them and alter agent … set sets the streaming switches. On create or modify, a setting the statement leaves out keeps its stored value. describe now prints an MCP tool as an mcp service block, where a "mcp"/"MCP" mismatch printed a generic tool. Measured on Studio Pro 11.12–11.16 documents.

  • mcp-server skill: publish an app as an MCP server with runtime configuration (part of ako#2020) — the MCP Server module (3.x–4.x) publishes through a startup microflow calling MCPServer.CreateMCPServer, AddTool and AddPrompt, which MDL already writes as call java action. The skill gives the four microflows (authentication, tool, prompt, initialize) with their measured parameters and return types, and the per-version ProtocolVersion values; it was written into copies with MCP Server 4.0.0 and 5.4.0 (Mendix 11.16), where mx check reported nothing new. It also gives the 5.x OAuth parameters (AuthorizationServerUrl, Scopes, tool Title) and the 1:1 mapping onto Agent Editor's Published MCP service document, which AgentEditorCommons turns into the same Java action calls at startup.

  • check warns on an AI model or knowledge base Provider Agent Editor does not import (ako#2023, MDL-AGENT01) — Provider: TotallyMadeUp wrote, built and round-tripped, because mxbuild does not read the value; ASU_AgentEditor then failed the import at startup. check now warns, with or without a project, when a model's Provider is not MxCloudGenAI, Azure or Fuse (the values ImportDeployedModel switches on) or is in the wrong case, when a knowledge base uses anything but MxCloudGenAI, and on alter … set Provider too. It warns rather than refuses, so a provider a newer Agent Editor adds stays writable.

  • The catalog records a page's and a layout's own class and style, and the widgets of every layout (ako#1302, #1311) — a Starlark rule "no inline styles" passed a page with Style: 'color: red;' and a styled container in a layout, the two places whose styling reaches every page. pages() and layouts() gain class_name, style and dynamic_classes, and widgets() returns a layout's widgets with container_type "LAYOUT"; a microflow that only a layout's button calls now has that layout as its caller in refs. Built-in rules that read widgets report a finding in a layout against the layout, and the React-client rule skips native layouts as it skips native pages. update widgets still changes page and snippet widgets only. Full catalog; the schema moves to version 34, so a cached catalog is rebuilt once.

  • drop X boundary event N drops one boundary event of several (ako#808) — numbered as describe workflow lists them. Without a number the op named no event, so under mdl 1 an activity with several could not lose any of them; a number past the last is refused with nothing written.

  • Associations to include on a call external action argument (ako#1914, #1721) — command = $Cmd include associations (Odata.Quantity (Odata.Unit)) is Studio Pro's Associations to include: the associations whose objects are sent in the OData request with that argument, as a tree. Without them a sub-object linked to the argument was silently missing at runtime. Which end each association is on is worked out from the domain model; a self-association, or one that does not touch the entity on its side, is refused by check -p and exec. describe prints the clause. include associations () removes them all. Left out on create or modify, the stored tree is kept, and check -p and exec warn (MDL-ODATA10). That now also holds when the rewrite is spliced in place, which dropped the tree. Mendix 10.11+ for a non-empty clause. See mxcli syntax odata.consume.

  • send external object and delete external object (ako#1939, #1735) — Studio Pro's Send external object and Delete external object activities, which save and remove an external (OData) object, now have MDL: send external object $Obj [refresh] [on error …]; and delete external object $Obj [refresh] [on error …];. describe prints them, so a flow that holds one survives a drop + create from its description, which used to delete it silently. check refuses on error continue on either (CE6035, MDL076). See mxcli syntax odata.write.

  • A module's version and module type from MDL (ako#1921) — the General tab of Studio Pro's Module settings dialog: create or modify module Inventory (Version: '2.3.1', ModuleType: addon) and alter module Inventory set (Version: '2.4.0'). describe module prints the settings that differ from a new module's (App, 1.0.0), and the location as a comment when it is Marketplace modules; list modules gains Version and Type columns. The version must be SemVer, the type app or addon (Mendix 9.12+), and a statement that restates the stored settings writes nothing. Measured on 11.16: the type is the settings' ExportLevel (Source / Protected), not ProtectedModuleType, which is AddOn on every module.

  • --constant Module.Name=@env:VAR and =@file:path keep a secret out of ps and shell history (#1765) — mxcli run and mxcli test --local read the value from an environment variable or from a file, so a token no longer has to be typed into the command line, which shows it in ps for as long as the app runs. An unset or empty variable and an empty file are errors, not an empty value; the owner-only check on a file is made on Unix and not on Windows; a relative @file: path is stored as an absolute one, so run restart works from another directory; a file readable by its group or by others is refused (chmod 600); one trailing newline is dropped. A value that really starts with @ is written @@…. The run state keeps the @env:/@file: form, never the value.

  • The secret run flags take @env:VAR and @file:path, so a password stays off the command line and run restart still works (supersedes #1761) — --db-password, --db-admin-password, --screenshot-password, --hub-secret and --debug-pass resolve a reference the way --constant does: an unset or empty variable, an empty file and a file its group or others can read are refused, naming the flag and the source, never the value. The reference, not the secret, is what ps shows and what .mxcli/run-state.json stores, so a run started with one can be --detached and run restarted; each start resolves it again in its own environment, and a relative @file: path is stored as an absolute one. #1761 proposed reading the password from stdin, which cannot be read again and so ruled out --detach and run restart; its fix for a flag value that looks like -p being dropped from the detached command is included.

  • brain can start before the project, and entries can be edited, moved, sourced and listed (ako#1949, ako#1950, ako#1951, ako#1952) — the brain's store now lives as long as the project does, from before it exists, and changes in place:

    • -C/--dir <dir> names the brain's directory on every brain command, so you can fill a brain before its .mpr exists, and mxcli new adopts a target directory that holds only a brain. brain init no longer takes an argument it used to ignore.
    • brain edit <id> [<text>] [-a @Anchor]… rewrites a staged or committed entry in place and keeps its id; brain move <id> --slice <s> moves a requirement to another slice.
    • capture --source <doc>[#<locator>] and edit --source record which document an entry came from, and where in it. The path is stored relative to the project, so it works on other machines.
    • brain list [--slice] [--kind] [--open] [--json] returns the committed store as data instead of rendered markdown, and reports malformed blocks rather than dropping them.
  • alter page set reaches a Data grid 2 column's attribute and a text template's parameters (ako#1644, ako#1966) — set (Attribute: …) on <grid> column(…) binds the column as create does (bare name, Module.Entity.Attr or an Assoc/…/Attr path). set (CaptionParams: …) on a button and set (ContentParams: …) on a dynamic text replace the template's parameters. Before this, you had to replace the whole widget.

  • A queued call's retry policy in MDL (ako#1931) — in queue … retry fixed|exponential (…) writes Queues$QueueFixedRetry / QueueExponentialRetry; describe prints a stored retry so it round-trips, and check refuses Retries: 0 (CE7044). A flow with a queued retry could not be rewritten before; a rewrite that would drop the stored retry is now refused.

Changed

  • A secret run flag value starting with @ is refused unless it is @env:, @file: or @@ — --db-password, --db-admin-password, --screenshot-password, --hub-secret and --debug-pass follow the --constant rule; write @@… for a password that really starts with @.
  • A --constant value starting with @ is refused unless it is @env:, @file: or @@ (#1765) — it used to be applied literally; now a mistyped @evn:TOKEN is not quietly used as the constant's value. Write @@… for a value that really starts with @.

Fixed

  • Building a flow no longer reads every microflow of the project once per called flow (ako#2043) — resolving a call microflow target looked its name up by reading and decoding every unit of the type, so one large flow in a large project took minutes, and outlasted the per-statement limit. A lookup now records where each name is and later reads that one unit, re-checking its name, type and module so a unit renamed or moved outside mxcli is not returned; a miss scans as before. On TestApp a flow calling 44 microflows builds in 0.29 s instead of 0.6 s; the saving grows with the number of flows in the project.
  • A statement that times out or panics no longer crashes exec past its rollback (ako#2043, #1780) — a statement that outlasted the per-statement limit kept running after Execute returned the timeout; exec then closed the project to roll back, the statement read through the closed reader, and the nil-pointer panic in its goroutine ended the process before the rollback and before the timeout was printed. The drop and every earlier statement stayed written. Close now waits (up to 2 minutes) for such a statement to stop, and a panic in any statement is that statement's error, so the script is rolled back as for any other failure.
  • References to entities named as a type or reached through an association (ako#1829) — a flow whose action result, created list or Java/JavaScript entity-type argument names an entity, a snippet's parameters, and the entity an association path arrives at made no refs row, so show references, impact and the unused-entity check missed them (five entities in use on one project read as unreferenced), and an incremental build left a flow stale when an enumeration value it named was added. They are now type, parameter, member and xpath edges to the entity, and value to the enumeration when the value does not exist. Annotation and text prose and module-role names no longer produce member edges. Full catalog; the schema moves to version 37, so a cached catalog is rebuilt once.
  • The test endpoint's generated Java source is git-ignored (ako#2033) — while run --test-endpoint hosts the endpoint, javasource/mxtest/actions/RegisterTestEndpoint.java is in the working tree, and a git add -A in that window committed it. run --test-endpoint now ignores /javasource/mxtest/ (and .mxcli/, which holds the endpoint's token) before installing it, creating the project's .gitignore when there is none; the .gitignore mxcli init writes, or adds to, carries the entry too.
  • run --local --watch no longer rebuilds after the boot because the build wrote a new module's theme scaffold (ako#2033) — with --test-endpoint the boot build creates themesource/mxtest/, whose files are watched theme source newer than the boot's baseline, so build #2 started with nothing edited: ~30 s of 404s on dist/pages and a forced client reload, right as run --detach returned. A themesource/ folder a build creates is now its own output up to the build's end; edits to existing theme files, and later edits to the new folder, rebuild as before. Measured on a blank 11.12.6 app: build #2 every boot before, build #1 only after.
  • marketplace install and update download the module's managed Java dependencies (ako#2028) — a module such as MCP Client 4.4.0 declares Maven dependencies that Studio Pro downloads into vendorlib/ on import. A headless install copied only the declarations, so mx check passed and run --local / test --local failed with ~180 package … does not exist errors. The install now runs the sync, and prints mxcli sync-java-deps -p … when it cannot. The sync retries once when mx cannot reach the Maven repository ("Failed to find some hosts"), and a version range such as [2.21.2,3) counts as vendored when a version inside it is — it used to read as missing after every successful sync, so run --local re-synced on every boot.
  • Lint SEC013 warns when the security level ignores the project's access rules (ako#2027) — a blank app ships at security level Off, where the runtime applies no entity access, page or microflow grant and creates no demo user; at Prototype it applies no entity access, so XPath constraints have no effect. check, mx check, test --local and lint all passed while a customer read every row. SEC013 names what the level ignores and suggests Production; the manage-security skill says up front that a blank app ships Off.
  • Lint ARCH001 no longer flags the System workflow entities a task page must use (ako#2034) — the System.WorkflowUserTask data view of every task page and a task inbox over it were reported as cross-module data access, though Mendix requires them (CE7410/CE7412, CE1843) and offers no view entity or microflow instead. System.Workflow* is exempt; other cross-module references are reported as before.
  • References to import and export mappings and to JSON structures (ako#1861, #1703) — a flow that applies a mapping, through an import or export action or a REST or web service call, and a mapping built on a JSON structure made no refs row, so refs, show callers and orphan checks reported mappings and structures in use as unreferenced. They are now mapping_call edges (counted by show callers) and structure edges, and an import or export activity's ActionRef names its mapping. Full catalog; the schema moves to version 36, so a cached catalog is rebuilt once.
  • Rewriting an agent, model or knowledge base kept nothing mxcli does not model — alter agent, alter knowledge base and create or modify rebuilt the document's JSON from mxcli's own fields. What Agent Editor stored beyond those was lost without a warning: each tool's user approval and display title and description, the agent's streaming switches and selected model, and a knowledge base tool's minimum similarity. The knowledge base's Portal name was written empty, and on create or modify so was the model the Model document had picked. create or modify agent also ignored a tool's Document:, which left the tool running no microflow. It gave every tool a new, empty id and blanked the entity reference, and its MCP service tools lost their service. A rewrite now writes onto the stored JSON in Studio Pro's layout and key order, and restated tools keep their stored ids. Measured on Studio Pro 11.16 projects: describe → exec of all 33 agent documents of one app now writes nothing.
  • The catalog's source text is what describe prints (ako#2036) — CATALOG.SOURCE kept every stored layout annotation for speed, so a script generated from it stated layout describe leaves out (376 of one project's 858 microflows). It now stores the canonical description; a cold source build pays for telling derived layout from authored, and the incremental source build (#1790) carries unchanged rows after it.
  • The agents skill showed calls to the agent Java actions that do not exist (ako#2024) — it said call agent was missing and gave AgentCommons examples with parameters the actions do not have (UserMessage, ChatContext). Studio Pro's Call Agent toolbox actions are exposed Java actions that call java action already writes. The skill now leads with the AgentEditorCommons actions, which take an agent document by name (Agent = 'Module.MyAgent'). It lists each action's real parameters, measured on 11.16 projects. The AgentCommons actions are kept for agents built at runtime.
  • brain capture and brain promote honour --json (ako#1953) — they printed prose, so a caller scraped the new id out of a sentence. capture --json returns the id, kind, slice, shard and whether it was added (status: queued, already_queued, already_committed); promote --json the id, kind, shard and the file written. A duplicate capture also named a freshly minted id no entry had ("Already queued as …"); it now names the queued twin's.
  • drop user role refuses a role a demo user still holds (#1775 follow-up) — it passed check and exec and left the demo user naming a role that no longer exists, which mx check reports as CE1613. check --references and exec now refuse it and name the demo users to drop first; dropping them earlier in the same script is followed.
  • drop user role refuses the administrator's user role instead of leaving a project every build crashes on (ako#2015, #1775) — dropping the role Security$ProjectSecurity.AdminUserRole names (the template Administrator of a new app) passed check, exec and mx check, and every build then failed with a NullReferenceException in mxbuild. check --references and exec now refuse it. The administrator's role can be moved first with alter app security ( AdminUserRole: <UserRole> ), which must name an existing role, and describe app security / show project security print it as Admin User Role.
  • check --references still reports the flow changes exec would refuse when it finds a reference error (ako#2014, #1774) — it stopped at the reference errors, so a create or modify exec refuses (MDL-V1-REBUILD) went unreported until the reference error was fixed and the script checked again. The flow verdicts now run either way and the run reports both. The other project checks stay skipped after a reference error; the summary already counted reference errors (ako#1928).
  • A snippet call is recorded as a reference to the snippet (ako#2017) — the catalog stored a snippet call's snippet as the page a widget opens, so refs held a show_page edge to a PAGE for every snippet call and none had TargetType SNIPPET; a query for what uses a snippet, from a layout or anywhere else, found nothing. Snippet calls are now snippet_call edges to a SNIPPET, widgets gains SnippetRef (snippet_ref in Starlark), and show callers counts them. Full catalog; the schema moves to version 35, so a cached catalog is rebuilt once.
  • search finds usages in flow, page and expression bodies on a project that has never built the source index (ako#2004, #1771) — it searched only string literals and documentation, answered "No matches found." for a name used only in a microflow body, and exited 0 with just a stderr warning. The first search now builds the source index, saying so; later searches keep it current incrementally. search --no-build never builds it, and exits 3 when it is missing, so an incomplete answer is never read as "nothing matched".
  • create persistent entity … extends a non-persistent entity is refused instead of silently created non-persistent (ako#2005, #1772) — a specialization has no persistability of its own: Mendix stores it only on the root of the generalization chain, so the stated keyword was dropped and exec said "Created entity". A persistent or non-persistent that contradicts the root is now an error from check --references and exec, naming the root; one that matches it, or none, is accepted as before, so describe output still re-runs.
  • The catalog indexes widgets inside tables, scroll containers, headers and split panes (ako#1895) — the widget walk followed only the container keys it knew, so every widget in a legacy table cell, a scroll container region, a header side or a split pane was missing from widgets while xpath_expressions named it. On FactoryManagement 213 widgets were absent, among them the MxModelReflection snippets' list view and combo boxes, and lint rules that read widgets did not see them. The catalog schema moves to version 33, so a cached catalog is rebuilt once.
  • check --references reports a snippet call into an add-on module (CE1406) (ako#1998 follow-up) — a page or snippet in an app module that called a snippet of a protected (add-on) module passed check, and mxbuild refused the build: "Snippet call 'sc1' is not allowed to use snippet 'Lib.Sn' because app modules cannot reference hidden elements in add-on modules". Like a page, such a snippet is refused whatever export level it stores (measured on 11.12.6, with Hidden and with API), so the call is reported whenever the snippet's module is an add-on; a call from inside the add-on, or into an app module, is not.
  • Packaged and sealed add-on modules (modules/*.mxmodule) are read like any other module (ako#1780, ako#1998, #1762) — a module the project holds as a package file has no unit in the .mpr, so mxcli did not see it: show modules left it out, describe said "not found", and check --references refused valid calls into its public nanoflows. Its documents are now read from the package's own project.mpr (an ordinary v1 .mpr, or the module tree a protected add-on ships), in v1 and v2 projects alike; show modules and describe module mark it as packaged. It is read-only: a write into it is refused by check --references and by exec, and nothing is written.
  • An alter microflow fragment can name members of a created list, a cast, a nanoflow call's result and a mapped import or REST result (ako#1796) — those stored outputs were untyped for the fragment, so a member of one was refused with "cannot qualify member" although create microflow types them. A name that two stored origins type differently now stays untyped whatever kind the origins are; before, of two direct origins (a retrieve, a create, a call) the last one won.
  • run restart no longer suggests PGPASSWORD for a run it cannot restart — refusing a run whose literal secret was not stored, it said to set the database password in PGPASSWORD. The runtime's database password never reads that variable (only --ensure-db's psql does), so a run started that way connected with the default mendix. It now says to start the run with --db-password @env:VAR or @file:path, which restart resolves again.
  • widget sync --page takes the qualified name it documents (ako#1997) — --page Module.Page matched nothing and reported no drift; only the bare name worked, and it matched a page of that name in every module. A bare name is still accepted when it is unambiguous and refused when it is not, the plan names pages by their qualified name, and an applying run no longer prints "Read-only: nothing was written."
  • git merge keeps your text resolutions when it stops for a model decision (ako#1982, #1751) — after you resolved and staged a non-model conflict and re-ran, a model conflict that needs a decision aborted the merge and threw the resolution away; --plan then wrote the model and failed at the commit on "unmerged files", leaving the merge half done. The abort now records your resolutions in the plan template and --plan restores them, and --plan stops before it touches the model while a text conflict is still open.
  • create constant writes ExportLevel: Hidden, as Studio Pro does (ako#1980, #1749) — it wrote "", which mx check accepts but mx diff crashes on against a Hidden copy (exit 129), taking git merge's report with it. create or modify constant now keeps a stored level instead of clearing it, and repairs a stored "".
  • git merge reports the side whose mx diff failed instead of dropping the report (ako#1981, #1750) — when one side's diff failed, no property-level report was printed at all. The side that worked is shown, and the failing side reads (diff unavailable: <why>).
  • widget sync keeps the integers it does not change as integers (ako#1976, #1748) — syncing a widget wrote every int32 and int64 in it back as a double, a text box in the widget's slot included, whose MaxLengthCode -1.0 Mendix reads as unlimited (CE6510). A unit already synced by an earlier version keeps its doubles; re-create the page to repair it.
  • A Marketplace Calendar written by mxcli passes a plain mx check (#1632) — every Calendar failed with CE0463 "The definition of this widget has changed" until mxcli fix widgets or Studio Pro's "Update all widgets"; mxcli docker check hid it, since it runs that update on a temporary copy first. The Calendar's toolbarItems object list declares its sub-properties directly under <properties>, not inside a <propertyGroup>, and the .mpk parser behind generated widget templates read only the grouped shape, so the list was stored with no definition of its items. Both shapes are now read, a direct sub-property takes its category from its own <category> element, and the stored Calendar equals what mx update-widgets writes. The other object-list widgets in doctype-tests/32-pluggable-widget-object-lists-v010.mdl (Accordion, PopupMenu, Maps, area and line chart, heat map) are stored unchanged.
  • alter workflow no longer guesses which outcome to drop (ako#808) — drop X outcome 'V' with two outcomes of value V dropped the first; it is refused as ambiguous now. check --references also resolves an activity reference by name before caption, as exec does, so its guards no longer go silent on a target captioned like another activity's name.
  • describe → exec keeps a decision whose true branch is empty (ako#1963, #1742) — describe microflow folded such a decision into if not(X) then <false branch> end if;, and executing that stored not(X) with the true and false outcomes swapped: the same behaviour, a different model in Studio Pro. It is now printed as stored, if X then else <false branch> end if;, which builds Studio Pro's shape. A script written from the old output still means the same thing; an alter microflow target written in the folded if not(X) then form no longer resolves, and is refused rather than applied.
  • marketplace install installs protected (add-on) modules shipped as .mxmodule — such a package carries its model not as an SQLite .mpr but as one BSON document holding the module as a tree, so install failed with file is not a database, and mx module-import refuses protected modules outright ("Importing protected module is not supported", exit 111). The reference project is now built by splitting that tree into units and writing them with the same raw-unit writer the transplant uses; protection settings travel unchanged. Measured on Menditect's MtaPluginModule 11.5.5 (Marketplace 214717) into an 11.15 MPR v2 app: 32 units, mx check 0 errors, module listed as Protected AddOn, and the plugin starts and serves its endpoint. The package's declared files (javasource/, javascriptsource/, …) are written beside the reference project as mx module-import would, so marketplace diff and update do not report every Java or JavaScript action as changed; the units are not converted to the project's Mendix version, so a package saved with a newer Mendix than the project is refused.
  • test --attach on Mendix 11.14.0 says why it is refused instead of blaming an entity — that version's mxbuild --serve fails every build after its first, so since ako#1917 the attach builds on a fresh serve process, whose first build asks for a runtime restart whatever changed. The refusal claimed "an entity or association changed" and suggested restarting the dev loop, which does not help; it now says the build could not be done incrementally and points to --local. The hosting app's pages survive either way. 11.15.0 builds incrementally and attaches as before.
  • describe microflow no longer leaves out a note linked to the start event or to another note (ako#1933, #1731) — such a note counted as linked, so it was not printed as a free note, and the element it is linked to prints no @annotation, so it was not printed at all; re-executing the description deleted it. It is now described as a free note, after a -- WARNING line saying the link itself has no MDL form and a rewrite drops it.
  • Rebuilding a described decision no longer adds a merge (ako#1929, #1727) — a case, split type or if … elsif one of whose branches returns is described with its own merge as a label (end case; join shared1; merge shared1;). Executing that as a fresh create built the decision's merge and a second one for the label, wired merge to merge, and every further describe → drop + create added another (join junctionN; merge junctionN;). The label's merge is now the decision's merge when nothing else reaches it, so the round trip keeps one merge.
  • describe microflow prints a blank call external action argument as = nothing (ako#1915) — Studio Pro 11.16 stores an optional OData action parameter left blank as an empty argument, and describe printed it as additional = ), which does not parse, so every mdl 1; create or modify of such a microflow was refused (MDL-V1-REBUILD). call external action now takes Param = nothing, the blank-argument spelling call microflow and call nanoflow already use; Param = empty (what 11.14 stored) stays a different value, and each round-trips as itself.
  • The description of an AccessibilityHelper re-executes (ako#904) — an attr (attribute: 'aria-label', …) item was read by check --references, which exec runs first, as an attribute binding with no data container, so describe → exec refused every helper that sets an HTML attribute. An object-list item's properties are now the item's own.
  • A Delete button in a data grid's control bar deletes the grid's selection (ako#1932, #1730) — action: delete $grid1 stores the selection as Studio Pro does (SourceVariable = a page variable naming the grid), and describe prints a stored one back that way; it printed plain delete, so a describe → exec round trip dropped it. A bare delete with no object to delete — in a control bar, or outside every data widget — wrote a button mx check reports as CE7005; check and exec now refuse it (MDL-PAGEDEL01). delete $Param deletes a page or snippet parameter.
  • A script that would rebuild a flow holding unsupported activities is refused before it runs — under mdl 0, a create or modify the splice cannot make is rebuilt as a whole, and a flow holding an activity MDL cannot express (-- Unsupported action: …) refuses that rebuild when exec reaches it, after the statements before it were written. check -p and exec's preflight now refuse the script up front (MDL-FLOW-UNSUPPORTED), as they already do for a drop + create of such a flow.
  • retrieve … offset n without a limit keeps its offset (ako#1941, #1737) — an offset with no limit was written as the "all" range, so the retrieve returned the first objects instead of skipping n, and describe printed no offset. It is now written as a Custom range with that offset and no limit, which mx check accepts.
  • marketplace update no longer removes the module and then fails (ako#1934, #1732) — every update failed with open package target.mpk: no such file or directory after it had already dropped the installed module, leaving the project half-updated: it looked for the downloaded package under a different name than it was saved under. It now uses the downloaded package, and checks the package and the new version's module before it changes anything, so an update that cannot complete stops with the project untouched and says so.
  • create or modify microflow no longer inverts a decision whose condition changes (ako#1930, #1728) — describe prints a decision whose true path goes straight on as if not(X) then … with its paths swapped. Fed back with only the condition changed, the in-place edit stored the printed not(Y) as the decision's expression while its paths stayed, so the decision did the opposite of what the script said, with check, exec and diff all clean. The edit now stores the condition the decision means.
  • test --attach no longer breaks the hosting app's pages (ako#1917, #1723) — after a green mxcli test --attach against run --local --test-endpoint, the app answered 404 on its page bundles and every page showed the generic error dialog until it was restarted. The attach's builds went to the app's own mxbuild --serve, where on Mendix 11.12.5 and later any build after the first leaves the client bundle without its pages. The attach now finishes such a build on a mxbuild --serve of its own, which it stops afterwards, and leaves the app's untouched; this costs a cold build each time it happens.
  • run stop removes --test-endpoint from the project on Windows (ako#1917, #1723) — a detached run --local --test-endpoint stopped with run stop left the MxTest module, its after-startup microflow, themesource/mxtest/ and javasource/mxtest/ in the project, because on Windows the stop ends the run without its teardown; the next build then failed with CE1613 once the module was dropped. The run now records how to remove the endpoint when it installs it, and run stop (or the next --test-endpoint run) removes it when the run could not, on any platform, leaving the project file byte-identical.
  • A jar dependency change keeps the module's Module ID and Marketplace checksum (ako#1920) — alter module … add | set | drop jar dependency rebuilt the module's settings from the keys mxcli models, so the rest came back as defaults: the Module ID (PackageId) and a Marketplace module's Checksum were wiped, and detailed troubleshooting was switched on. The change is now written onto the stored settings, and only the jar dependencies change.
  • create or modify microflow keeps a call external action's "Associations to include" (ako#1914, #1721) — MDL has no syntax for them, so a rewrite that restated the call wrote it with none, and the OData request then silently left out the associated objects; check, lint and the build all passed. The associations Studio Pro stored on each parameter — a tree, not a flag: an association ticked under another is nested in it — are now carried onto the rewritten call, matched by action and parameter name, and only while the parameter still binds the same entity. When a microflow calls the same action twice with different associations, the rewrite is refused rather than guessing which call has which. A drop followed by a create still starts without them.
  • validation feedback on an attribute named Error parses (ako#1913, #1720) — validation feedback $E/Error message 'x', which is what describe writes for such an attribute, failed with "missing MESSAGE" because Error message was read as the error message clause keyword. After a / or . the word is now always a member name, in any case and anywhere an attribute path appears; the error message clause itself is unchanged.
  • dateFormat: custom on a dynamic-text parameter is stored as Custom (ako#1864, #1706) — check accepts a parameter's dateFormat and enumFormat in any case, but the writer stored the value as written, and "custom" is not a value Mendix knows, so the runtime showed the default format. The values are now stored in Mendix's spelling (Custom, DateTime, Image) on dynamic text and on Data grid 2 column templates.
  • A doc comment above alter entity … add attribute is stored on the attribute (ako#1863, #1705) — /** Why */ written above the statement was read by nothing: exec reported "Added attribute" and the attribute had no documentation, with only a later check warning MDL089. It now documents the attribute the statement adds, as it does above a create entity attribute, for alter entities too. When the statement adds two attributes, or the attribute has its own comment, the comment is still not stored and check still warns.
  • mxcli test --watch no longer shows a green run for a rebuild that failed — when the rebuild after an edit failed (or the changed tests could not be parsed or injected), the loop printed the error and then ran the suite again at once, against the model from before the edit, so a broken microflow could pass. The round is now reported as an error row naming the step and the reason, in the terminal and in the JUnit file, the suite does not run, and the loop waits for the next change. The change baseline is taken when the change is detected, so a save made while the rebuild is failing starts a new round, and the "run #N" counter only advances for a round that ran. Ctrl-C after a failed change ends the watch with the red result, so the command exits non-zero. (#1766)
  • A copied app folder no longer inherits the original's run state, and run state no longer stores secrets (#1764) — .mxcli/run-state.json, run-local.json and test-endpoint.json record the project they were written for, but nothing compared it, so in a copy of a running app mxcli run refused to start and mxcli run stop signalled the original's process, and mxcli test --attach talked to the original's app. All three are now ignored when they name another project (symlinks resolved, case-folded on macOS and Windows); run status and run stop say that they ignored one. --constant values, the password flags and the DatabasePassword and MicroflowConstants runtime settings are no longer stored in run-state.json, and the dev-loop handshake no longer holds the resolved constants. The handshake file run-local.json still holds the database password (re-applying constants needs it); it is written owner-only (0600). Behaviour change: a run started with --db-password (or another inline secret) cannot be run restarted; run restart says so and names the alternatives (the PGPASSWORD environment variable, or re-running mxcli run with the password) instead of starting the app with a placeholder. A run recorded from a devcontainer or WSL path is treated as another folder by the host side, so its state is ignored there.
  • mxcli test failure line shows the actual value of an enumeration compare (#1763) — @expect $Paid/Status = Procurement.InvoiceStatus.PAID failed with only expected …, because the actual value was reported only when the operand type was known and an attribute path next to an enumeration value never was. An enumeration value on one side now pins the other, so the line reads expected $Paid/Status = Procurement.InvoiceStatus.PAID, actual: APPROVED (the enumeration key, via toString()).
  • mxcli test --local no longer carries state or noise between runs — a run of the suite now reports only what the suite did:
    • Each run starts on an empty test database, on hsqldb and on PostgreSQL (where only the scratch <app>_test database is cleared, never the dev loop's) (ako#1853).
    • When mxbuild --serve stops answering, the boot restarts it and builds once more instead of failing with context deadline exceeded; a test that does not build has its errors repeated above the summary rather than lost among the "not run" rows (ako#1834).
    • No more Module 'MxTest' already exists on a clean project (ako#1856), and no MDL-V1-SLASH / MDL-DEPR001 per test on a headerless suite (ako#1855).
  • check --references catches more of what exec or the build would refuse:
    • An image reference is matched case-sensitively, as Mendix does (a case difference is CE1613), and the hint names the stored spelling.
    • drop, rename and move of a document that does not exist, and alter enumeration / alter microflow / alter nanoflow on one, are reported with the module's near names (ako#1821).
    • A show page passing a parameter that an earlier alter page … add parameters declared is accepted (ako#1940).
    • alter page … set (DateFormat: Custom, CustomDateFormat: …) is accepted in either order (ako#1965).
  • Page widgets that mx check refused — a text template bound to an unset optional data source is stored as null, as Studio Pro does, and no longer CE0463 (ako#1967). A snippetcall passing a data view's object names the widget instead of a page parameter the page lacks (CE0115) (ako#1968). On a 9.x project, editing a pluggable widget keeps its Database source type instead of turning it into XPath (ako#1208).
  • Association paths are checked from the context entity (ako#834) — database from $ctx/A1/A2 resolves multi-hop paths the way an association source does, and a hop that does not start at the context entity is refused instead of written as a step that cannot be followed.
  • An import mapping rooted at a JSON array returns one object (ako#1938) — the import activity was written as returning a list, which mx check reported as CE0243.
  • External entities keep their OData capabilities when $metadata aliases a vocabulary (ako#1977) — aliased Insert/Update/DeleteRestrictions and Core terms were skipped, so create external entities imported entities as not creatable or deletable (CE6630).
  • Workflows on Mendix 9 — the user-task claim MDL-WORKFLOW10 suggests uses System.WorkflowUserTask_Assignee on Mendix 9 and _Assignees from 10.0 (ako#1810). An annotation is written as Workflows$Annotation; the old storage name made Studio Pro throw TypeCacheUnknownTypeException (ako#1784).
  • describe output that did not re-execute — a text holding $$ (such as a JSON sample) is written as a string literal (ako#1003). A message definition member is printed by the attribute it references after a rename, and check refuses a member naming an attribute the entity lacks (ako#1935).
  • marketplace diff and update no longer report every Java action of a protected module as changed — the reference project now holds the package's Java and JavaScript sources, as mx module-import writes them.
  • Lint MPR008 names the loop when a loop's box causes the overlap (ako#1839) — it gives the gap needed instead of asking for @position annotations that are already there.
  • mxcli syntax documents every built-in expression function and every drop statement (ako#1866, ako#1911) — the function list is rendered from the type checker's own signature table, so the reference and check agree.

Full Changelog: v0.30.0...v0.31.0