Skip to content

Sync ako/mxcli: codec writes Integers as int64 like Studio Pro (#675), template brace and regex checks (#1515), SEC011 widget names - #1718

Merged
ako merged 10 commits into
mendixlabs:mainfrom
ako:main
Nov 2, 2026
Merged

ako merged 10 commits into
mendixlabs:mainfrom
ako:main

Conversation

@ako

@ako ako commented Nov 2, 2026

Copy link
Copy Markdown
Collaborator

Syncs ako/mxcli:main into upstream: 4 commits from fork PRs ako#1884, ako#1885 and ako#1892.

Fixes #675. Refs #1515.

The codec writes Integer properties as int64, as Studio Pro does (#675)

The problem: every Integer property the codec wrote came out as BSON int32, where Studio Pro stores int64. That covered:

  • canvas and popup dimensions;
  • flow connection indices;
  • TabIndex;
  • layout-grid weights;
  • DecimalPrecision;
  • glyph codes;
  • mapping MinOccurs;
  • workflow thresholds.

On one page, CREATE (int32) and ALTER's page-level SET (int64) disagreed. mx check passes either way. But a title-only edit of a Studio Pro page narrowed all 154 of its integers to int32, so every real edit came with a whole-document width diff.

The fix: Primitive.BSONValue widens int32 to int64. The Go-facing type stays int32, and reads already accepted every width.

Measured, not assumed:

  • A census of 15,354 Studio Pro-authored units, from Mendix 10.6.4 to 11.13.0, holds 208,038 integer values. Every one is int64, and none is int32.
  • So the change needs no exception list.

check: expressions the build or runtime reject (part of #1515)

  • Template brace grammar, MDL-TEMPLATE02. log info node 'Mod' 'Item {number} not found'; and a REST Body: template '{}' passed check, then failed mx check with CE0712. Four of mxcli's own docs taught a failing JSON body.
    • The grammar was measured with mx check -j on 60 inputs in up to 12 slots, on 10.24, 11.13 and 11.15. The results were identical across versions, covering CE0712, CE0713, CE0714, CE0716, CE0717 and CE0720.
    • The new mdl/texttemplate package implements that grammar. It reports mxbuild's own code and wording, and check and the flow builders share it.
    • It replaces the placeholder regexps in MDL-WF21 and MDL-WIDGET04. Those never saw a malformed brace, and they wrongly counted the escaped {{1}}.
    • It now covers the microflow templates too: log, show message, validation feedback, send email, REST URL and body, and Java action text templates.
  • New warning MDL-REGEX01: a replaceAll, replaceFirst or isMatch pattern ending in a lone backslash. It builds with 0 errors, then fails on every call with PatternSyntaxException.
    • This was measured against the runtime's own StringFunctions from the 10.24, 11.13 and 11.15 jars.
    • Only the pattern is a regex. The replacement is literal text on both the server and the client, so there is no warning on it.
  • Already refused on main: item 1 of check passes expressions the build rejects or that misbehave at runtime (function arguments, Decimal toString, raw backslash literals) #1515, formatDecimal's argument kind.
  • Documented, not checked: item 2, toString(Decimal) into a bounded String attribute, because nothing enforces attribute lengths at check time.

Lint

  • SEC011 names the widget its XPath row records. It used to guess "the only data widget over the entity on the page", so a page with two grids over one entity was reported only as "A data source on page …"

ako and others added 10 commits November 1, 2026 21:48
…int64 (#1264)

A page created through the codec stored CanvasWidth/CanvasHeight and
PopupWidth/PopupHeight as BSON int32, where every Studio Pro page stores
int64, and on one page CREATE (codec, int32) and ALTER's page-level SET
(page mutator, int64) disagreed (mendixlabs#675). mx check reports
0 errors either way. It was not only pages: every Integer property the
codec writes came out int32 - flow connection indices, TabIndex,
layout-grid weights, DecimalPrecision, GlyphIcon codes, mapping
MinOccurs/LevelOfParent, workflow thresholds. Under the old build, a
title-only edit of a Studio Pro page (describe, edit, exec) narrowed all
154 of its integers to int32, so every real edit of a Studio Pro
document came with a whole-document width diff.

Cause: modelsdk/gen binds the metamodel's Integer type as
property.Primitive[int32], and Primitive.BSONValue returned the Go value
unchanged. The backend's addInt32 helper is a Primitive[int32] too. The
read side (DecodeInt32) already accepted every width, so reads never
failed.

Fix: Primitive.BSONValue widens int32 to int64. The Go-facing type stays
int32.

Measured, not assumed. A prior finding ("keep a stored int's width")
suggested that Studio Pro might store some Integers as int32. The census
covered 15,354 Studio Pro-authored units: a pristine `mx create-project`
11.13.0 app and the .mpr inside eight app packages, Mendix 10.6.4 to
11.13.0. With typed-array markers excluded they hold 208,038 integer
values, every one int64, and 0 int32. The only other numbers are 154
doubles, all NewSortIndex, which gen binds float64. 137 of the 331 gen
Integer bindings occur in that set, and each is int64-only, so the
change needs no exception list. The int32 that "keep the width" guards
exists only in documents an older mxcli wrote. A second source agrees:
canon's measured studiopro_property_defaults.json, where `mx convert`
fills in absent Integers as $numberLong.

Readers swept for `.(int32)`, `Int32OK()`, `.Int32()`, `!= int32(` and
int32-only type switches. Array-marker probes were set aside.
- mapping_read.go customHandlerFromRaw read LevelOfParent with Int32OK
  only. That was already wrong for every Studio Pro document: an
  ancestor binding read back as source "parent", level -1. It would
  also have broken mxcli's own documents once the codec wrote int64.
  It now uses rawInteger.
- codec.BSONDocument.GetInt32 accepts int32/int64/double. It has no
  callers, but it reads codec documents.
- The pagemutator MaxFileSize setter now defaults to int64. It keeps
  int32 only where int32 is already stored, as DecimalPrecision and
  NumberOfLines already did. The DecimalPrecision comment is no longer
  stale.
Every other reader of a numeric value was already width-agnostic.

canon (ADR-0008) is width-blind: renderScalar formats with %v. So an
int32-vs-int64 difference never counted as a change, and this fix moves
nothing between Replaced and Unchanged. Measured: describe -> exec of
the bug-test page reports "Unchanged page" with identical bytes, both on
an app the fixed build wrote and on one the old build wrote. On the
second app the int32 stays on disk. There is no churn and no migration.

Tests:
- property.TestPrimitiveInt32EncodesAsBSONInt64
- codec.TestEncoder_EveryIntegerPropertyIsInt64. It walks
  DefaultRegistry (the new TypeRegistry.TypeNames) and sets every
  Primitive[int32]: 331 properties on 173 types, with a floor of 300
  against a vacuous pass.
- codec.TestEncoder_RewriteKeepsStudioProIntegerWidth
- modelsdkbackend.TestCreatePage_IntegerHeaderMatchesStudioProWidth.
  The reference width is read off the fixture's own pages, which
  mx create-project wrote.
- modelsdkbackend.TestPopupDimensions_CreateAndAlterAgreeOnWidth
- modelsdkbackend.TestCustomHandlerFromRaw_ReadsLevelOfParentAtEitherWidth
- modelsdkbackend.TestCustomHandler_AncestorLevelRoundTrips
Six existing tests pinned int32 on the encoded side. They now assert
Studio Pro's width: GlyphIcon Code (570/570 int64), FileManager
MaxFileSize (26/26), DecimalPrecision (4,856/4,856), and annotation-flow
sides (now width-agnostic). The pagemutator file-manager fixture claimed
to be Studio Pro's shape but used int32. It now uses int64, with int32
kept as the older-mxcli case.

Controls:
- BSONValue reverted: TestPrimitiveInt32EncodesAsBSONInt64 fails with
  "BSONValue() = int32(1200), want int64", and the registry walk fails
  on all 331 properties. TestCreatePage_IntegerHeaderMatchesStudioProWidth
  fails with "created page stores CanvasWidth as BSON 32-bit integer;
  Studio Pro's pages store it as 64-bit integer", and the same for the
  other three keys. TestPopupDimensions_CreateAndAlterAgreeOnWidth fails
  with "CREATE wrote PopupHeight as 32-bit integer, ALTER wrote
  PopupWidth as 64-bit integer".
- Encoder fix kept, reader fix reverted: TestCustomHandler_AncestorLevelRoundTrips
  fails with "round trip gave {Source: "parent", LevelOfParent: -1},
  want {ancestor, 2}". The widening does break that reader without its
  fix.

End to end: two pristine 11.13.0 apps, one run with the fixed build and
one with BSONValue reverted. Each ran the bug-test MDL and eight doctype
scripts; 353 units were written, and the 24-workflow script was refused
for unresolved references on both builds. Census of each app afterwards:
fixed has 7,664 int64 and 0 int32; fault-forced has 2,970 int32 values
on 67 (type, key). `mxcli bson dump --format bson` of BugTest1264.P_Popup
shows type 0x12 (int64) for all four header keys on the fixed build and
0x10 (int32) on the other. `mxcli docker check` reports "The app
contains: 0 errors." on both apps, so mx check does not distinguish the
two builds.

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

Mirror of mendixlabs#1515. `log info node 'Mod' 'Item {number} not
found';` and a REST POST's `Body: template '{}'` printed "Check passed!",
and mx check then reported [CE0712] "Brace should be followed by a number
of digits or another brace." Four of mxcli's own docs taught the failing
JSON body `template '{"name": "{1}"}'`.

Cause: no check modelled the template grammar. Workflow texts (MDL-WF21)
and dynamic text / button captions (MDL-WIDGET04) counted placeholders
with a `\{(\d+)\}` regexp. That regexp never sees a malformed brace, and
it counts the escaped `{{1}}` as an unfilled {1}, refusing a text that
builds. The microflow templates (log, show message, validation feedback,
send email, REST URL and body, Java action text template) had no check.

Measured with mx check -j, one document per text: 60 inputs in up to 12
slots on 10.24.24, 11.13.0 and 11.15.0. Every slot gave the same code for
every input, and the versions agreed on every shared cell.
- CE0712: `{` followed by anything but a digit or `{`.
- CE0713: digits run to the end of the text.
- CE0714: digits followed by anything but `}`.
- CE0716: `{0}`.
- CE0717: an index past int32.
- CE0720: an index greater than the parameter count.
- Accepted: `{{`, a lone `}` or `}}`, a `{` that ends the text, `{01}`.
mxbuild reports one error per text, the first in reading order. CE0720
counts only for a text with no other error.

Fix:
- mdl/texttemplate implements that grammar.
- MDL-TEMPLATE02 reports it with mxbuild's code and wording. Check and
  the flow builders call the same function (templateTextRefusal), so exec
  refuses it too, --no-check included, in microflows, nanoflows, rules and
  alter fragments.
- A Java action argument is judged at check time when it states
  `with (…)`. Exec judges every StringTemplate parameter.
- Workflow texts and dynamic text / button captions use the same scanner,
  which ends the `{{1}}` false refusal.
- The docs now show `'{{"name": "{1}"}'`.
- Not covered: pluggable textTemplates. mxcli rewrites their named
  `{Attr}` placeholders first.

Verification:
- Unit tests pin every measured row and check that check and exec agree.
- With the check stubbed out, the reported rows fail with
  `check: MDL-TEMPLATE02 = "", want one naming "CE0712"` and
  `exec: refused = false`.
- With the old regexp count restored, `{{1}}` is refused again by
  MDL-WIDGET04 and MDL-WF21 CE0720.
- On 11.13.0, the .fail.mdl written by a binary with the check stubbed
  (exec --no-check) gives exactly its 10 annotated errors. The controls
  build at 0 errors.
- No mdl-examples script (1127) gets a new diagnostic.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… fails at runtime (#1397)

Mirror of mendixlabs#1515, item 3: "a lone \ in replaceAll was
stored as an unterminated or wrong regex and failed at runtime ('Failed
to evaluate expression'); a JSON escape needs the 4-backslash and
8-backslash forms because replaceAll is a regex replace". The issue asked
for a warning on a replacement whose backslash escapes nothing.

Measured, not assumed. A small Java program called the Mendix runtime's
own StringFunctions from the 10.24.24, 11.13.0 and 11.15.0 runtime jars:
- replaceAll and replaceFirst run text.replaceAll(regex,
  Matcher.quoteReplacement(replacement)). The 11.13 nanoflow client
  escapes `$` and calls String.replace. So the REPLACEMENT is literal text
  on both sides: '\"' inserts a backslash and a quote, and a lone '\' or
  '$1' is inserted as written. A warning there would only flag working
  code, so there is none.
- Only the PATTERN is a regex. A pattern ending in a lone backslash builds
  (mx check 11.13.0: 0 errors) and fails on every call: PatternSyntaxException
  "Unescaped trailing backslash" on the server, "\ at end of pattern" in a
  nanoflow. isMatch fails the same way.
- What is stored (mxcli bson dump): under `mdl 1;` a literal is stored as
  written, and without the header `\\` becomes one backslash. The 4- and
  8-backslash forms are mdl 0's escaping of the correct 2 and 4.
- millisecondsBetween … weeksBetween return the absolute difference,
  server and client (`.sub().abs().div()` in mxui.js).

Fix:
- MDL-REGEX01 is a warning, because the build accepts it. It fires on a
  replaceAll, replaceFirst or isMatch whose pattern literal ends in an odd
  number of backslashes.
- It judges the stored text, so it is right under both headers, and it
  runs from the per-expression hook that microflows and nanoflows share.
- A new `mxcli syntax microflow expressions` topic covers string literals
  and backslashes, the regex functions, the absolute *Between functions
  and template braces. Pitfall §20 and error-messages.md cover the rule.

Not done: a warning for toString(Decimal) into a bounded String
attribute. Attribute values are ResolveBy:AttributeOf slots that nothing
enforces, and no tier knows a String attribute's length. The expressions
topic says to round first instead.

Verification: with the check stubbed out, every warning row of
TestTrailingBackslashRegexes and TestRegexTrailingBackslash fails
(`MDL-REGEX01 reported = false, want true`). On 11.13.0 the bug-test
script builds at 0 errors, and check reports exactly its one MDL-REGEX01.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ec-int64-integers

# Conflicts:
#	CHANGELOG.md
…ression-check-gaps

# Conflicts:
#	CHANGELOG.md
SEC011 guessed the widget from "the only data widget over the entity on
the page", so a page with two grids over one entity was reported as "A
data source on page …". The catalog's XPath rows now carry WidgetName
(#1879); the rule uses it and keeps the guess for a row without
one.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
fix: codec writes Integer properties as BSON int32, not Studio Pro's int64 (#1264)
…ression-check-gaps

# Conflicts:
#	CHANGELOG.md
fix: check catches template braces and a trailing-backslash regex the build or runtime rejects (#1397)
feat(lint): SEC011 names the widget the XPath row records
@ako
ako merged commit 3b6fe8b into mendixlabs:main Nov 2, 2026
15 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.

modelsdk codec writes Integer fields (canvas/popup dimensions) as BSON int32, not int64

1 participant