Skip to content

Sync ako/mxcli: drop the v1 BSON driver; container RenderMode, Calendar expression properties (CE0642), column filter-slot hint - #1594

Merged
ako merged 14 commits into
mendixlabs:mainfrom
ako:main
Oct 20, 2026
Merged

ako merged 14 commits into
mendixlabs:mainfrom
ako:main

Conversation

@ako

@ako ako commented Oct 20, 2026

Copy link
Copy Markdown
Collaborator

Syncs ako/mxcli:main into upstream: 9 commits from fork PRs ako#1498 (silent-property-drop fixes), ako#1497 (dropping the v1 BSON driver) and ako#1500 (a test follow-up).

Fixes

  • A container's RenderMode is described, alterable and validated (create layout: a container's RenderMode is silently dropped; check and exec pass #1582).
    • The write was always correct; bson dump shows RenderMode: "Main" stored. But describe never printed it, so it looked dropped, and a describe → exec round trip really did drop it.
    • describe now prints RenderMode: Main, leaving out the default Div.
    • alter page|layout … set (RenderMode: …) on a container now writes the value. Before, it was refused as "not a property of this built-in widget".
    • create used to write any string verbatim into the enum ('main', 'Banner'). Values are now matched case-insensitively against the generated enum, and an unknown member is refused.
  • Pluggable widgets' top-level expression properties are written (Calendar widget: the required editable expression cannot be written; mx check fails CE0642 #1578). The Marketplace Calendar's required editable and showEventDate used to fail mx check with CE0642. Four gaps are closed:
    • The template generated from the .mpk now writes an expression's declared defaultValue instead of an empty expression.
    • The widget definition generator maps every top-level expression by its own key (v19). Before, it drew a false MDL-WIDGET06.
    • When a package declares no Editability system property, editable: goes to the widget's own property. check (MDL-WIDGET41) and the build both follow that.
    • describe prints stored expressions.
  • A column's Filterable: hint names the filter slot (feat: Support Filterable property on DataGrid2 COLUMN in MDL #177, the remaining part). check already refused the key, but its hint listed the column's properties, or suggested FilterType. It now explains that filter is a slot and shows the body form: column (…) { textfilter w1 (…) }.

Dependencies: the deprecated v1 BSON driver is gone (ako#1497)

  • go.mongodb.org/mongo-driver v1.17.10 is removed. mxcli now runs on mongo-driver/v2 v2.9.2 alone. The v1 module was flagged for CVE-2026-107325, which has no fix on the v1 line. mxcli was never exposed to it, but no upgrade could clear the scan finding.
  • This is not a find-and-replace. v2 decodes nested documents into bson.D where v1 gave primitive.M or map[string]any. So a .(bson.M) or .(map[string]any) type assertion silently fails, and the next write drops a property.
    • The new leaf package modelsdk/bsoncompat (UnmarshalDocM, UnmarshalDocMap, DMap) uses v2's own decoder options to restore v1's shapes.
    • It is applied at 73 call sites, chosen by each target's declared type. bson.D destinations need no change.
    • Existing tests caught every instance. For example, scheduled events lost a property, and Atlas layout copies lost their icon and image lookups.
  • Guards:
    • TestNoV1DriverImports and TestGoModDoesNotRequireV1Driver catch v1 coming back. The first already caught 7 files from ALTER NAVIGATION/MENU, which were migrated in a follow-up commit.
    • TestDecodeShapes pins the nested shapes, so it fails loudly if a driver release makes a helper unnecessary.
  • go mod tidy runs again. mdl/exprcheck/roundtrip_test.go sat behind a roundtrip tag that nothing ever passed, and it imported the deleted legacy engine. It is ported to mdl/executor/exprcheck_roundtrip_test.go under integration, with a vacuity guard. A follow-up commit makes it resolve a microflow's module through folders.
  • Protected (add-on) modules: export levels are not shown by DESCRIBE and not checked, so check passes builds that fail with CE1406 and CE5400 #1498's container RenderMode test now uses the v2 driver (fix(test): point container_rendermode_test at the v2 BSON driver ako/mxcli#1500). The two PRs landed side by side, and the test still imported v1.

Validation

  • Each silent-property-drop test fails with the reported symptom when its fix is reverted.
  • Tested on mxbuild 11.13.0 against a mxcli new app with Calendar v2.6.1:
    • main gives CE0642 twice. This branch gives 0 errors with the properties unset, set to 'true', and set to false.
    • A describe → exec round trip reports Unchanged page.
    • The column-filter form the new hint suggests builds with 0 errors.
  • Two generator tests are updated, because they asserted that expression properties stay unmapped, which is the behaviour this PR changes.
  • There is a regression script per issue in mdl-examples/bug-tests/. The Calendar one needs the package.
  • make build, test, lint, check-mdl, check-findings and check-wiki-pages pass.
  • For the driver removal, go build, go vet (also under -tags integration), go mod tidy and go mod verify pass. go version -m bin/mxcli lists only mongo-driver/v2.

Docs

  • docs-wiki/bug-patterns/silent-property-drop.md gets two insights:
    • Read the stored document before blaming the writer.
    • A builtin MDL key can shadow a widget's own property of the same name.
  • Each fix records its finding

claude and others added 14 commits October 20, 2026 10:16
A layout or page container's RenderMode was stored but describe never
printed it, so it read as dropped and a describe -> exec round trip did
drop it. alter set routed RenderMode only for dynamic texts and refused
it on a container, and create wrote any string verbatim into the enum.

Fixes mendixlabs#1582 (#1487).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SPETbWEhRQ85EX4gckfCxU
The Marketplace Calendar's required editable and showEventDate
expressions (defaultValue="true") failed mx check with CE0642: the
mpk-generated template wrote them empty, the def generator gave a
top-level expression no mapping, editable: only reached the widget-level
editability (which the Calendar does not declare), and describe printed
neither.

The template now writes an expression's declared default, the generator
maps every top-level expression by its own key (v19), editable: routes
to the widget's own property when the package declares no Editability,
and describe prints stored expressions.

Fixes mendixlabs#1578 (#1483).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SPETbWEhRQ85EX4gckfCxU
go.mongodb.org/mongo-driver v1.17.10 is removed. mxcli now runs on
mongo-driver/v2 v2.9.2 alone, and `go version -m bin/mxcli` lists only
v2.

It was the last component left on a customer's blocking dependency scan,
flagged for CVE-2026-107325, which has no fix on the v1 line and never
will: v1.17.10 is that line's final release and the module is marked
deprecated. mxcli was never exposed — the panic is in
bsoncore.Array.Validate, which no file imported — but the finding would
have recurred on every scan, with no upgrade able to clear it.

The migration is not a find-and-replace. The two drivers decode nested
documents into different Go types, and the type assertions mxcli reads
them back with were written against v1's. Measured on identical bytes:

  destination        v1 nested        v2 nested
  bson.D             primitive.D      bson.D          equivalent
  bson.M             primitive.M      bson.D          BREAKS
  map[string]any     map[string]any   bson.D          BREAKS

A failed type assertion is not an error, so the value is silently
dropped and the next write emits a document with a property missing,
which Studio Pro refuses to merge (canon.CompletePropertySets,
mendixlabs#1373). The repo's own tests caught every instance:
scheduledevents wrote 13 properties where Studio Pro writes 14,
TestNoopRerun_ReportsUnchanged tripped the settings guard, and the Atlas
layout copies lost their icon and image lookups.

Rather than audit the 618 type assertions in the migrated files — where
a wrong one compiles clean — this uses the switch v2's decoder already
has. New leaf package modelsdk/bsoncompat:

  UnmarshalDocM    DefaultDocumentM()   -> nested bson.M, v1's shape
  UnmarshalDocMap  DefaultDocumentMap() -> nested map[string]any
  DMap                                  -> the bson.D.Map() v2 removed

Applied at 73 call sites found by how each Unmarshal target was
DECLARED, not by reading the call: 39 bson.M destinations and 34
map[string]any. The two helpers are not interchangeable — bson.M is a
named type, so a bson.M value does not satisfy .(map[string]any) — and a
bson.D destination needs neither, since a helper there re-introduces the
bug inverted. mdl/executor's TestResolveWebServiceName_ReadsMapDecodedDocuments
already existed for that exact confusion and caught this migration too,
because it pins its own precondition instead of passing vacuously.

bsoncompat depends on nothing inside mxcli. It cannot live in
modelsdk/codec: codec imports modelsdk/mpr, which imports canon and
meta, whose own tests could then not import codec at all ("import cycle
not allowed in test").

Guards, both verified by control (each names the offending file or
go.mod line): TestNoV1DriverImports walks the tree for the v1 import and
TestGoModDoesNotRequireV1Driver reads go.mod, since re-adding either is
not a build error and nothing else would notice until the next scan.
TestDecodeShapes pins all three nested shapes and fails loudly if a
driver release makes a helper unnecessary.

Note `go mod tidy` cannot run in this repo at all — mdl/exprcheck/
roundtrip_test.go (//go:build roundtrip) imports the long-deleted
mdl/backend/mpr — so the requirement was dropped with `go mod edit
-droprequire` and a manual go.sum prune. That dead test file is
pre-existing and left untouched here.

Verified: go build ./... clean, go vet clean, gofmt clean, 105 test
packages pass with 0 failures, `go mod verify` reports all modules
verified, and the built binary links only mongo-driver/v2 v2.9.2.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Rer1mzP69XxbXKxQqheipM
A Data grid 2 column has no Filterable property: its filter is the
column's filter slot, written as a filter widget in its braces. check
already refused the key, but the hint listed the column's properties
(with a project) or suggested FilterType (without one). A key that
starts with an item's widgets-slot key now names the slot, the body
form, and the widget types it routes.

Fixes the remaining part of mendixlabs#177 (#1230).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SPETbWEhRQ85EX4gckfCxU
Read the store before the writer (a container's RenderMode was stored;
only describe lacked it), and a builtin key can shadow a pluggable
widget's own property of the same name (the Calendar's editable).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SPETbWEhRQ85EX4gckfCxU
Merging origin/main brought in PR #1495 (ALTER NAVIGATION / ALTER MENU),
whose 7 new and changed files import the v1 BSON driver. The merge itself
was conflict-free, so nothing flagged them — which is exactly what
TestNoV1DriverImports is for: it failed on the merged tree and named
them.

All 7 decode into bson.D destinations, which need no bsoncompat helper:
v2 nests bson.D inside bson.D just as v1 nested primitive.D, so their
type assertions are already correct. The change is the import path and
primitive.X -> bson.X only.

Verified on the merged tree: go build ./... clean, go vet clean, gofmt
clean, 106 test packages pass with 0 failures, zero v1 imports remain,
and the built binary links only mongo-driver/v2 v2.9.2.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Rer1mzP69XxbXKxQqheipM
mdl/exprcheck/roundtrip_test.go could not compile. It imported three
things that no longer exist: mdl/backend/mpr (the legacy engine,
deleted), executor.GetHierarchyForMining and
executor.DescribeMicroflowToString.

It never broke a build because it sat behind `//go:build roundtrip` — a
tag no Makefile target and no CI job has ever passed, and the only file
in the tree that uses it. So it rotted through the legacy-engine
deletion and two executor API removals without one red build. The only
command that still saw it was `go mod tidy`, which considers every build
tag and failed with "no matching versions for query latest" on the
deleted package — a message that reads as a dependency problem rather
than a dead test. That is why the v1 BSON driver had to be dropped from
go.mod with `go mod edit -droprequire` and a hand-pruned go.sum earlier
on this branch.

Ported to mdl/executor/exprcheck_roundtrip_test.go under the
`integration` tag rather than deleted: the assertion is one the unit
tests cannot make. Describing every microflow and re-parsing each
expression with the robust parser catches grammar gaps — a missing
parsePrimary branch, a missing funcTable entry, a missing slot
constraint — where the unit tests only ever feed the parser expressions
someone thought to write down.

It lives in package executor because that is where the real-project
harness already is (setupTestEnv, describeMDL) and because
`make test-integration-executor` actually runs it. The import cycle the
original file's header warned about does not arise in this direction:
mdl/exprcheck does not import mdl/executor. The removed
GetHierarchyForMining is replaced by building the containerID -> module
name map from backend.ListModules().

Two vacuity guards added, because the fixture project lives in a
separate repo (mx-test-projects/) and TestMain skips the whole suite
when it is absent: the test now fails if no microflow could be described
or if no expression slot was found, rather than reporting success
against an empty walk.

With the dead import gone, `go mod tidy` runs clean and pruned 35 more
stale go.sum lines than the hand-edit had, so the v1 driver's removal is
now the tool's work rather than mine.

Verified: go build ./... clean, go vet clean (also with -tags
integration), gofmt clean, 106 test packages pass with 0 failures,
`go mod tidy` exits 0, `go mod verify` reports all modules verified. The
ported test compiles under the integration tag but SKIPS in this
container, which has no mx-test-projects checkout — CI's
`integration (executor)` job is what exercises it.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Rer1mzP69XxbXKxQqheipM
CI's integration (executor) job failed the round-trip test ported in the
previous commit, and failed it on the vacuity guard rather than on a
hint:

  described 1 microflow(s) but found no expression slots; either the
  test project lost its expressions or the walk below no longer matches
  the AST

The guard was right and the test was wrong. A microflow's ContainerID is
its FOLDER when it sits in one, not its module, so the flat map I built
from ListModules resolved almost nothing — and an unresolved module name
meant the microflow was skipped before it was ever described. That is
precisely the job the removed executor.GetHierarchyForMining did, and
replacing it with a flat lookup is what broke the walk.

NewContainerHierarchyFromBackend is the live replacement: FindModuleID
walks up through folders to the owning module. It is exported and this
test is in package executor, so it needs no new seam.

Measured locally against testdata/expr-checker/minimal.mpr, which this
container does have (the integration fixture, mx-test-projects/, it does
not — hence the first push going out unvalidated):

  resolved by flat ListModules map  :  1/16 microflows
  resolved by hierarchy FindModuleID: 16/16 microflows

and the full describe -> reparse -> walk chain then yields 16 microflows
described and 11 SourceExpr slots, where it previously found 0. The
other 36 expression nodes are LiteralExpr and VariableExpr, which the
walk deliberately ignores: they are already structured, so there is no
raw source text for the robust parser to re-check.

So both halves of the guard's complaint are closed, and the thresholds
it asserts (at least one microflow described, at least one expression
slot) are met with margin rather than exactly.

Verified: go build ./... clean, go vet clean (also with -tags
integration), gofmt clean, 106 test packages pass with 0 failures.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Rer1mzP69XxbXKxQqheipM
…55o66g

fix: silent-property-drop cluster — container RenderMode, Calendar expressions, column Filterable
refactor(deps): move off the deprecated v1 BSON driver entirely

Removes go.mongodb.org/mongo-driver v1.17.10; mxcli now runs on
mongo-driver/v2 v2.9.2 alone. It was the last component on a customer's
blocking dependency scan, flagged for CVE-2026-107325, which has no fix
on the v1 line and never will — v1.17.10 is that line's final release
and the module is deprecated.

The two drivers decode nested documents into different Go types, so a
bson.M or map[string]any destination silently stopped matching its type
assertions and dropped properties from the next write. New leaf package
modelsdk/bsoncompat (UnmarshalDocM, UnmarshalDocMap, DMap) restores v1's
shapes at 73 call sites; a bson.D destination needs no helper. Three
guards keep v1 from returning, since re-adding it is not a build error.

Also ports mdl/exprcheck/roundtrip_test.go, dead behind a build tag no
CI job ran, to mdl/executor under the integration tag — which unblocked
`go mod tidy` for the whole repo.

Validated beyond CI: tested against the Formula1 solution (two Mendix
11.14.0 apps, 1,060 units, 41 MDL scripts) with base as a control, and
on five further test projects. No regression; writes byte-identical to
main wherever bytes are deterministic, and every unit keeps the property
names Studio Pro writes.
main is red. The merge of #1497, which removed
go.mongodb.org/mongo-driver v1 from go.mod, landed just after #1498
added mdl/backend/pagemutator/container_rendermode_test.go — a file
written against v1. The package now fails to compile at all:

  container_rendermode_test.go:8:2: no required module provides package
  go.mongodb.org/mongo-driver/bson
  FAIL github.com/mendixlabs/mxcli/mdl/backend/pagemutator [setup failed]

and TestNoV1DriverImports names the file, which is what it is for.

Neither PR is at fault. #1498 was authored and tested against a main
that still required v1, the two touched different files so nothing
conflicted, and each was green on its own base. The combination is what
breaks.

The file uses bson.D only, so this is the import path and nothing else:
v2 nests bson.D inside bson.D exactly as v1 nested primitive.D, so its
assertions are already correct and it needs no modelsdk/bsoncompat
helper.

Checked the whole tree rather than just the file CI named, since `go
test` stops at the first [setup failed] package and a second offender
would only surface on the next run: this is the only one.

Verified: go build ./... clean, go vet clean, gofmt clean, 106 test
packages pass with 0 failures, the two packages that failed in CI
(mdl/backend/pagemutator and modelsdk/bsoncompat) both pass, and `go mod
tidy` exits 0 leaving go.mod and go.sum unchanged — so v1 stays out.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Rer1mzP69XxbXKxQqheipM
fix(test): point container_rendermode_test at the v2 BSON driver

Unbreaks main. #1497 removed go.mongodb.org/mongo-driver v1 from go.mod
just after #1498 added a test file written against it, so
mdl/backend/pagemutator stopped compiling and TestNoV1DriverImports
named the file. Neither PR is at fault: each was green on its own base
and the two touched different files, so nothing conflicted — only the
combination breaks.

The file uses bson.D only, so this is the import path alone; v2 nests
bson.D inside bson.D exactly as v1 nested primitive.D, needing no
modelsdk/bsoncompat helper. Whole tree checked, not just the file CI
named: this is the only one.
@ako
ako merged commit 3b24e5a into mendixlabs:main Oct 20, 2026
13 checks passed
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.

2 participants