Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
cd0c0ba
fix(mpr): a v1 write transaction reads the stored unit through itself
claude Oct 29, 2026
9bdfee1
fix(move): a Java or JavaScript action's source moves with it across …
claude Oct 29, 2026
825654a
fix(mpr): keep Unit.ContentsHash in step on MPR v1 writes
claude Oct 29, 2026
b14984f
fix(odata): keep a client's MetadataUrl when another checkout re-runs…
claude Oct 29, 2026
586d376
test(mpr): run the v1 transactional hash test through the real pool
claude Oct 29, 2026
3ab3c6c
fix(testrunner): rename every variable a test defines on the legacy r…
claude Oct 29, 2026
95fd9c4
fix(exec): name every file a statement writes beside the model
claude Oct 29, 2026
b941f09
refactor(move): report an action-source move with the other files exe…
claude Oct 29, 2026
c235820
feat(canon): add type floors extracted from the Model SDK
claude Oct 29, 2026
8340dd4
fix(mpr): refuse an element whose type the project does not have yet
claude Oct 29, 2026
721e1b2
fix(executor): gate the workflow types older Mendix does not have
claude Oct 29, 2026
ae5b9b7
test(bug-tests): measure the 1774 type floor on 9.24, 10.24 and 11.13
claude Oct 29, 2026
1ac4ad8
Merge origin/main into claude/loving-clarke-qun7w5
claude Oct 29, 2026
d74b8e2
Merge pull request #1788 from ako/claude/loving-clarke-qun7w5
ako Oct 29, 2026
ccb3a82
fix(docker): check the stored project by default and label its CE0463
claude Oct 29, 2026
ab9ef62
docs(docker): say docker check checks the stored project and labels C…
claude Oct 29, 2026
0f7d87b
fix(canon): measure the property-set table on Mendix 10.24.0 and 10.2…
claude Oct 29, 2026
0d82814
fix(cmd): fix property-sets reports names the elements of one type di…
claude Oct 29, 2026
eee078a
docs(findings): record ako/mxcli#1505, a table floor read as an intro…
claude Oct 29, 2026
64eadc2
refactor(executor): type list-operation, aggregate and Java action re…
claude Oct 29, 2026
c52a49c
fix(alter): type stored list-operation, Java action and self-referenc…
claude Oct 29, 2026
9a8cf0b
Merge pull request #1792 from ako/claude/loving-clarke-qun7w5
ako Oct 29, 2026
3259bba
fix(catalog): read each unit file once in a source catalog build (#1789)
ako Oct 29, 2026
87a5010
Merge pull request #1798 from ako/fix/1789-source-build-quadratic-reads
ako Oct 29, 2026
ebcb836
fix(mpr): name units from the index so v2 commands open only what the…
ako Oct 29, 2026
e3f7f06
Merge pull request #1801 from ako/fix/1791-v2-header-index
ako Oct 30, 2026
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
15 changes: 15 additions & 0 deletions .claude/commands/mendix/validate-project.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,21 @@ Found 1 error(s) and 1 warning(s)
| CW0xxx | Warning | Warning - should be reviewed |
| CD0xxx | Deprecation | Deprecated feature usage |

## Reading CE0463

`mxcli docker check` checks the project as stored, and labels each CE0463 ("The
definition of this widget has changed") after re-checking a temporary copy with the
widgets resynced:

- `stale widget definition — mxcli fix widgets fixes it`: the widget is older than its
installed package. Run `mxcli fix widgets -p app.mpr` (never bare `mx update-widgets`
on an MPR v2 project: it deletes `mprcontents/`).
- `persists after update-widgets`: a resync does not clear it; something in the stored
widget is wrong. If mxcli wrote the widget, report it.

`--update-widgets` checks only the resynced copy and hides the stale ones, which still
fail MxBuild and `mxcli run --local`; do not use it to decide that the project builds.

## When to Use

- After making changes with mxcli to verify they're valid
Expand Down
4 changes: 2 additions & 2 deletions .claude/skills/debug-bson.md
Original file line number Diff line number Diff line change
Expand Up @@ -359,7 +359,7 @@ for t, props in crash_props.items():

**Investigation methodology used for v0.10 CE0463 fixes** — see [WIDGET_BSON_VERSION_COMPATIBILITY.md](../../docs/03-development/WIDGET_BSON_VERSION_COMPATIBILITY.md) for the full case study and version-resilience model.

**Quick workaround** (if you can't fix the root cause): normalize with `mxcli fix widgets -p app.mpr`, which runs the widget update on the project **and preserves MPRv2 storage**. Not `mxcli docker check`/`build`: they run the update on a **temporary copy**, so a CE0463 the normalization clears is not reported while the stored project still fails MxBuild — use `docker check --no-update-widgets` to see the project as stored (and [`diagnose-ce0463.md`](./diagnose-ce0463.md) before calling it widget drift). Do **not** run bare `mx update-widgets` on a v2 project you care about — it converts to single-file v1 and deletes `mprcontents/` (corrupts git, breaks `mxcli run --local`). Raw `mx update-widgets` is fine only on a throwaway diagnostic copy or a v1 project.
**Quick workaround** (if you can't fix the root cause): normalize with `mxcli fix widgets -p app.mpr`, which runs the widget update on the project **and preserves MPRv2 storage**. `mxcli docker check` does not fix anything: it checks the project as stored and labels each CE0463 the normalization would clear as a *stale widget definition* (it runs the update on a **temporary copy** to find out), and one it would not as *persists after update-widgets* — read [`diagnose-ce0463.md`](./diagnose-ce0463.md) before calling either widget drift. `docker build` and `docker check --update-widgets` check only the normalized copy, so they pass while the stored project still fails MxBuild. Do **not** run bare `mx update-widgets` on a v2 project you care about — it converts to single-file v1 and deletes `mprcontents/` (corrupts git, breaks `mxcli run --local`). Raw `mx update-widgets` is fine only on a throwaway diagnostic copy or a v1 project.

### CE0642: Property X Is Required

Expand Down Expand Up @@ -439,7 +439,7 @@ for _, pm := range a.ParameterMappings {

2. **Mode-dependent properties must be consistent**: When changing a mode-switching property (e.g., `showContentAs`), all dependent properties must be updated to match.

3. **Widget normalization is the safety net — via `mxcli fix widgets`**: it applies the update-widgets normalization to the project *and* preserves MPRv2 storage. `mxcli docker check`/`build` normalize only a temporary copy, which hides CE0463 without fixing the stored project. Bare `mx update-widgets` does the same normalization but rewrites a v2 project to v1 and deletes `mprcontents/` — only use it on a v1 project or a throwaway diagnostic copy.
3. **Widget normalization is the safety net — via `mxcli fix widgets`**: it applies the update-widgets normalization to the project *and* preserves MPRv2 storage. `mxcli docker build` (and `docker check --update-widgets`) normalize only a temporary copy, which hides CE0463 without fixing the stored project; a default `docker check` reports the stored project's CE0463 and labels the ones `fix widgets` clears. Bare `mx update-widgets` does the same normalization but rewrites a v2 project to v1 and deletes `mprcontents/` — only use it on a v1 project or a throwaway diagnostic copy.

4. **The mpk is the source of truth**: The XML schema defines property types/defaults, the editorConfig.js defines visibility rules. Together they specify the complete expected Object structure.

Expand Down
37 changes: 26 additions & 11 deletions .claude/skills/diagnose-ce0463.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,21 +9,35 @@ Related: [`debug-bson.md`](debug-bson.md) for the general BSON diff workflow;
[`WIDGET_BSON_VERSION_COMPATIBILITY.md`](../../docs/03-development/WIDGET_BSON_VERSION_COMPATIBILITY.md)
for what is version-fragile and the per-minor onboarding record.

## Before you measure anything: `docker check` clears CE0463 by default
## Before you measure anything: which `docker check` you are reading

`mxcli docker check` runs **`mx update-widgets` before `mx check`**, deliberately — it
normalizes pluggable widget definitions to suppress the Case A noise below. So the plain
command reports **0 errors** on a project that genuinely has a CE0463, and the repair
also *mutates the project*, leaving it clean for any later check too.
`mxcli docker check` checks the project **as stored** (ako/mxcli#568, #646), and when
it finds CE0463 it already runs control 2 below for you: it re-checks its temporary
copy after `mx update-widgets` and labels each CE0463 —

```
at Data grid 2 'dgOrders' (4×): stale widget definition — `mxcli fix widgets` fixes it
at Combo box 'cbStatus': persists after update-widgets
```

A *stale* one is Case A; one that *persists* is the candidate for Case B. The project
itself is never touched (everything runs on a copy). Two flags change what it shows:

```bash
mxcli docker check -p app.mpr --no-update-widgets # the only form that can see CE0463
mxcli docker check -p app.mpr # stored project + labels (default)
mxcli docker check -p app.mpr --no-update-widgets # stored project, no re-check, no labels
mxcli docker check -p app.mpr --update-widgets # ONLY the resynced copy: hides Case A
```

This is documented in `docker check --help` and is not a defect, but it is the first
thing that goes wrong in a CE0463 investigation: you check, you see zero errors, and you
conclude there is nothing to find. Measured on 11.13.0: a page mxcli wrote reported
`0 errors / Project check passed` with the default and **1 error, CE0463** with the flag.
`--update-widgets` was the default until #646, and it is the first thing that went wrong
in CE0463 investigations: it reports 0 errors on a project that genuinely has a CE0463
(measured on 11.13.0: a page mxcli wrote, `0 errors / Project check passed` that way and
**1 error, CE0463** as stored). Findings and bug reports written before #646 that say
"`docker check` passed" mean the resynced copy passed.

mx check's text output does not name the page, so same-named widgets on different pages
print the same line; the labels count them (`(4×)`, `(2 of 3)`). For page names, run
`mx check -j out.json` on a copy (10.24+; 9.24's mx has no `-j`).

**`mxcli check` is not an alternative.** It validates an MDL *script* — syntax,
references, creation order, expression types — and never invokes mxbuild, so it cannot
Expand All @@ -47,7 +61,8 @@ These look identical in `mx check` output and have unrelated causes.
2. **Does `mx update-widgets` clear it?** If yes, the BSON was structurally valid and
correct for the version it was written against → Case A. Genuine template bugs do
*not* clear this way (the Image stale default and the number-filter markerless
array both needed template fixes).
array both needed template fixes). A default `mxcli docker check` answers this per
CE0463 (*stale widget definition* vs *persists after update-widgets*); by hand:

```bash
cp -r proj proj-ref && mx update-widgets proj-ref/App.mpr # ALWAYS on a copy
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"area": "cmd/mxcli/docker", "date": "2026-10-29", "symptom": "`mxcli docker check` reports 0 errors and exits 0, then `mxcli run --local` refuses to start with CE0463 \"The definition of this widget has changed\" at the same widgets; `docker check --no-update-widgets` reports them. A gate that says yes about a model it did not examine", "cause": "`Check` ran `mx update-widgets` on its temporary copy before `mx check` by default, to suppress \"false CE0463\" — so it validated a widget-normalised model, never the stored one. The CE0463s it cleared were real: MxBuild and `run --local` build the project as stored. An earlier fix only added a closing note saying so; the verdict and exit status stayed the normalised copy's", "file": "`cmd/mxcli/docker/check.go` (`Check`, `recheckCE0463`), flags in `cmd/mxcli/docker.go` (`--update-widgets`, `--no-update-widgets`)", "insight": "Disclosing a normalisation in the output does not fix a gate: callers read the exit status, not the note. The fix moves diagnose-ce0463's second control into the gate: check the stored project (its verdict and exit status), and only when that found CE0463 run update-widgets on the same copy and re-check, labelling each \"stale widget definition — `mxcli fix widgets` fixes it\" or \"persists after update-widgets\" — no second check on a clean project. mx check's text output does not name the page and a blank 11.13 app has four `dataGrid2_1` and four `dataGrid21` on different pages, so the lines are compared as counts (`(4×)`, `(2 of 3)`); `-j` would give module/document, but 9.24's mx rejects it (\"Option 'j' is unknown\") before checking anything. A stale instance needs no second package version: add one property to the widget XML inside the project's own .mpk (Data grid 2 3.4.0 + a boolean → 11 CE0463 on a blank 11.13.0 app, all cleared by update-widgets). Measured: old default 0 errors / exit 0; new default 11 errors / exit 1, all 11 labelled stale; `--update-widgets` 0 / exit 0 (old behaviour kept as opt-in); after `mxcli fix widgets` (11 units, MPR v2 kept) the default reports 0. The project tree's hash was unchanged by every run. Tests that pinned update-widgets-before-check as the default now pass `UpdateWidgetsFirst`", "test": "`cmd/mxcli/docker/check_stored_test.go` (`TestCheck_ChecksTheStoredProjectByDefault`, `TestCheck_LabelsCE0463ThatPersistsAfterUpdateWidgets`, `TestCheck_NoCE0463NoSecondCheck`), `cmd/mxcli/docker/check_stored_integration_test.go` (`TestCheck_ReportsStaleWidgetCE0463OfTheStoredProject`, real mx), `cmd/mxcli/docker_help_test.go` (`TestDockerCheckHelp_StoredProjectIsTheDefault`)", "refs": ["ako/mxcli#568", "ako/mxcli#646"], "ce": ["CE0463"]}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"area": "cmd/mxcli", "date": "2026-10-29", "symptom": "On the after-startup runner (every Docker run, `--local --legacy-runner`) two tests in one file that define the same variable with `retrieve $x`, `declare $x`, a list operation or aggregate (`$x = head(…)`, `$x = count(…)`), `send rest request`, `execute database query` or a loop iterator fail injection with MDL063 (MDL052 for the iterator), CE0111 \"Duplicate variable name\" from mxbuild. Measured by sweeping every non-.fail test file through GenerateTestRunner + ValidateProgram: setup-annotation.test.mdl (`retrieve $Brands` x3) and 1103-test-file-checks-as-microflow-bodies.test.mdl (`retrieve $Request` + `$Request = head(…)`) failed; the endpoint runner was never affected", "cause": "The runner joins every test into one microflow and renames each test's variables with `_N`, but the rename set came from `assignPattern`, a regex matching `$x = call|create` only. Every other way to define a variable kept its name and was declared once per test", "file": "`cmd/mxcli/testrunner/generator.go` (`extractVariableNames` now parses the body and takes `executor.FlowDefinedVariables`, new in `mdl/executor/validate_microflow_ce_gaps.go`); `generator_endpoint.go` (`bodyParsePrefix`, shared with `attachOnError`)", "insight": "Ask the validator what a body defines instead of keeping a second list: `statementProducedVars` (reflective on `OutputVariable`, plus declare/create/create list/retrieve/loop iterator) walked with `ast.StatementBodies` is exactly what MDL063 counts, so the rename set and the gate cannot drift. The wrong turn the previous finding suggested is `visitor.FlowStatements`: it yields grammar-rule names and offsets, not variable names, so it would need a hand-kept list of producing rules, a regex by another name. Sweep on MDL052 as well as MDL063: a reused loop iterator is CE0111 but reported by MDL052, and a sweep filtered on MDL063 alone shows it green. Over-renaming costs nothing (a void call's output, a String contains/find the builder turns into Change Variable) while under-renaming is CE0111, so the rename set deliberately skips MDL063's type-dependent exemptions. `$x = …` without `set` is a Change Variable (setStatement is the last alternative), not a definition, and keeps the old \\b guard's intent for free. The sweep is now a test (TestLegacyRunnerRepositoryTestFilesDefineEachVariableOnce); control: the regex restored fails 15 shape cases and 3 repository files with MDL063/MDL052", "refs": ["ako/mxcli#1775", "ako/mxcli#1753"], "rules": ["MDL063", "MDL052"], "ce": ["CE0111"]}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"area":"mdl/executor","date":"2026-10-29","symptom":"ako/mxcli#1777 (follow-up to #1769): `alter microflow … { insert after $Related begin change $First (Status = 'Seen'); end; }` with $First = head $Lines in the stored flow was refused, nothing written: \"cannot qualify member \\\"Status\\\": the entity its variable holds is not known here\". The same for any member (find/filter by, sort, change) of a stored filter/sort/tail/union/intersect/subtract/range/head/find output, a Java action result (entity, list, list of a type parameter), or a retrieve over a self-reference. `create microflow` types all of them.","cause":"alterFlowContext.storedVariables, the scope a spliced fragment starts from, left ListOperationAction, JavaActionCallAction and a self-reference AssociationRetrieveSource 'Unknown'; associationRetrieveType returned \"\" for a self-reference because 'which end it starts from is not known'.","file":"mdl/executor/cmd_alter_flow.go (storedVariables, storedDependent, storedDependents.typeOf), mdl/executor/cmd_microflows_builder_actions.go (listOperationResult, listResult.typeFrom, listOperationInput, aggregateResultType, associationRetrieveType), mdl/executor/cmd_microflows_builder_calls.go (javaActionResultType, returnsTypeParameterList, firstListType, javaActionArgVariables)","fix":"The full build's typing was pulled out of addListOperationAction, addAggregateListAction and addCallJavaActionAction into functions over the stored shapes (the microflows.ListOperation, the AggregateFunction + ReduceReturnType, the JavaAction definition + argument variables), and storedVariables calls the same ones: a list operation's output and a Java action's result are storedDependents typed in the fixed point, so one reading a list another output holds is typed whatever the stored order, and a name two origins give different derivations stays untyped (a head and a find of one list are one origin). Aggregates get the primitive the full build declares. A self-reference is followed from its FROM end.","test":"mdl/executor/flow_splice_stored_outputs_test.go (TestStoredVariables_ListOperationJavaAndSelfReferenceAreTyped, TestSpliceFragment_MembersOfStoredOutputsAreQualified: 13 shapes, TestSpliceFragment_MembersOfConflictingOutputsAreRefused); flow_splice_assoc_retrieve_test.go $Parent now Mod.Order. Controls: storedVariables and associationRetrieveType reverted -> all 12 new shapes refused 'cannot qualify member' (the self reference set was already typed); conflict guard off -> $Dup typed Mod.Tag, $Mixed Mod.OrderLine, both written. Platform 11.13.0: 7 alters refused before, all 8 written after, mx check 0 errors, describe -> exec Unchanged.","insight":"The stored AssociationRetrieveSource has no direction field (only StartVariableName and the association), so 'which end a self-reference starts from' is not a missing fact to be guessed but a platform rule to be measured: on 11.13.0 `retrieve $P from $Order/M.Order_Parent; change $P (…)` builds and `count($P)` is CE0013 \"Input variable 'P' must be of type 'List'\" — Mendix follows it from the FROM end. Owner Both on a self-reference is CE0002, so that case does not exist. The full build types a self-reference by use (a list unless an object use follows), which disagrees with the platform for a list use; the fragment takes the platform's reading. To type stored outputs, refactor the builder's typing to take the stored action rather than the AST statement, then call it from both sides — the builder's switch on ast.ListOpX was the only copy and would have drifted.","ce":["CE0013","CE0002"],"refs":["ako/mxcli#1777","ako/mxcli#1769","ako/mxcli#885"]}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"area": "mdl/executor", "date": "2026-10-29", "symptom": "`mxcli exec` of `create java action Probe.JA_Hello () returns String as $$ return \"hi\"; $$;` printed only \"Created java action: Probe.JA_Hello\" while also writing javasource/probe/actions/JA_Hello.java; `git status` then showed the .java (and a JavaScript action's .js under javascriptsource/) untracked, so a workflow that commits the model (.mpr + mprcontents/) left the body behind and a clone got an action with no logic", "cause": "Nothing reported writes beside the model. The backend already knew each one — `WriteSourceIfChanged` returns whether the content changed and `noteFileWrite` counted it, but only as a number feeding the Unchanged decision; the path never left the backend, and each handler printed its model-element sentence on its own", "file": "`mdl/backend/source_files.go` (SourceFileChange, SourceFileReporter); `mdl/backend/modelsdk/backend.go` (noteSourceFile, TakeSourceFileChanges), `java_write.go`, `javascript_write.go`, `module_write.go` (removeModuleSourceDirs); `mdl/executor/source_file_report.go`, drained in `executor_dispatch.go` executeInner, summary in `flushTally`", "insight": "Record the change where the bytes move, not in the handler: only the writer knows the path and whether the content actually differed, so a handler-side `Wrote …` would print for an identical rewrite (the reports-that-outrun-the-write shape). One drain after every statement in executeInner then covers every writer — create/modify, rename (Moved a to b), drop (Removed), DROP MODULE's folder removal — with no per-handler print to forget. Writers found by grepping os.WriteFile/Rename/Remove/RemoveAll under mdl/ and sdk/: java_write.go, javascript_write.go, module_write.go; widget_defs.go also writes during exec (.mxcli/widgets/*.def.json, and widget docs on refresh catalog), deliberately not reported — gitignored, regenerable caches that `mxcli git commit` unstages anyway. `exec` has no JSON mode, so the reporter's third suggestion does not apply. Control: with the drain disabled, an 11.13.0 app shows the issue exactly (model elements only, both source files untracked). Not fixed here, measured on 11.13.0: `move java action MvA.JA_Mv to MvB` leaves the body at javasource/mva/actions/JA_Mv.java and `describe` of MvB.JA_Mv says the Java source is not available; and exec's rollback restores only .mpr/mprcontents, so a failed run's 'Nothing was written' is untrue of these files", "refs": ["ako/mxcli#1618", "mendixlabs/mxcli#1634"]}
Loading
Loading