Skip to content

Sync ako/mxcli: diff-local hunks, run --local hangs, playwright check, admin password from MDL, widget property refusals, QUAL004 callers - #1665

Merged
ako merged 44 commits into
mendixlabs:mainfrom
ako:main
Oct 28, 2026

Conversation

@ako

@ako ako commented Oct 28, 2026

Copy link
Copy Markdown
Collaborator

Syncs ako/mxcli:main into upstream: 34 commits from fork PRs ako#1745–ako#1752 and ako#1755. Two of them are a gofmt and its revert, which net to nothing.

Fixes #624, #1483, #1550, #1553, #1579, #1581, #1602, #1606, #1614, #1621, #1630, #1645, #1651 and #1659.

diff-local (#1602, #1630)

run --local and mxcli test (#1483, #1614, #1550, #1645)

playwright check

  • An unknown /p/ URL no longer passes as the home page.
  • A page on a different host from the one it signed in on is no longer failed.
  • It waits out --wait on the sign-in form.
  • A page the role may not open is reported as the sign-in form it actually lands on.
  • The docs cover --wait's default and the sign-in form.

Pages (#1651, #1606, #1659)

Security and REST (#624, #1581)

Lint, refs and catalog (#1579, #1553, #1621)

Docs

  • The local-loop-silence wiki page is synced with the cluster's new findings

ako and others added 30 commits October 27, 2026 14:54
Every QUAL004 finding printed its location as `at M.M.NAV_Home`, and so
did CONV001-010, the SEC rules and most other bundled Starlark rules:
they pass `document_name=<x>.qualified_name`, while
Location.QualifiedName() prefixes the module to DocumentName, which it
takes to be the short name. The Go rule missing_translations does the
same, and CONV018's module-level finding printed `at Sales.Sales`.

QualifiedName() is the one place a location becomes a name (text,
Markdown, SARIF, `lint -d`), so it now accepts both spellings: a
DocumentName that already starts with "<module>." is returned as is (a
Mendix document name cannot contain a dot, so the prefix is always the
module), and a module-level finding naming the module itself prints the
module once. Fixing the ~30 rule files instead would leave every
project's stale `mxcli init` copies, and every user-written rule, still
doubling.

Verified: TestLocationQualifiedNameDoesNotRepeatTheModule (with controls
for a document named like its module and a module name that prefixes
the document's) and TestShippedRuleLocationPrintsTheModuleOnce (the
shipped QUAL004 rule through the text formatter) both fail with
`at Sales.Sales.Big_MF` when the change to linter.go is reverted.
Found while reproducing #1484.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…m anywhere (#1484)

A microflow started only from the navigation — a profile's home
microflow, or a menu item's `call microflow` — was reported as
"is not called from anywhere. Remove if unused", while `mxcli refs`
listed its `home_page` / `menu_item` edge (upstream
mendixlabs#1579; the 4 NAV_ findings left in #1639).

Cause: orphaned_elements.star decided "has a caller" from its own copy
of the reference vocabulary, MICROFLOW_ENTRY_KINDS. PAGE_ENTRY_KINDS had
home_page and menu_item; the microflow list did not. It is the fourth
time that list fell behind the catalog (datasource/action/calculate,
schedule, settings/publish, event), so adding the two kinds would only
reset the clock.

Fix: the rule keeps no kind list. A microflow or page is used when any
edge in the reference graph points at it — what `refs` and
GRAPH_DEAD_ASSETS already read; no kind that targets a microflow or a
page means anything but "this runs / opens it", so no exclusion list is
needed either. Drift is guarded behaviourally: catalog.RefKinds is held
complete against the RefKind constants, and TestQUAL004CountsEveryRefKind
drives every one of them at a microflow, a nanoflow and a page through
the shipped rule (an unreferenced microflow is the per-kind control), so
a kind added later is tested the day it is declared and a reintroduced
allowlist fails by name. The two catalog tests that pinned the old lists
are replaced by it; `schedule` edges are among the kinds driven.

Verification:
- TestQUAL004CountsNavigationAsACaller (controls: an unreferenced
  microflow and page are still reported, an ACT_ microflow is still
  exempt) fails with "QUAL004 reports M.NAV_Home / M.NAV_Orders" when
  the rule change is reverted; TestQUAL004CountsEveryRefKind fails for
  every kind, and the RefKinds guard fails when a constant is left out.
- Real 11.13 app (blank app + navigation from the repro), BASELINE
  binary with only the rule swapped: NAV_Home and NAV_Orders drop out of
  QUAL004; the unreferenced control microflow M.Unused and page
  M.ItemEdit are still reported; ACT_Entry still exempt.

Repro: mdl-examples/bug-tests/qual004-1484-navigation-microflows.mdl.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ng (#1512)

After `alter settings constant … in configuration …` or `alter settings
runtime (BcryptCost: 11)`, `mxcli diff-local` printed only
"Summary: 0 new, 1 modified, 0 deleted", and `--format struct` said
"(no changes)" (mendixlabs#1602).

Cause: bsonToMDL had no case for Settings$ProjectSettings, so the unit
fell to the generic rendering — two comment lines naming the type, the
same at every revision. The same silence hit every unit whose change lies
outside what its renderer prints: a probe of one statement per kind on a
git-initialised 11.13 app found navigation, project security (admin user,
user roles), module security, a page's and a published REST service's
allowed roles, an entity index and a constant's default value all counted
and blank.

Fix:
- The settings unit renders with DESCRIBE SETTINGS' own writer
  (writeProjectSettingsMDL, split out of describeSettings) over a parse of
  each revision (new Backend.ParseProjectSettingsFromRaw, RawParts
  included, so the stored-property filter matches describe). Configuration
  constant values are masked, as the issue asks: each side is rendered
  knowing the other's value, so a change still shows as
  `value <masked: old value>` / `value <masked: new value>`; an unchanged
  or one-sided value is `<masked>`. The mask is not a string literal, so
  pasted output fails to parse instead of storing it.
- The class: git has already said the bytes differ, so a modified unit
  whose two renderings are equal is printed — in every format — as one
  line naming the property paths that changed, never their values:
  "Modified: ModuleSecurity M: changed in properties the MDL rendering
  does not show: ModuleRoles (0 -> 1 items)". diff-local now prints via
  WriteDiffResults, the writer `mxcli diff` uses for the same situation,
  and the path walker moved from mdl/scriptdiff into the executor
  (ChangedPaths) so both commands share it. The walker no longer counts
  Mendix's leading typed-array marker as a list item (a first grant read
  "1 -> 2 items"), which corrects `mxcli diff`'s note too.

Verification: real-backend tests drive diff-local over a copy of the v2
fixture with git answered from a unit snapshot — masked constant change
and addition, runtime change in unified/side/struct, the note for an
unrendered unit in all three formats; controls: an elided re-run reports
no changes, and an attribute change renders byte for byte as on
97e1cce. Revert proofs: without the settings case and the note the
tests print exactly the reported "Summary: 0 new, 1 modified, 0 deleted"
/ "(no changes)"; without the mask they print 'secret123'; without the
marker skip they read "1 -> 2 items". Real app (11.13, git): the issue's
script now shows the BcryptCost and masked-constant hunks.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…s not found (#1486)

`grant access on published rest service M.Svc to M.Role` failed with
"published rest service not found: M.Svc" when the service was created
with a `folder '…'` clause earlier in the same script, and exec rolled
the whole script back; `check -p --references` passed. Mirror of
mendixlabs#1581.

Trigger, measured on 11.13.0 with the baseline binary: no folder, a
folder that already existed (made by a previous exec), or the grant in a
second exec all resolve. Only a folder the CREATE itself makes fails.

Cause: exec caches the container hierarchy (module <- folder <- folder)
and resolves every document's module by walking up from its
ContainerID. The CREATE's existence check built that cache, then
resolveFolder -> createFolder made the folder and nothing recorded it,
so the walk stopped at an unknown folder, the module name came back ""
and the lookup missed. Every other container-changing write (create
module, move document, move/drop folder, applyDocumentFolder, rename
module) already invalidated the cache; folder creation was the gap.

Fix: createFolder records the new folder in the cached hierarchy
(ContainerHierarchy.addFolder). The page builder's own folder creator
now delegates to it, leaving one Backend.CreateFolder call site in the
executor, which TestFolderCreationHasOneCallSite holds.

Same mechanism, fixed by the same change (audit: 19 doctypes, each
created in a new folder then looked up by name, warm and cold cache):
published REST service, consumed REST service, scheduled event, task
queue and regular expression failed on baseline; the other 14 passed
because their CREATE invalidates the hierarchy afterwards or their
lookup does not use it. The worse half: a re-run CREATE OR MODIFY of any
of the five in the same script missed its own document and silently
created a second one with the same name (measured: 2 services, 2
queues, 2 regexes, 2 rest clients, 2 scheduled events); now "Unchanged".

check had nothing to predict: its grant-ref validation already tracks
script-created documents; exec's cache was what was wrong.

Verification:
- mdl/executor/hierarchy_created_folder_test.go: grant+revoke after a
  create in a new folder (module-root control), re-run CREATE OR MODIFY
  is one service, missing service still refused, the cached hierarchy
  resolves folders made by resolveFolder and the page builder, and the
  single-call-site guard. Revert proof: with the fix removed the four
  new tests fail with "published rest service not found: M.PingApi",
  "2 services named M.PingApi" and an empty module/path; controls pass.
- Real 11.13.0 app: the issue's script now execs, describe shows the
  service in 'Api/Services' with the grant, mx check 0 errors.
  mdl-examples/bug-tests/rest-grant-1486-service-in-new-folder.mdl execs
  cleanly (baseline: not found, rolled back) and mx check reports 0.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ntication microflows dead (#1639)

`mxcli refs Mod.Read_Drivers` said "no references found" for a
microflow that is a published OData resource's `ReadMode: microflow`,
and likewise for the service's `Authentication: (microflow X)`; `impact`
agreed and QUAL004 advised "Remove if unused" — 35 of the reporter's
backend's 43 QUAL004 findings.

Half 1 of the issue (QUAL004 ignoring references the catalog already
has) was, after the reporter's re-measurement, the navigation menu-item
case alone; it is fixed by the #1484 commit, which makes the
rule count every edge. This commit is half 2: the edges themselves.

Cause: buildODataServices catalogued the service and emitted no edge for
any microflow it names — entity-set read/change modes, published OData
actions, the authentication microflow. Auditing the sibling services
(every `*Microflow*QualifiedName()` in modelsdk/gen against the
builders) found the same gap where the reader already has the name: a
published REST service's authentication microflow, a business event
service operation's handler (stored in business_events_data, dropped),
and a consumed OData service's configuration / headers / error-handling
microflows.

Fix: the service builders collect one edge per distinct microflow
(builder_service_refs.go) and buildReferences emits them, from the
service (new source types PUBLISHED_ODATA_SERVICE,
PUBLISHED_REST_SERVICE, BUSINESS_EVENT_SERVICE, CONSUMED_ODATA_SERVICE,
equal to their objects ObjectType, catalog id in SourceId). Kinds are
reused, not minted, so `show callers`, the analysis graph and QUAL004
need no list change: `publish` (a published service runs it), `event`
(an event handler — entity or business event), `call` (the consumed
service calls its configuration microflow, as a workflow calls its
handlers). CatalogSchemaVersion 28 -> 29 so cached catalogs rebuild.

Verification:
- builder_service_refs_test.go pins each edge, the SourceId join, the
  dedup and the no-microflow modes; with the collection hooks reverted
  every edge test gets [] and GRAPH_DEAD_ASSETS lists Read_Rows and
  AuthenticateApiClient again (unreferenced control still listed).
- Real 11.13 app, same model, baseline vs fixed binary: `refs` for the
  read, authentication and action microflows of a published OData
  service, a published REST service's authentication microflow and a
  business event handler goes from "no references found" to one
  publish/event row each; `show callers` names the service; QUAL004 and
  GRAPH_DEAD_ASSETS drop them; the unreferenced control is still
  reported. On ako/TestApp (Studio Pro reference) the three published
  OData actions gain `publish` edges. No Studio Pro document with a read
  or authentication microflow was at hand, so those two read paths are
  verified on mxcli-written documents (keys match generated/metamodel).

Not fixed, same symptom, different mechanism (no reader or no table):
published SOAP web services, import/export mapping custom-handler
microflows, ProjectSecurity.SignInMicroflow, a microflow's
ConcurrencyErrorMicroflow, the navigation not-found page.

Repro: mdl-examples/bug-tests/catalog-1639-service-microflow-refs.mdl.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…or clause (#1448)

A call in a test body that already had an error-handling clause (`on error
rollback`, `on error continue`, a custom handler) got the runner's
`ON ERROR BEGIN … END ERROR` appended as a second one. The generated MDL did
not parse ("mismatched input 'ON' expecting ';'", plus a misleading hint about
`Error` being a keyword) and the run stopped at injection, before the app
started. Upstream: mendixlabs#1550.

Cause: attachOnError (endpoint runner) and its two copies in the legacy
after-startup runner wrapped every line startsErrorRaisingCall matched,
unconditionally. Their line-based join also broke on a custom handler (the
handler body's first `;` ended the "statement") and on a trailing comment.

Fix:
- One attachOnError for both runners, with and without @throws. It parses the
  body inside the generator's own wrapper and language version
  (visitor.FlowStatements, new) and reads each call's own onErrorClause from
  the tree — not text, so a clause on a later line counts and `on error` in a
  string, a comment or a nested handler does not. The set of wrapped
  statements is now a set of grammar rule names, pinned to the grammar.
- A call with `on error continue` or a custom handler keeps it verbatim and
  gets no second handler. `on error rollback` is Mendix's default spelled out
  (the same ErrorHandlingType as no clause), so it is replaced by the runner's
  handler, which also rolls back: same model, same verdict.
- Keeping custom handlers opened a false green that only the runtime showed:
  a handler that ends normally rejoins the flow past every statement reading
  the failed call's output — the @expect checks — and the test returned PASS
  with nothing evaluated. Each assertion now counts itself in both branches of
  its decision, and a guard after the last fails a short count ("not every
  assertion was evaluated"). Both runners.
- Legacy log parser: a test with a RUN line and no verdict read as PASS
  (StatusPass is the zero value). A re-raising handler ends the legacy runner
  exactly there; it is now an ERROR that says so.

Verification (Mendix 11.13.0, --local --db-type hsqldb, the issue's setup):
the baseline binary stops with the reported parse error; the fixed one runs
mdl-examples/bug-tests/test-runner-1448-own-error-handler.test.mdl:
plain/rollback throwing FAIL "exception during execution: boom: a"; continue
FAIL on its assertion; custom-handler-ends-normally FAIL "not every assertion
was evaluated" (PASS before the guard); re-raise ERROR "boom: a"; @throws
plain/rollback PASS, continue/custom FAIL "expected an exception but none was
thrown", re-raise ERROR; the report's two statements PASS; an assertion on
what the handler set PASS. --legacy-runner gives the same verdicts; its
re-raise case was PASS and is now ERROR. Revert proofs (stubbing each guard):
the always-attach stub reproduces "mismatched input 'ON'" in the unit tests;
the rollback, counter and RUN-without-verdict stubs each fail their test.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
After `grant read *, write * on entity Shop.Customer to Shop.User`,
`mxcli diff-local` counted the domain model as modified and printed no
hunk (mendixlabs#1630); `describe entity` showed the new rule.

Cause: domainModelBsonToMDL rendered each entity's attributes and the
associations from the raw BSON map, but never the entities' AccessRules,
so an access-only change rendered the same at both revisions.

Fix: each entity is now followed by its access rules, written by
outputEntityAccessGrants — the writer DESCRIBE ENTITY uses — over a parse
of that revision of the unit (new Backend.ParseDomainModelFromRaw, the
gen->semantic conversion GetDomainModel uses). No second grant renderer:
a test pins that every `grant` line `describe entity` prints, where-clause
and member lists included, appears verbatim on diff-local's new side. A
parse failure is printed as a comment rather than dropping the rules
silently.

    -grant read * on entity M.Item to M.User;
    +grant read *, write * on entity M.Item to M.User;

Verification: real-backend tests over a copy of the v2 fixture (git
answered from a unit snapshot) for a changed, an added and a revoked
rule, unified and struct; a backend test that the raw parse yields the
same access rules as GetDomainModel for every domain model in the
fixture. Control: an entity without rules still renders byte for byte as
on 97e1cce. Revert proofs: without the grant rendering the tests fail
with "Modified: DomainModel M: changed in properties the MDL rendering
does not show: Entities[Item].AccessRules[0]…" (the #1512 note
catching it), and with that note also removed with exactly the reported
"Summary: 0 new, 1 modified, 0 deleted". Real app (11.13, git): the
issue's script shows the -/+ grant pair; mx check 0 errors.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…roflow applies entity access (#1654)

`mxcli test --local` generates MxTest.RegisterEndpoint, the startup microflow
that registers the test endpoint and then calls the project's own after-startup
microflow. It was written without "apply entity access", and MxBuild refuses a
microflow that does not apply entity access calling one that does (CE0114). In
any project whose after-startup microflow applies entity access the build
failed, no test ran, and the error said "These errors are in the project, not
in this run's tests" about a microflow mxcli generated. Upstream:
mendixlabs#1645.

Measured direction (mx check, Mendix 11.13.0, security Off and Production):
non-applying -> applying is CE0114; applying -> non-applying and
applying -> applying (with a Java action call beside it) are 0 errors, and an
applying microflow is accepted as the after-startup microflow. MDL-SEC22
records the same table for 11.14.0.

Fix:
- GenerateEndpointMDL writes `@applyentityaccess` on RegisterEndpoint. Since
  an applying caller may call either kind, a constant serves every project
  with nothing read from the user's microflow (the suggested mirror would have
  added a second scrape of describe output). It governs only the flow's own
  activities: the Java action ignores it, the chained microflow runs with its
  own setting. Also covers `run --local --test-endpoint`, which shares the
  generator.
- buildFailureHint attributes an error located in MxTest.RegisterEndpoint or
  MxTest.RegisterTestEndpoint to mxcli — "not part of your project: mxcli
  generated it for this run … a defect in mxcli … please report it" — with
  --skip-app-startup as the escape hatch, and says "in the project" only for
  the errors that are.

Verification: on a blank 11.13.0 app with
mdl-examples/bug-tests/test-runner-1654-setup.mdl (startup microflow applies
entity access and leaves a marker), the baseline binary fails with the
reported CE0114; the fixed one passes both tests, the second proving the
startup microflow ran at boot. Controls: the same run with --skip-app-startup
fails that test ("expected $Count > 0, actual: 0") — the test consumes the
marker so an earlier boot's cannot pass it; a non-applying startup microflow
still passes both. A binary with only the attribution half shows the new
message on the live CE0114. Revert proof (patch method): with endpoint.go and
build_attribution.go checked out, TestRegisterEndpointAppliesEntityAccess and
TestBuildErrorInGeneratedStartupFlowIsMxclis fail; the project-only control
passes either way.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…on a textbox (#1698)

`textbox tb (Attribute: Name, Caption: 'Item name')` passed check and exec,
and the text box was written with no label (mendixlabs#1651): an input
takes `Label:`, `Caption:` is a button's. MDL-WIDGET07 judged every built-in
widget against the UNION of all built-in widgets' vocabularies, so a key valid
on any widget passed on all of them - the class behind mendixlabs#1307,
#1465, #1582 and #1643. Measured on the per-widget sets, the same hole drops
`Visible:` on a snippet call, scroll container, placeholder, layout header or
menu widget (their stored types have no ConditionalVisibilitySettings), and
every appearance key but Class on a scroll-container region; layouts were
walked by no widget rule at all.

Fix: every built-in page element - each buildWidgetV3 keyword, and the
children a parent builds itself (regions, layout-grid rows and columns,
navigation-list items, a data view's footer, a list view's specialization
template) - has the set of properties its builder reads, plus what
buildWidgetV3's tail writes for its stored type (appearance always, Visible
only where the gen type can hold it). nativeWidgetPropRefusals is the one
verdict: check reports it (in place of MDL-WIDGET07 for these kinds, and now
for layouts too) and exec refuses on it in buildWidgetV3 and in each parent
builder, so `exec --no-check` cannot drop either. A new rejection, gated per
ADR-0011 like MDL-V1-TABPAGEPROP: MDL-WIDGET52 under `mdl 1;`, an
MDL-V1-WIDGETPROP warning without the header. The message names what was
meant (Caption on an input -> `Label:`, Label on a button -> `Caption:`, a typo
-> did you mean) and lists what the widget takes. describe prints only a
region's Class, the one appearance key a region is read and written with.

The sets cannot drift from the builders: TestNativeWidgetProps_MatchBuilderReads
taint-walks the builders' source (go/parser, following the widget argument
through helpers) and requires each kind's reads to equal what its builder
reads, both ways; _TailMatchesGen ties Visible to the gen writer's setters;
_DescribeEmitsOnlyAccepted holds describe's cases to the same sets.

Verification. Blast radius, measured before choosing a refusal: 0 refusals
across every non-.fail doctype, bug-test and deprecated-alias script; 0 across
all skill and docs-site MDL blocks; 0 in describe -> check of every module of a
blank 11.13 app, ako/TestApp and PedApp, and of 19 doctype page and layout
scripts exec'd into a fresh app. The one script that changes is
widget-unknown-property.mdl, whose two typos are now refused; it becomes a
.fail.mdl. On a fresh 11.13 app: check fails with MDL-WIDGET52 naming
`Label:`, exec and exec --no-check write nothing; the Label: version writes
the label and mx check reports 0 errors; without the header it warns and
writes what it wrote before. Revert proof: with the verdict stubbed all seven
behaviour tests fail with the reported symptom (no violation); a table entry
the builder does not read, or one it lacks, fails the drift test.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…1703)

A responsive layout's scroll container has a LayoutMode: Headline (the top
region runs the full width) or Sidebar (the left region runs the full height).
describe layout did not print it and create layout could not write it, so the
documented describe -> rename -> exec copy of Atlas_Core.Atlas_SideBar
(stored Sidebar) came out Headline - a different frame, with Atlas's sidebar
CSS positioned for the other one (mendixlabs#1659). mx check reported 0
errors either way; the copies were Headline only because canon fills the
absent key with Studio Pro's measured default.

Fix: pages.ScrollContainer carries LayoutMode, with the members
(Headline | Sidebar, generated/metamodel's PagesLayoutMode, tested against it)
and a case-insensitive canonical spelling in sdk/pages shared by the builder
and the mutator. buildScrollContainerV3 reads it and refuses any other value,
the modelsdk writer sets it when stated, `alter layout ... set (LayoutMode: ...)
on <scrollcontainer>` writes it on a stored scroll container that carries the
key (never inventing one), and describe prints it when it is Sidebar. The
policy comes from the stored documents: of the 17 Atlas_Core layouts with a
scroll container on 11.13, 8 are Sidebar and 9 Headline, and an unset key reads
as Headline - so a layout that never set it is described and written exactly
as before. Before this commit the per-widget property check of #1698
refused `LayoutMode:` (MDL-WIDGET52) rather than dropping it; now the
scroll container's set takes it.

Verification: on a fresh 11.13 app, describe Atlas_SideBar prints
`scrollcontainer layoutContainer (LayoutMode: 'Sidebar') {`, the renamed copy
passes check, execs, stores LayoutMode "Sidebar" (baseline: "Headline" with
and without the property) and describes identically; the doctype layouts and
the bug-test script exec, `alter layout` flips Headline -> Sidebar, and mx
check reports 0 errors. On PedApp, TestCopyAtlasSideBarKeepsItsLayoutMode and
TestCopyEveryAtlasLayoutKeepsEveryWidget (which compared every widget's key
SET and so passed while the value was wrong) now compare the stored value for
every Atlas_Core layout. Revert proof: with the builder, writer and describe
changes reverted the new tests fail with the reported symptom.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…el (#1562)

After `grant read *, write * on entity Shop.Customer to Shop.User;`,
`diff-local` printed only "Summary: 0 new, 1 modified, 0 deleted"
(mendixlabs#1630). The domain model renderer printed entities and
attributes but not their access rules, so both sides rendered the same.

Each entity is now followed by the grants DESCRIBE ENTITY prints
(outputEntityAccessGrants), from a domain model decoded through the new
backend method ParseDomainModelFromRaw. The decode goes through the codec
because access-rule roles are stored as AllowedModuleRoles, not the SDK's
ModuleRoles.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011oK82sUVQEB3K5W5vh85wM
After `ALTER SETTINGS CONSTANT ... VALUE 'secret123' IN CONFIGURATION
'Default'` or `ALTER SETTINGS RUNTIME (BcryptCost: 11)`, diff-local printed
"Summary: 0 new, 1 modified, 0 deleted" and `-f struct` said "(no changes)"
(mendixlabs#1602). bsonToMDL had no case for the settings unit, so
both sides got the same one-line generic header.

The settings unit now renders with DESCRIBE SETTINGS's code (split out as
writeProjectSettingsMDL), from a revision decoded by the new backend method
ParseProjectSettingsFromRaw. Configuration constant values and the
database password are masked with a per-process HMAC fingerprint, so a
changed value still shows as a changed line without being printed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011oK82sUVQEB3K5W5vh85wM
A catalog SELECT removed the text `Catalog.` everywhere in the query, not
only from the CATALOG.<table> prefix, so a module named Catalog could not be
filtered on by qualified name: `'Catalog.Product' as lit` came back as
'Product', and `where QualifiedName = 'Catalog.Product'` returned 0 rows
(mirror of mendixlabs#1553).

Cause: execCatalogQuery stripped the prefix with a case-insensitive regex
over the raw query text (`\bcatalog\.([a-z_][a-z0-9_]*)`), which cannot tell a
table reference from data. It also rewrote quoted identifiers ("Catalog.x")
and comments, and missed `CATALOG . ENTITIES` (the grammar reads CATALOG DOT
name as three tokens, so whitespace at the dot parses and then failed with
"no such table: CATALOG.ENTITIES"). The mode warning next to it made a second
textual guess -- the word after the first "from " -- so a literal containing
"from" hid the real table and only a join's first table was ever checked.

Fix: rewriteCatalogTableRefs, a small scanner that lexes the query the way
SQLite will: it skips single-quoted strings (doubled-quote escape), "..." /
`...` / [...] identifiers and -- / /* */ comments, and drops `catalog` only
when it is a whole identifier followed by a dot and a name (any case,
whitespace or comments around the dot, not itself after a dot). It returns
the tables it rewrote, and the mode warning now checks each of them. An
ATTACHed `catalog` schema would avoid rewriting entirely, but the catalog is
an in-memory SQLite database, which cannot be attached to itself without
changing how it is opened; the scanner is contained and local.

Every path that runs user SQL against the catalog goes through this one
function: -c, exec scripts, the REPL and the LSP (which shells out to -c).
Starlark lint rules get builtins, not raw SQL.

Verification:
- New tests: the rewrite table (literals, escapes, quoted identifiers,
  comments, whitespace at the dot, unterminated literal, plus controls:
  upper/lower/mixed prefix, joins, subqueries, ProductCatalog / my_catalog
  aliases, no prefix), the issue's queries end to end through the MDL parser
  on a catalog holding Catalog, ProductCatalog and MyCatalog modules, and the
  mode warning for a literal containing "from" and a gated joined table.
- Revert proof: with the production change removed, 19 subtests fail with the
  reported symptom; the controls pass both ways.
- Real 11.13.0 app with a Catalog module: before lit = "Product" and n = 0,
  after lit = "Catalog.Product" and n = 1, on -c, an exec script and the REPL;
  mdl-examples/bug-tests/catalog-1450-prefix-in-string-literals.mdl fails
  before ("no such table") and passes after.
- go vet + go test ./mdl/executor/ pass; make check-findings passes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Upstream: mendixlabs#1621.

Cause: the storage format had two sources. The reader decides v1/v2 from
disk (an mprcontents/ folder beside the .mpr, reconciled by the
Unit.Contents column) and reads units with that answer; every consumer
that branches on the format (diff-local's guard, the mx harvest,
diag --check-units, fix hashes) asks the reader. ProjectVersion.FormatVersion
came separately from version.DetectFromDB, which read
_MetaData._FormatVersion and, when the column was missing, assumed "a newer
schema (11.6.2+), so 2". The column is missing precisely because the schema
is v1 (absent in a 9.24.30 and an 11.13.0 v1 project; present and 2 in
11.6.6, 11.13.0 and 11.14.0 v2 ones), so every v1 project printed
`MPR Format: v2` in SHOW VERSION and STATUS and logged mpr_format=2 in the
session log that `diag --bundle` ships.

Fix: DetectFromDB no longer sets FormatVersion (it reads the three columns
both schemas share), and the reader stamps its own detection onto the
ProjectVersion it returns — in OpenWithOptions after the Unit.Contents
reconciliation, and in OpenWithDB. The MCP backend delegates to the same
reader, so it is covered too.

Verification:
- modelsdk/mpr: TestProjectVersionReportsTheDetectedStorageFormat (v1
  fixture, v2 control, v2 schema detached from its folder),
  TestOpenWithDBReportsTheFormatItWasGiven,
  TestDetectFromDBDoesNotGuessTheStorageFormat.
- cmd/mxcli: TestShowVersionReportsTheStorageFormatOnDisk runs the real CLI
  on a v1 and a v2 project and asserts SHOW VERSION, STATUS and the session
  log's connect record.
- Revert proof: with the production change removed, the v1 cases fail with
  FormatVersion = 2 / `MPR Format: v2` / mpr_format = 2 and the v2 controls
  pass. Reverting only the reader stamp fails every case with 0; moving the
  stamp before the reconciliation fails the detached-v2 case with 1.
- Real apps (11.13.0, made with mx create-project, one --use-mpr-format-v1):
  v1 app v2 -> v1 in SHOW VERSION, STATUS and the session log; v2 app v2
  before and after. diff-local, diag --check-units and fix hashes already
  treated the v1 app as v1 and are unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…its it does not render (#1512)

A changed constant, a new module role, or any unit type without an MDL
renderer was counted as modified with no hunk, because the generic
renderer's header reads the same before and after (mendixlabs#1602,
"at minimum, fall back to a field-level difference summary").

When both renderings agree but the stored documents differ, diff-local now
walks the two documents and lists the changed fields by path, type and
size, never by value, as an MDL comment hunk and as structural changes.
Security$ModuleSecurity and Projects$ModuleSettings no longer fall into the
module renderer (`create module ;`) through strings.Contains.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011oK82sUVQEB3K5W5vh85wM
Run from outside the repository with `-p <app>/App.mpr`, diff-local failed
with "failed to find changed files: failed to git diff: exit status 1"
(side note in mendixlabs#1602); from a subdirectory of the repo it
read working-tree files at repo-root-relative paths and counted nothing.

Git now runs in the project's directory, reports `--relative` paths, and
revisions are read as `<rev>:./<path>`, so the cwd no longer matters.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011oK82sUVQEB3K5W5vh85wM
A page action calling a parameterless flow with an empty argument list,
`Action: call microflow M.Ping()`, failed check with "mismatched input ')'
expecting the start of a statement", while the flow body accepts `()` and
the widget accepted the call without brackets (mendixlabs#1606).

Cause: microflowArgsV3 in MDLPage.g4 required at least one argument. It
is the argument rule of every page action (call microflow/nanoflow, show
page and their MDL-DEPR020 spellings) and both flow data sources, so the
same error hit Action, OnClick, OnChange, pluggable action properties,
alter page set Action/DataSource, navigation and menu-document items,
fragment arguments and building-block overrides. fragmentArgs had the
same shape; there `use fragment Hdr()` was not even an error without a
project: it parsed as two generic widgets (`use` named `fragment`, and
`Hdr` with an empty property list), and check -p reported MDL-WIDGET25.

Fix: both rules accept an empty list inside the brackets, as the flow
body's callArgumentList and workflowCallArguments already do. No visitor
change is needed: the builders return nil for no arguments, so `X()`
builds exactly the statement `X` builds. Additive, so no mdl-version
gate. describe still writes the bracket-less form. The page.action
syntax topic and the create-page reference say so; the syntax-docs guard
keeps the reference on the describe form with a corrected reason.

Verification:
- mdl/visitor/visitor_empty_call_args_test.go: 23 sites, X() vs X as
  whole statements and deprecation codes, a real argument list as the
  control. Reverting the grammar fails all 23 (22 parse errors, and use
  fragment on statement equality).
- Real 11.13.0 app: a page written with () and its bracket-less twin
  store identical BSON once $ID and the page name are set aside, and
  describe identically without brackets; describe -> exec reports
  Unchanged page; mx check 0 errors.
  mdl-examples/bug-tests/widget-action-1516-empty-call-args.mdl.
- All 81 non-.fail doctype-tests and all bug-tests check as before; only
  the new bug test changes (fail -> pass).

Not changed on purpose: `use building block X()` (an override list) and
`snippetcall (Params: ())` (a property map) stay errors with messages
that name what is expected; a flow-body call still requires its ().

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
CE0129 "Administrator password has not been set" could only be resolved in
Studio Pro (mendixlabs#624): `alter app security` had no property for
it, and the backend no writer for Security$ProjectSecurity.AdminPassword.

    alter app security ( AdminPassword: 'N3w-Admin-Passw0rd' );

joins AdminUserName in the property list. '' is refused: measured on
11.13.0, an empty password is CE0129 at prototype and production level.
mxbuild does not check it against the password policy, so neither does
mxcli. The value is never echoed: exec reports "Admin password set", and
`show project security` says only whether one is set.

Verified on a copy of testdata/pedapp with the password blanked at
production level: mx check 1003 errors incl. CE0129 before, 1002 and the
same CE codes as the untouched app after.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011oK82sUVQEB3K5W5vh85wM
Five findings now share one defect: diff-local compares rendered MDL, so a
change its renderer does not print counts as modified and shows nothing.
The new page frames the shapes it has taken and the remedies, and points
at the field-level fallback that closes the class.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011oK82sUVQEB3K5W5vh85wM
`create constant … type T default v` is MDL-DEPR136, which the conformance
gate refuses in bug-test scripts.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011oK82sUVQEB3K5W5vh85wM
It was not gofmt-clean on the base commit, so `make lint` failed there too.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011oK82sUVQEB3K5W5vh85wM
On a page that shows the sign-in form, `playwright check` waited the
whole --wait (33s at --wait 30s) and then reported the form as still
loading, advising a longer --wait, next to the sign-in failure. The client
keeps a POST /xas/ open behind the form (measured on 11.13.0 and 11.15.0),
so the probe's "no request open" condition (#845) never held.

Once the sign-in form is up the probe now settles on text and busy
indicators alone, and the verdict does not call a sign-in form still
loading. Against the same live apps: 33s -> 3s, still exit 1, with the
sign-in form as the only failure.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011oK82sUVQEB3K5W5vh85wM
…gned in on (#1216)

`mxcli playwright check http://frontend.local:8180/ -p app.mpr --role R`
failed every run with "the sign-in form is showing, not the page", while
the same command without the URL passed. With -p the base URL comes from
the run handshake, http://localhost:<port>, and the sign-in ran there.
The page named another host for the same app, the browser sent it none
of localhost's cookies, and the client started anonymous. With a saved
session the renewal path signed in on localhost again and retried, so
each run also added one live session on the unlicensed runtime.

The pages are now grouped by origin and each group signs in on its own
host, with the session saved per host and user as before. Measured on
11.13.0 against the boot URL http://127.0.0.1:8180/ (the handshake says
localhost). Before: FAIL, and three runs left three sessions (M2EE
runtime_statistics). After: OK, and four runs left one.

The check also tells the login where the runtime log is
(<project>/.mxcli/runtime.log). The session-cap explanation can now
fire. Before, it never could: at the cap, the login printed only "Sign
in failed".

Sign-out, best effort and never part of the verdict:
- --fresh-login signs the replaced session out before signing in.
- A session the run signed in that got no page past the sign-in form is
  signed out rather than kept.
- The new --sign-out ends the run signed out.
A session that is working is still kept and reused, so repeated checks
hold one slot rather than signing in and out on each run.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011oK82sUVQEB3K5W5vh85wM
`mxcli playwright check / /p/doesnotexist` printed the home page's title
and heading for both pages, then `OK 2 page(s)`. The probe already
recorded the location the browser ended on, but nothing compared it
with the URL asked for, and nothing printed it.

Measured on 11.13.0 and 11.15.0, with the same result on both:
- The runtime answers every /p/<x> with a 301 to /#/<x>, whether or not
  a page has that URL.
- The client then replaces the location with /p/<x> for a known page.
  For an unknown one it uses /, renders the home page and opens an
  information dialog ("The page you requested was not found").
- The status is 200 throughout.
So the final path is the signal. It does not depend on the language,
and it needs no second load of the home page to compare titles.

ProbePages now records the URL each page was loaded from. When the
browser ended elsewhere, the verdict says where (`url=/`), and the
title and heading describe that page. A /p/ URL that did not stay put
fails, with one exception: a page that went to the sign-in form keeps
that failure as its only reason. Matching is exact, as the client's is:
/p/ITEMS and /p/items/ fail where /p/items passes. The client's
information dialog is shown as an ALERT line and does not fail a page.
`run --page-check` prints the same `url=` field.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011oK82sUVQEB3K5W5vh85wM
The options table gave --wait a 4s default; the code has used 15s since
the probe began waiting for open requests (#845). Also say that the
sign-in form is measured once it holds still rather than waited for.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011oK82sUVQEB3K5W5vh85wM
… is down (#866)

startLocalPostgres fell back to the user-owned initdb cluster whenever no
service manager made the port ready, without asking whether a system
cluster for that port existed. Since the fallback works on Debian/Ubuntu,
a container restarted with the system cluster down got a fresh, empty
cluster on the same port, and the app booted healthy against it.

Before initdb, probe the package-made data directories (postgresql-common
via /etc/postgresql, RHEL/PGDG, Arch, Homebrew, Windows); a PG_VERSION
that a non-root user may not stat counts as present. When one is
configured for the requested port, refuse with its data directory and the
command that starts it, unless the user-owned cluster already exists (a
restart, noted) or MXCLI_ENSURE_DB_NEW_CLUSTER=1. Every initdb now prints
a WARNING that a new, empty cluster was created. The bootstrap hook's
banner takes mxcli's first "Error:" line, not the last line mentioning
"error", which behind a service diagnostic was pg_ctlcluster's.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011oK82sUVQEB3K5W5vh85wM
…stale Gradle daemon (#1340)

mx sync-java-dependencies ran unbounded with no output, and every
one-shot mx/mxbuild run used Gradle's default daemon, which outlives the
run; with an idle daemon from an earlier run alive, the next sync hung for
10+ minutes printing nothing (mirror of mendixlabs#1483).

One-shot runs (the sync, mxcli new's settle build, docker build) now get
-Dorg.gradle.daemon=false appended to GRADLE_OPTS, keeping the user's
options and LD_PRELOAD and any explicit daemon setting. The sync runs
under the existing process-tree watchdog: "still resolving..." every 30s,
killed after 10 minutes (MXCLI_SYNC_JAVA_DEPS_TIMEOUT) with an error
naming the step, the likely cause and the last output. mxbuild --serve
keeps its daemon.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011oK82sUVQEB3K5W5vh85wM
… stop reason (#1542)

Every run --local process deferred an unconditional removal of
.mxcli/run-local.json, including --setup, which the SessionStart hook
runs on every session start, resume and compaction. So the hook deleted
the handshake of the detached run serving the app, and oql/log fell back
to default credentials against a live runtime: HTTP 403, blamed on the
password (mirror of mendixlabs#1614). Separately, the stop reason
was read from the JVM's stdout, but the trial licence's "Maximum run time
exceeded" is an application log line that only reaches runtime.log, and
run status printed only the first line of the recorded error.

The handshake is now withdrawn only by the run whose pid it carries,
right after run-state.json records phase stopped, ended and a one-line
reason, read from the newest boot's section of runtime.log (licence
limit, out of memory, else the last ERROR line). run status prints
"stopped: trial licence time limit reached at 18:43:16 (runtime.log);
restart with mxcli run restart -p ...", names the log's reason for a
killed run, and flags a live run whose handshake is missing. oql, log
and constant --apply say to check run status, not the credentials, when
the handshake was missing or stale. The lifecycle is documented in the
run-local skill and the docs-site page.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011oK82sUVQEB3K5W5vh85wM
Five beats from the triage board's local-loop-silence fixes (#866, #1340,
#1542, #1216, #998, and the sign-in form wait): a fallback whose success is
the defect, a shared file needs an owner, a one-shot Gradle run must not
leave or wait on a daemon, a signed-in check is a host and a session, and a
settle rule written for one page holds every page to it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011oK82sUVQEB3K5W5vh85wM
…ner property (#1698)

The upgrade property test failed on
mdl-examples/bug-tests/action-slot-not-an-action-1062.fail.mdl: the mdl 0
original of `container cCard (OnClick: TOTALLY_MADE_UP)` executes, and under
`mdl 1;` the new per-widget verdict refused it - "`OnClick` is not a property
mxcli writes on a container" (MDL-WIDGET52). A container's OnClick IS its
click action. The visitor stores `OnClick:` under "Action" only when the
value parses as an action (#603); a malformed value falls through to the
generic `key: value` branch and keeps its key as written. That is the action
slot holding something that is not an action, which MDL-WIDGET28 reports.
Refusing it here as well named the wrong problem and, this rule being gated
on the header and that one not, made the upgrade change what the script
writes.

Fix: nativeWidgetKeyAliases models the rename explicitly - `onclick` is
accepted wherever the kind takes Action, and is owned by the click-action
rules wherever Action is - without making it a builder read
(TestNativeWidgetProps_AliasesFollowTheVisitor ties it to
actionKeywordSource). It is the only key the visitor renames on a
well-formed value: every other dedicated branch stores under its own name,
and Visible/Editable lower to keys judged as one group.

The same shape had a second hole: MDL-WIDGET28 looked its slots up by exact
key, so the malformed value written `onClick:` (the casing the skills
teach), `action:` or `onchange:` was reported by nothing and written as a
dead control. On a built-in widget it now finds every case of the slot's
name; on a pluggable widget only the exact one, since its keys are its
package's own.

Verification: TestNativeWidgetProps_MalformedActionIsTheActionSlotRules
(no MDL-WIDGET52 for OnClick/onClick/action/onchange scalars, MDL-WIDGET28
for each, exec under mdl 1 builds the container) and _OnClickAliasControls
(a well-formed OnClick is written, `Bogus:` on a container and a click
action on a label are still refused). Revert proof: without the alias the
first test fails with the reported refusal at check and at exec; with
MDL-WIDGET28's exact lookup it fails to report four of the five. Upgrade
property test, shards 1/3, 2/3, 3/3: pass (17 + 14 + 11 scripts execute to
the same model, 62 executable). Re-sweep of all 1129 mdl-examples scripts
including .fail ones: no MDL-WIDGET52 / MDL-V1-WIDGETPROP outside the two
intentional .fail tests, which carry `mdl 1;` already.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
ako and others added 14 commits October 27, 2026 21:57
fix(diff-local): show settings and access-rule changes, and never count a change silently
fix(lint): QUAL004 counts every catalog reference as a caller; published-service microflows get edges
fix(pages): refuse a property a built-in widget does not take; keep a scroll container's LayoutMode
fix(test): keep a call's own on error clause; the generated startup flow applies entity access
fix: a folder created mid-exec joins the cached hierarchy, so a grant finds the service in it
fix(grammar): an empty argument list on a page action or flow data source means no arguments
fix(catalog): rewrite the CATALOG. table prefix only where SQLite reads a table name
fix: SHOW VERSION reports the MPR format the reader detected, not a guess
`--ref main...feature-branch`, documented as "changes since common
ancestor", warned "failed to read .feature version" for every unit and
reported 0 modified: parseRefRange split on the first "..", so the target
became ".feature". It also read the old side at main, where `git diff
a...b` compares against the merge base.

The range is now resolved once: three dots read the old side at
`git merge-base a b`, labelled "merge base of a and b"; an empty side is
HEAD, as it is to git.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011oK82sUVQEB3K5W5vh85wM
Git's rename detection reported a moved .mxunit as "R100 old new", which
diff-local read as a modification of the old path; the new revision has no
such file, so the unit was skipped with a warning and counted nowhere. A
unit's file name is its ID, so the move is one unit deleted and one added:
`git diff` now runs with --no-renames and reports it that way.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011oK82sUVQEB3K5W5vh85wM
…n-in form

The runtime answers a signed-in client's session request for a page its
user roles may not open with 403, and the client lands on
/login.html?messageCode=403#/<page>: the same sign-in form, with no
message on it, that an anonymous browser or a forgotten session gets
(401, no messageCode). The check took the form for "not signed in": it
told the user to pass credentials, its stale-session retry signed in
beside the live session on every run, and a check of only such pages
signed its valid session out.

PageSignals.AccessDenied() reads messageCode=403 from the final URL. Such
a page now fails as "access denied: signed in as demo_user (user role
User), the runtime refused /p/secret (403)", the verdict says ACCESS
DENIED, and it no longer counts as needing a sign-in, so its session is
neither renewed nor signed out.

Measured on 11.13.0 and 11.15.0, `playwright check /p/secret /p/items
-p app.mpr --role User` three times: before, `login demo_user` on every
run and 1, 2, 3 live sessions (M2EE runtime_statistics); after, one
sign-in and one session throughout, /p/secret alone included.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011oK82sUVQEB3K5W5vh85wM
main had already fixed #1512 (af4c7de) and #1562
(3317b13) with the same design this branch used: Backend
ParseProjectSettingsFromRaw / ParseDomainModelFromRaw, DESCRIBE's own
writers, and a note for units whose renderings agree. Those are taken as on
main, and this branch's duplicates are dropped: the HMAC value mask and
settings renderer (cmd_diff_local_settings.go), the field-level fallback
(cmd_diff_local_fields.go), the grant rendering, their tests, findings and
bug-test scripts.

Kept from this branch, re-applied on main's cmd_diff_local.go: git runs in
the project directory with --relative paths (#1512's side note),
--no-renames, the three-dot range read at the merge base, and the exact
Projects$ModuleImpl dispatch. Their tests now use enumerations, whose
values the rendering shows; main's git stub in the unrendered-change
harness answers with project-relative paths, as git run in the project
directory does. Generated backend stubs regenerated with both sides'
methods (SetProjectAdminPassword from this branch).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011oK82sUVQEB3K5W5vh85wM
This reverts commit 79e6637. The file was already gofmt-clean: the gofmt
on this container's PATH is an older Go's (/usr/local/go), not the go1.27.3
toolchain's that CI runs, and it formats this composite literal
differently. CI's lint-go flagged the "fixed" version. Checked with
$(go env GOROOT)/bin/gofmt: every tracked Go file is clean.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011oK82sUVQEB3K5W5vh85wM
fix: the local-loop-silence and project-security clusters, and the diff-local fixes #1745 did not cover
@ako
ako merged commit 8b6f421 into mendixlabs:main Oct 28, 2026
15 checks passed
Ricovl pushed a commit to Ricovl/mxcli that referenced this pull request Oct 28, 2026
…holder-dropped

fix: a filter's Placeholder/Adjustable in create page are not written (ako#1652)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add ALTER PROJECT SECURITY ADMIN PASSWORD (resolve CE0129 from MDL)

2 participants