Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .claude/skills/fix-issue/findings/cmd-mxcli.jsonl
Original file line number Diff line number Diff line change
Expand Up @@ -152,3 +152,4 @@
{"date": "2026-10-03", "area": "cmd/mxcli/check", "symptom": "`mxcli check -p` prints MDL067 (bare commit now WITH events) for a create-or-modify flow already stored with events, which `exec -p` no longer prints", "cause": "cmd_check ran ValidateProgram without the DropSettledCommitNotes filter exec_preflight applies; the project was only connected later, for the reference tier", "fix": "check connects to the project before the semantic report when -p is given, applies DropSettledCommitNotes and StoredTaskClaimViolations, and reuses that connection for the reference tier", "insight": "Two gates over one rule set drift whenever a post-filter lives in only one of them; grep for every caller of ValidateProgram when adding a filter. Control: a flow stored without events still notes", "issue": "ako/mxcli#943", "file": "cmd/mxcli/cmd_check.go", "test": "cmd/mxcli/check_stored_semantics_test.go"}
{"date": "2026-10-03", "area": "cmd/mxcli/test", "symptom": "every `mxcli test` run prints 2x MDL-DEPR001 and 2x MDL-V1-SLASH about a script the user never wrote", "cause": "GenerateEndpointMDL emitted a headerless mdl 0 script with `create or replace` and `/` terminators; the test-flow generators had already moved to the version-aware writeScriptHeader/createFlow/writeFlowEnd", "fix": "GenerateEndpointMDL writes mdl 1 through the same helpers (header, create or modify, `;` only); endpoint script is independent of the suite's version", "insight": "A generated script is checked like a user's one; pin it with a test that parses it and asserts ValidateProgram returns nothing. Verified end to end with `mxcli test --local` on a fresh 11.13 app", "issue": "ako/mxcli#943", "file": "cmd/mxcli/testrunner/endpoint.go", "test": "cmd/mxcli/testrunner/endpoint_clean_test.go"}
{"date": "2026-10-03", "area": "cmd/mxcli/theme", "symptom": "`theme create acme --from design.css` with `--mxt-font: \"Inter\", system-ui, sans-serif` prints nothing about Inter; the theme ships no woff2 and no @font-face for it and renders in the fallback font wherever Inter is not installed", "cause": "planFonts only decided which VENDORED families to drop; a seeded family outside the vendored set was never looked at, so the silent outcome was the default", "fix": "unvendoredSeededFamilies takes the primary (first) family of each seeded font stack, skips generic families and var() and the families the base partial loads, and CreateResult.UnvendoredFonts carries them to cmd_theme.go, which prints a note per family naming mxcli-fonts/ and the partial", "insight": "Only the first family of a stack is the design's choice; flagging the fallbacks (Helvetica, Arial) would make the note noise. The controls are a vendored family (IBM Plex Mono) and a generic stack, which must stay silent", "issue": "ako/mxcli#944", "file": "cmd/mxcli/theme/create_seeded.go (unvendoredSeededFamilies, planFonts); cmd/mxcli/cmd_theme.go", "test": "cmd/mxcli/theme/create_seeded_test.go (TestCreate_NamesSeededFontsItDoesNotVendor)"}
{"area": "cmd/mxcli/docker", "date": "2026-10-03", "symptom": "`mxcli docker check` (a check) rewrote an MPRv1 project's .mpr permanently, and on v1 and v2 alike rewrote theme-cache/web/theme.compiled.css(.map) and created deployment/sass/main.scss \u2014 with or without --no-update-widgets", "cause": "update-widgets ran on the user's project, protected only by a snapshot/restore of the v2 storage (.mpr + mprcontents/), so v1 had no protection; and `mx check` itself compiles the theme into theme-cache/ and writes deployment/sass/, which nothing guarded", "file": "`cmd/mxcli/docker/check.go` (`Check`), `cmd/mxcli/docker/check_copy.go` (`copyProjectForCheck`)", "insight": "A snapshot of the files you expect a tool to touch protects only those files; measure with a whole-tree hash+mtime diff before/after, which is what showed that plain `mx check` writes too. The fix is to run both mx steps on a temporary copy (skipping deployment/, releases/, theme-cache/, .git, node_modules) and rewrite the copy's path back in mx output. Control: a CE0117 microflow is still reported on both formats with the tree unchanged. `docker build` keeps runUpdateWidgets because it is expected to write deployment/ \u2014 but it still rewrites a v1 .mpr", "refs": ["ako/mxcli#951", "ako/mxcli#568", "ako/mxcli#646"]}
1 change: 1 addition & 0 deletions .claude/skills/fix-issue/findings/mdl-other.jsonl
Original file line number Diff line number Diff line change
Expand Up @@ -90,3 +90,4 @@
{"area": "mdl/catalog", "date": "2026-10-03", "symptom": "`delete $Order` / `change $Order (…)` inside `loop $Order in $Orders` produce no refs row (no delete/change edge to the entity), while `delete $Orders` on the list does; same for the output of `retrieve $Cust from $Order/Mod.Assoc`. show references / impact under-report batch flows.", "cause": "buildVarEntityMap seeded only object/list parameters and create / database-retrieve outputs, from a flattened action list that had already lost the loop's IterableList; loop iterators and association-retrieve outputs never got an entity, so microflowVarActionRef could not resolve them.", "file": "`mdl/catalog/builder_references.go` (buildVarEntityMap, associationTarget, associationEnds)", "insight": "Walk the object collection, not the flattened actions: the iterator's type lives on the LoopedActivity. Objects is not flow order, so map to a fixpoint (first assignment wins, which also bounds it). An association retrieve's output is the OTHER end from the start variable's entity; when the start is neither end (a specialization) leave it unmapped rather than guess. Control in the test: the list delete that always resolved.", "refs": ["mendixlabs/mxcli#1266"]}
{"area": "mdl/linter", "date": "2026-10-03", "symptom": "lint CONV013 reports \"Java action call ... uses '' error handling instead of Custom\" on calls that have `on error { \u2026 }`, and CONV014 never fires on `on error continue` on an action", "cause": "Both rules read BaseActivity.ErrorHandlingType, which the model reader never fills: Mendix stores an action activity's error handling on the ACTION (Microflows$JavaActionCallAction.ErrorHandlingType). The '' in the message was the empty field", "file": "`mdl/linter/rules/conv_error_handling.go`; shared reader `sdk/microflows/error_handling.go` (`ObjectErrorHandlingType`)", "insight": "The unit tests had always set the activity field by hand, so they passed against a shape the reader never produces. Build test objects the way flowObjectFromGen does. Two private reflection helpers (executor DESCRIBE, MCP backend) already read the action correctly; the rules had a third, wrong copy. A '' interpolated into a diagnostic is the cheapest tell of a never-populated field", "refs": ["mendixlabs/mxcli#1202"], "rules": ["CONV013", "CONV014"]}
{"area": "mdl/catalog", "date": "2026-10-03", "symptom": "activities table / activities_for() has no rows for anything inside a loop (nested loops included) in a microflow, nanoflow or rule; a Starlark rule cannot find a retrieve, commit or delete in a loop", "cause": "buildMicroflows had three near-copy loops (microflow, nanoflow, rule) over ObjectCollection.Objects that never recursed into LoopedActivity.ObjectCollection, although the reader fills it; countDecisionPoints beside them did recurse", "file": "`mdl/catalog/builder_microflows.go` (`insertFlowActivities`, `countFlowActivities`)", "insight": "Three copies of one walk is how the gap stayed in all three flavours. One shared walker writes ParentLoopId/LoopDepth; activities_for() keeps its top-level default (filtering ParentLoopId = '') so bundled rules such as CONV010 keep their counts, and ActivityCount keeps its top-level meaning beside a new TotalActivityCount. Raw SQL over activities now sees loop-body rows", "refs": ["mendixlabs/mxcli#1266"]}
{"area": "mdl/catalog", "date": "2026-10-03", "symptom": "Parallel mxcli processes on one project (8x `lint` on a fresh copy) print `Warning: failed to save catalog cache: failed to create table catalog_meta: table catalog_meta already exists` or `database is locked (SQLITE_BUSY)`; a reader can open a half-written .mxcli/catalog.db", "cause": "buildCatalog removed the cache and SaveToFile wrote into the path in place: VACUUM INTO refuses a non-empty target another process had just created, and the manual-copy fallback then CREATE TABLEd into that same file. Opening a cache (NewFromFile) also always wrote (createTables + schema_version row), so concurrent openers contended for the write lock", "file": "`mdl/catalog/catalog.go` (`SaveToFile`, `NewFromFile`), `mdl/catalog/catalogdb_sqlite.go`, `mdl/executor/cmd_catalog.go` (buildCatalog save)", "insight": "Write to a temp file in the same directory and os.Rename it over the cache \u2014 but a rename alone moves the failure to readers: SQLite refuses a write on a file renamed out from under an open connection (SQLITE_READONLY_DBMOVED, 'attempt to write a readonly database', 1032). So opening a cache at the current schema version must not write at all; the busy_timeout in the DSN covers the remaining writes of an old-version cache. An in-process test with 8 goroutine writers + 4 reader loops reproduces all three errors deterministically, no subprocesses needed", "refs": ["ako/mxcli#951"]}
12 changes: 6 additions & 6 deletions .claude/skills/mendix/custom-widgets/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -303,15 +303,15 @@ MDL032).

**CE0463 "update this widget" is EXPECTED after generating charts.** mxcli writes
the WidgetType from an embedded 11.6 baseline; the installed Charts.mpk is a
different version, so Studio Pro/mxbuild flags drift. Clear it with **`mxcli docker
check`/`build`** (they normalize the widgets and preserve your storage format). The
whole `mdl-examples/doctype-tests/34-chart-widget-examples.mdl` builds **0 errors**
after normalization.
different version, so Studio Pro/mxbuild flags drift. Clear it with **`mxcli fix
widgets`** (keeps your storage format); `docker check` only normalizes a temp copy,
so check the stored project with `--no-update-widgets`. The whole
`mdl-examples/doctype-tests/34-chart-widget-examples.mdl` builds **0 errors** after.
**Do NOT run bare `mx update-widgets` on an MPRv2 project** (an `mprcontents/`-folder
project — what `mxcli new` creates): it converts the project to single-file v1 and
**deletes `mprcontents/`**, corrupting git, breaking a running `mxcli run --local`
loop, and sometimes making the project unopenable in Studio Pro. `mxcli docker
check`/`build` snapshot/restore the v2 files around the normalization; raw
loop, and sometimes making the project unopenable in Studio Pro. `mxcli fix widgets`
writes the result back as v2, `mxcli docker check` runs on a temporary copy; raw
`mx update-widgets` is only safe on a v1 project or a throwaway diagnostic copy.

**DESCRIBE round-trips** series/line/scalecolor object-lists (item names are
Expand Down
8 changes: 4 additions & 4 deletions .claude/skills/mendix/migrate-design-prototype/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -347,8 +347,8 @@ too — each `series`/`line` binds its own OQL-view datasource + X/Y attributes;
Pie/HeatMap bind at the widget level (`ValueAttribute:`, Pie needs `SeriesName:`).
See **[Custom & Pluggable Widgets → Charts](../custom-widgets/SKILL.md)** for the chart-type
→ id table, per-chart required-property gotchas (TimeSeries needs a datetime X,
Bubble needs a size attribute), and the **CE0463 → `mxcli docker check`/`build`** step
(these normalize widgets *and* preserve MPRv2 storage — never run bare
Bubble needs a size attribute), and the **CE0463 → `mxcli fix widgets`** step
(it normalizes the stored widgets *and* preserves MPRv2 storage — never run bare
`mx update-widgets` on a `mxcli new` project; it deletes `mprcontents/`).
`mdl-examples/doctype-tests/34-chart-widget-examples.mdl` is the full showcase.

Expand Down Expand Up @@ -659,8 +659,8 @@ the fast index so a design migration doesn't rediscover them.
(`Charts.mpk`: column/bar/line/area/pie)** now author via MDL — each `series` (an object-list
item inside the chart) binds a datasource plus X/Y attributes:
`series s1 (staticDataSource: database from Module.View, staticXAttribute: "X", staticYAttribute: "Y")`
(a per-series OQL view works too). `mxcli docker check`/`build` clear the
widget-version-drift CE0463 (they normalize the widgets and preserve MPRv2 storage —
(a per-series OQL view works too). `mxcli fix widgets` clears the
widget-version-drift CE0463 (it normalizes the stored widgets and preserves MPRv2 storage —
do not run bare `mx update-widgets`, which deletes `mprcontents/`). Still lighter when the design allows: a **CSS-background
SVG** container (or `HTMLElement`) for sparklines/trends — no datasource — and `ProgressCircle`
(`type: expression`, `expressionCurrentValue: '$currentObject/Rate'`, min `'0'` / max `'100'`,
Expand Down
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).

### Fixed

- **`docker check` no longer modifies the project** (ako/mxcli#951) — `mx update-widgets` and `mx check` now run on a temporary copy, for MPR v1 and v2 alike, and what mx prints names the project's own paths. Before, a check rewrote an MPR v1 project's `.mpr` permanently (only v2 was restored from a snapshot), and `mx check` itself rewrote `theme-cache/` and created `deployment/sass/` even with `--no-update-widgets`. The output now says that widget definitions were normalised on a copy, and that a CE0463 the stored project still has is therefore not reported: `--no-update-widgets` checks the project as stored, `mxcli fix widgets` applies the normalisation (ako/mxcli#568, #646). Build output, caches and VCS folders are not copied; the copy goes to `$TMPDIR` and is removed afterwards.
- **Parallel mxcli runs on one project no longer fail to save the catalog cache** (ako/mxcli#951) — eight parallel `lint` runs on a fresh copy printed `failed to create table catalog_meta: table catalog_meta already exists` or `database is locked`. The cache is now written to a temporary file next to it and renamed into place, so every run saves and a reader sees the old cache or the new one, never a half-written file; opening a current cache no longer writes to it.
- **`create or modify` of a flow matches `commit … with events`, a legacy `split type` spelling and an empty `else` against what is stored** (ako/mxcli#942). Describe prints a stored commit as a bare `commit`, the `when … then` split form, and no empty `else`. The statement diff compared the spelling, so these never matched their own activity. An unchanged re-run reported "Unchanged … (spliced: 1 replaced)". A change inside a loop body next to such a statement was not refused under `mdl 1`, and the loop was rebuilt with new element IDs. `without events` is still a change. **The loop-body refusal also holds when another statement changes next to the loop:** before, only a loop-body change on its own was refused. **`describe` no longer warns that the merge closing an `if` at the end of a loop body "joins no decision"** and would be deleted. The check counted a loop body's flows from the loop's own collection, which holds none.
- **Less noise from `check` and `test`** (ako/mxcli#943) — **MDL-WORKFLOW10** no longer warns when the task is claimed in a called microflow: a callee the script creates is read (nested calls too), and with `-p` a stored one; a call that passes the task to a microflow neither can find counts as a possible claim. A callee that does not claim the task it is passed still warns. **`mxcli test`** no longer prints MDL-DEPR001 / MDL-V1-SLASH warnings about the endpoint-registration script it generates itself: that script is `mdl 1`. **`check -p`** drops **MDL067** for a commit already stored the way the script writes it, as `exec` already did. **MDL-WIDGET15** skips a dynamictext with its own `class:` or `style:` (a laid-out label/value pair is not fused text), names the two widgets, and every widget-rule diagnostic now carries its page or snippet as its location instead of "(no module)".
- **A required caption with no text in the default language is foreseen** (ako/mxcli#944) — making de_DE the default left a stock app's `Administration.Account_Overview` `tabPage2` (en_US only) empty in de_DE, and mxbuild refused it with CE4899 "Empty caption. [German, Germany]" while `check -p`, `lint` and `exec` said nothing. Measured on 11.14, the tab page caption is the one caption kind the build requires (page titles, buttons, labels, group boxes, column headers, menu items, enumeration captions and messages build without it; page templates and building blocks are not checked). Now: **lint rule QUAL006** (error) lists every tab page caption without the default language; **`alter settings language (DefaultLanguageCode: …)`** prints how many there are and where, with the `alter page … { set (Caption: …) on … }` that fixes one; **`check -p` reports MDL-I18N01** for a script that changes the default — stored captions, and captions the script wrote before the change. And a page created **after** the change in the same script is now written in the new default: the authoring language was resolved once per session, so it was still written in the old one and failed the build too.
Expand Down
15 changes: 12 additions & 3 deletions cmd/mxcli/docker.go
Original file line number Diff line number Diff line change
Expand Up @@ -169,9 +169,18 @@ This catches project errors (broken references, missing attributes, etc.)
early, before the slower MxBuild step. The 'docker build' command runs
this automatically unless --skip-check is used.

By default, 'mx update-widgets' runs before 'mx check' to normalize
pluggable widget definitions and prevent false CE0463 errors. Use
--no-update-widgets to skip this step.
The check never modifies the project: both mx steps run on a temporary
copy (in $TMPDIR), because mx writes into the project it checks — it
compiles the theme into theme-cache/ and deployment/, and update-widgets
rewrites the model. Build output, caches and VCS folders (deployment/,
releases/, theme-cache/, .git/, node_modules/, ...) are not copied.

By default, 'mx update-widgets' runs before 'mx check', on that copy, to
normalize pluggable widget definitions and prevent false CE0463 errors.
A CE0463 that normalisation clears is then not reported, although it still
fails MxBuild and 'mxcli run --local' on the stored project. Use
--no-update-widgets to check the project as stored, and 'mxcli fix widgets'
to apply the normalisation to the project.

The mx binary is located from the same directory as mxbuild.

Expand Down
Loading
Loading