Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
83a94aa
fix(visitor): show features for version x.y reaches the feature table…
ako Oct 1, 2026
f4e5a99
fix(alter-page): set Editable writes a list view's boolean instead of…
ako Oct 1, 2026
93a2bb0
feat(cli): mxcli version subcommand (#534)
ako Oct 1, 2026
c7f89f5
fix(domainmodel): UpdateAttribute carries the stored attribute GUID (…
ako Oct 1, 2026
d25d79a
fix(cli): read MDL scripts with a UTF-8 BOM or in UTF-16 (mendixlabs/…
ako Oct 1, 2026
e494590
fix(alter-page): a data view footer is addressed as <dataview>.footer…
ako Oct 1, 2026
76ece77
fix(cli): -c "" is an error, and a failing -c statement says what was…
ako Oct 1, 2026
2fcf790
fix(describe): enumeration captions and folder escape apostrophes (me…
ako Oct 1, 2026
dcb324b
fix(lsp): a non-file document no longer panics the server (mendixlabs…
ako Oct 1, 2026
9e6d3fe
fix(check): check -p predicts exec's refusals by calling exec's decis…
ako Oct 1, 2026
4bca1f0
fix(move): MOVE ENTITY handles existing cross-associations (#628)
ako Oct 1, 2026
63c8f87
refactor(exec): the pre-flight checks are one function that returns t…
ako Oct 1, 2026
4c1e5c3
fix(diff): diff runs exec on a scratch copy and reports the units it …
ako Oct 1, 2026
86d9878
docs(findings): diff predicted exec with a second renderer (#907)
ako Oct 1, 2026
d20c47a
fix(mpr): refuse file writes while Studio Pro has the project open (m…
ako Oct 1, 2026
2ed9b86
fix(published-rest): write an operation's query and body parameters, …
ako Oct 1, 2026
b7a48cc
fix(diff): a script run on the scratch copy cannot reach past it (#907)
ako Oct 1, 2026
3af12c4
fix(pages): an input binds to a page variable, and describe prints th…
ako Oct 1, 2026
30daa44
fix(alter-page): REPLACE of a pluggable widget keeps the properties t…
ako Oct 1, 2026
5cbc1eb
fix(flows): refuse or write correctly six silent wrong writes in flows
ako Oct 1, 2026
1655160
fix(alter-page): REPLACE of a DataGrid 2 keeps its unstated propertie…
ako Oct 1, 2026
178e740
fix(check): MDL-WIDGET34 suggests the mdl 1 header map spelling
ako Oct 1, 2026
cedabc3
fix(xpath): a double-quoted value in an XPath is refused; a double-qu…
ako Oct 1, 2026
d11db90
Merge branch 'c19-911' into c19-912
ako Oct 1, 2026
d0a4c8c
Merge branch 'c19-912' into c19-915
ako Oct 1, 2026
696170f
Merge branch 'c19-915' into c19-916
ako Oct 1, 2026
28c1dff
Merge branch 'c19-916' into c19-918
ako Oct 1, 2026
0a73ace
Merge branch 'c19-918' into c19-917
ako Oct 1, 2026
6760d7c
Merge branch 'c19-917' into c19-914
ako Oct 1, 2026
b9e30d9
Merge branch 'c19-914' into c19-913
ako Oct 1, 2026
591ac71
Merge branch 'c19-913' into c19-919
ako Oct 1, 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
32 changes: 20 additions & 12 deletions .claude/commands/mendix/diff-script.md
Original file line number Diff line number Diff line change
Expand Up @@ -72,25 +72,31 @@ Entity: MyModule.Order

## What Gets Compared

- **Entities**: Attributes, constraints, indexes, documentation
- **Enumerations**: Values and captions
- **Associations**: Type, owner, delete behavior
- **Microflows**: Parameters, return type, body statements
`mxcli diff` runs the script with exec's own code on a scratch copy of the
project, then compares the copy with the project unit by unit. It reports
exactly the units exec would write — every document kind (domain model per
entity and association, pages, snippets, layouts, flows, security, navigation,
settings, folders) and every statement kind — each shown as its `describe`
before and after. The project itself is not changed.

A unit exec would rewrite although its description does not change is listed
on one line with the properties that change (`Modified: Page X: changed:
CanvasHeight`), or the folder it moves to.

## Summary Output

Every diff ends with a summary:

```
Summary: 2 new, 3 modified, 5 unchanged
Summary: 2 new, 3 modified, 0 removed — exec would write 4 unit(s)
```

When a `create or modify` of an existing microflow or nanoflow would be refused
by `exec` (under `mdl 1;`, a change the splice cannot make), diff lists it as
`Refused: …` with the reason and adds `, N refused` to the summary.
One `exec` would write although its MDL renders as stored (a `folder` clause
naming another folder, applied as a move) is counted as modified and listed as
`Modified: … exec would write it: …`.
A script exec has already applied reports `exec would write nothing`.

A script exec would refuse (its pre-flight checks) or a statement exec would
stop at (a plain `create` of an existing document; a flow change the splice
cannot make under `mdl 1;`) is reported as `Refused: …` with exec's message.
`--no-check` and `--continue-on-error` behave as they do for exec.

## Use Cases

Expand All @@ -104,4 +110,6 @@ naming another folder, applied as a move) is counted as modified and listed as
- Use `--color` for terminal output to easily spot changes
- Use `--format struct` for a quick overview of what changes
- Use `--format side` when comparing large objects
- Combine with `mxcli check` to validate syntax first
- Run it before `exec` on a Studio Pro-authored project: a write you did not
intend (a rewrite of a document you only meant to read back) shows up as a
modified unit
4 changes: 4 additions & 0 deletions .claude/skills/fix-issue/findings/cmd-mxcli.jsonl
Original file line number Diff line number Diff line change
Expand Up @@ -135,3 +135,7 @@
{"date": "2026-10-01", "area": "cmd/mxcli", "symptom": "The LSP's CREATE MICROFLOW / CREATE NANOFLOW / CREATE ENUMERATION snippet completions inserted MDL that does not parse under any language version (`missing '(' at 'BEGIN'`, a quoted enumeration value name), the CONSTANT snippet the deprecated clause form (MDL-DEPR136), and PAGE/SNIPPET a statement mdl 1 refuses for its missing `;`; completion also offered alias-only keywords (SHOW_PAGE, DELETE_BEHAVIOR, DEFINE) and the `show entities` listings after SHOW.", "cause": "The snippets and keyword lists were hand-written once and never parsed; the grammar moved on (R2 property lists, R8 words, `list` for `show`) and nothing tied the completion text to it. The keyword list is generated from the lexer alone, which cannot tell an alias token from a canonical one: that is said by the `/* @alias MDL-DEPRnnn */` markers in the parser grammar.", "fix": "Snippets rewritten to canonical mdl 1. cmd/gen-completions reads the parser grammar too and drops a token every parser-rule use of which carries an @alias marker (the `keyword` rule, which lists tokens usable as names, does not count). `list` gets the listings; `show` offers page/message/home page.", "insight": "A completion text is MDL that ships in the binary, so it is held to what docs are held to: TestCompletionSnippetsAreMdl1 parses every snippet, expanded with its defaults, under `mdl 1;` and requires no deprecation. Deciding alias-only from the markers is the registry's own data; a token-swap rewrite word (`snippet`, `column`, `comment`) is NOT alias-only \u2014 those words stay canonical elsewhere \u2014 and neither is SHOW (show page, show message).", "issue": "ako/mxcli#714 (decision 5)", "file": "cmd/mxcli/lsp_completion.go, cmd/gen-completions/aliases.go", "test": "cmd/mxcli/lsp_mdl1_test.go TestCompletionSnippetsAreMdl1, TestCompletionOffersNoDeprecatedSpelling, TestCompletionListAndShowContinueIntoMdl1; cmd/gen-completions/aliases_test.go"}
{"date": "2026-10-01", "area": "cmd/mxcli", "symptom": "A stub-then-real script set (two files each with `create or modify microflow X`) upgraded with `fmt --upgrade -p` one file at a time ended up split: the stub's file declined the header (MDL-V1-REBUILD) and the real flow's file took it. Run in order, the mdl 0 stub rebuilt the stored real flow and the mdl 1 real statement was refused, every run; mx check 0 errors, the app running the placeholder.", "cause": "Each command judged one file against the model as stored; check/fmt took exactly one file, so nothing could see a flow declared twice across the set, and a per-file header decision is wrong for a pair whose files must agree.", "fix": "check and fmt accept several files as one script set (cmd/mxcli/script_set.go): findFlowRedeclarations finds a flow declared by two create-or-modify statements; check warns MDL-STUB01 naming both; fmt --upgrade groups files sharing such a flow and declines the header for all of them when any one cannot take it (canTakeHeader = the per-file verdict). One file alone is unchanged.", "insight": "A per-file verdict is only right when the files are independent; the run is the unit when two files write the same document. The symptom hides because each half is individually correct (decline is right for the stub, header is right for the real file) - only the combination is wrong, so test the set, with the single-file decision as the control.", "issue": "ako/mxcli#905", "file": "cmd/mxcli/script_set.go", "test": "cmd/mxcli/script_set_test.go TestFmtUpgrade_ScriptSetDecidesStubThenRealHeaderTogether, TestCheck_ScriptSetWarnsOnStubThenReal"}
{"date": "2026-10-01", "area": "cmd/mxcli", "symptom": "fmt --upgrade over a stub-then-real set still split the pair when the stub's file carried a written `mdl 0;`: the real file took `mdl 1;`. With the real file already `mdl 1;`, fmt printed 'no language header added' about it while the pair stayed split.", "cause": "canTakeHeader returned true for ANY written header (langver.ScanWrittenHeader's bool), reading a pinned `mdl 0;` as 'already has the header'; and the group decline was applied to a file that already carries the header, which fmt never removes.", "fix": "canTakeHeader: a written header can take it only when it is langver.Latest. decideSetHeaders: a file already under the header is not declined; fmt says the pair stays under different headers and to drop the stub or take the header off.", "insight": "ScanWrittenHeader's bool means 'a header is written', not 'the header is mdl 1'; a written mdl 0 pin is the strongest 'cannot take it' there is. Test the group decision with every header state of each file, not only headerless ones.", "issue": "ako/mxcli#905", "file": "cmd/mxcli/cmd_fmt.go", "test": "cmd/mxcli/script_set_test.go TestFmtUpgrade_ScriptSetPinnedMdl0StubHoldsTheRealFileBack, TestFmtUpgrade_ScriptSetAlreadySplitPairIsReportedAsSplit"}
{"date": "2026-10-01", "area": "cmd/mxcli", "symptom": "`mxcli version` fails with `unknown command \"version\" for \"mxcli\"` while `mxcli --version` works", "cause": "cobra's `Version:` field registers only the --version flag; no subcommand existed, though shouldSuppressWarning already listed a `version` argument", "fix": "Add a `version` subcommand (cmd/mxcli/cmd_version.go) printing exactly cobra's --version text (name, version, build time)", "insight": "Code that special-cases an argument (warning suppression) is evidence of an intended command; a test pinning the subcommand output to the flag output keeps the two from drifting", "issue": "ako/mxcli#534", "file": "cmd/mxcli/cmd_version.go", "test": "cmd/mxcli/cmd_version_test.go"}
{"date": "2026-10-01", "area": "cmd/mxcli", "symptom": "`mxcli check`/`exec`/`fmt`/`diff` fail on a script saved by Windows PowerShell 5.1: a UTF-8 BOM gives `line 1:0 token recognition error at: '\\ufeff'` (an invisible character), UTF-16LE gives a token error on almost every character; the same through stdin and in .test.mdl files", "cause": "Every script reader passed the raw file bytes to the lexer, which reads UTF-8 without a BOM; there was no shared reader (fmt, diff, the multi-file check pass, the test runner and EXECUTE SCRIPT each called os.ReadFile on their own)", "fix": "New mdl/srctext.Decode (strip a leading UTF-8 BOM, decode UTF-16LE/BE by BOM); readMDLSource calls it and fmt, diff and parseScriptSet now read through readMDLSource; testrunner.ParseTestFile and EXECUTE SCRIPT call it directly", "insight": "A BOM also hides a `mdl 1;` header from langver.ScanWrittenHeader, so stripping it in the parser alone would have left the language version wrong: decode where the bytes are read, before anything inspects the text. Enumerate the readers (grep os.ReadFile / io.ReadAll(os.Stdin)), not just the one the report names", "issue": "mendixlabs/mxcli#1253", "file": "mdl/srctext/srctext.go; cmd/mxcli/mdlsource.go", "test": "mdl/srctext/srctext_test.go; cmd/mxcli/mdlsource_encoding_test.go"}
{"date": "2026-10-01", "area": "cmd/mxcli", "symptom": "`mxcli -p App.mpr -c \"\"` opens the interactive REPL (a generator spawning mxcli with an open stdin hangs at `mdl>`); `-c \"describe entity System.User; describe entity String; describe entity System.FileDocument\"` stops at statement 2 with `module name is required: objects must be created within a module` and the third statement is silently never run", "cause": "Root Run tested `commands != \"\"` to choose -c over the REPL, so an empty flag value was indistinguishable from no flag; the -c path used ExecuteProgram, which returns the first error without its position, and describe entity/association reached findModule(\"\"), whose message is written for the create path", "fix": "`cmd.Flags().Changed(\"command\")` selects the one-liner path; runCommandLine (cmd/mxcli/oneliner.go) refuses empty input, reports `statement N of M` and how many later statements were not run (via new Executor.ExecuteProgramReportingStop), and takes --continue-on-error like exec; execDescribe names an unqualified entity/association name", "insight": "A flag's zero value is not its absence: use Changed() whenever an empty value must mean something other than not given. Decided semantics: -c is fail-fast like exec (a later statement may depend on an earlier one), but a stop is never silent", "issue": "mendixlabs/mxcli#1218", "file": "cmd/mxcli/oneliner.go; cmd/mxcli/main.go; mdl/executor/executor.go; mdl/executor/executor_query.go", "test": "cmd/mxcli/oneliner_test.go; mdl/executor/describe_unqualified_name_test.go"}
{"date": "2026-10-01", "area": "cmd/mxcli", "symptom": "`mxcli lsp --stdio` exits rc=2 with `panic: only file URIs are supported, got mendix-mdl` on textDocument/didOpen of a `mendix-mdl:` virtual document (the VS Code extension's describe previews); VS Code restarts it, it crashes again, and after 5 crashes it stops restarting the server", "cause": "checkableDocument (diagnostics) and CodeAction called go.lsp.dev/uri URI.Filename(), which panics on any scheme but file, to decide whether the document is a .test.mdl", "fix": "documentPath(uri) returns Filename() only for file: URIs and the URI's path component otherwise; runSemanticCheck (which shells out `mxcli check <path>`) skips non-file documents; virtual documents are still diagnosed in memory", "insight": "Third-party helpers that panic on unexpected input are a crash path in a long-running server; every URI an LSP client sends is untrusted shape. A test with a non-file URI plus a file-URI control (same diagnostics) proves the virtual case is handled, not skipped", "issue": "mendixlabs/mxcli#1245", "file": "cmd/mxcli/lsp_helpers.go (documentPath, isFileURI); cmd/mxcli/lsp_diagnostics.go; cmd/mxcli/lsp_language.go", "test": "cmd/mxcli/lsp_virtual_uri_test.go"}
Loading
Loading