Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
b5429cc
fix: an OQL comment in a view's select list is not a select column
claude Sep 24, 2026
3935608
fix: an MDL keyword is a legal OQL source alias after AS
claude Sep 24, 2026
b36982e
fix: refuse ADD/DROP ATTRIBUTE on a view entity (CE6770)
claude Sep 24, 2026
21ee397
docs: correct the #1174 finding's two side observations
claude Sep 24, 2026
fb0a2cf
Merge pull request #651 from ako/claude/mxcli-issues-1175-1174-49eezh
ako Sep 24, 2026
3a63fce
Merge remote-tracking branch 'origin/main' into claude/mxcli-issue-11…
claude Sep 24, 2026
5bba04c
Merge pull request #654 from ako/claude/mxcli-issue-1173-ar591l
ako Sep 24, 2026
0399f96
fix: describe → exec of a view entity no longer drifts OQL indentation
claude Sep 24, 2026
78a35a6
Accept IF EXISTS on every document-level DROP (#531)
claude Sep 24, 2026
473fd65
fix: bind `DataSource: Module.Entity` as a database source instead of…
claude Sep 24, 2026
a441585
Merge pull request #655 from ako/claude/mxcli-issue-653-nygi8c
ako Sep 24, 2026
2ca9336
Merge remote-tracking branch 'origin/main' into claude/mxcli-issue-57…
claude Sep 24, 2026
7a2fc0d
Merge pull request #656 from ako/claude/mxcli-issue-576-qesk9m
ako Sep 24, 2026
c4a9ba6
Merge pull request #657 from ako/claude/mxcli-issue-531-0e9w3s
ako Sep 24, 2026
82f0ab9
fix: expand `use fragment` inside ALTER PAGE insert/replace (#572)
claude Sep 24, 2026
661fbae
feat(layout): author Forms$SimpleMenuBar and menu-document menu sourc…
claude Sep 24, 2026
e88b2c6
fix(check): resolve menu-document references, and check layouts at al…
claude Sep 24, 2026
abfdb7b
Merge pull request #658 from ako/claude/mxcli-issue-573-rz4znd
ako Sep 24, 2026
930b4a0
Merge remote-tracking branch 'origin/main' into claude/mxcli-issue-57…
claude Sep 24, 2026
0feb3f7
Merge branch 'mendixlabs:main' into main
ako Sep 24, 2026
8bb722c
Merge remote-tracking branch 'origin/main' into claude/mxcli-issue-57…
claude Sep 24, 2026
ed45d72
Merge pull request #659 from ako/claude/mxcli-issue-572-coz962
ako Sep 24, 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
4 changes: 4 additions & 0 deletions .claude/skills/fix-issue/findings/mdl-executor.jsonl
Original file line number Diff line number Diff line change
Expand Up @@ -686,3 +686,7 @@
{"area": "mdl/executor", "date": "2026-09-23", "symptom": "`CALL MICROFLOW M.F(…) IN QUEUE M.Q` where `F` returns **Boolean**: `mxcli check -p --references` says `Check passed!`, `mx check` says **CE7033** \"A microflow used for background execution must have a Microflow return type of 'Nothing'.\" (at Call microflow activity 'F'). Reported with the CE0142 after-startup sibling, which MDL073 had already closed.", "cause": "The --references pass resolved the call target and the queue name separately, and both resolve. Nothing compared the binding (`in queue`) against the signature of the flow it names. Added MDL088: a project-less pass (ValidateQueuedCallReturnType) for a target the script creates, and validateQueuedMicroflowTargets on the --references path for a stored target, which skips script-defined targets so the fault is not printed twice. Stored void microflows read back as ReturnType \"Void\", not \"\" — both must mean Nothing.", "file": "`mdl/executor/validate_queued_call_return.go` (queuedMicroflowCalls, checkQueuedMicroflowReturnsNothing, validateQueuedMicroflowTargets, ValidateQueuedCallReturnType), wired in `validate_program.go` and `validate.go` (validateFlowBodyReferences); examples `mdl-examples/bug-tests/1064-queued-microflow-must-return-nothing{,.fail}.mdl`", "insight": "Same class as MDL073 (\"the reference resolves\" ≠ \"the reference is usable\"): any binding that names a flow carries a constraint on that flow's signature, and a resolver checks only the name. When one such check lands, sweep for its siblings at other binding sites. The queued CALL JAVA ACTION twin (CE7038) is still unchecked and was deliberately left out of scope. Two things that cost time: (1) `mxcli exec` of a script that CREATEs a queue and then binds a call to it refuses with 'task queue not found' — validateFlowBodyReferences checks queues against the project only, not the script context — so the repro has to create the queue in a separate exec; (2) walk call statements by reflection, not by the flowRefCollector switch, which does not descend into WHILE bodies. Measured on mxbuild 11.12.0 with two projects: Boolean target → CE7033, void target → 0 errors.", "refs": ["mendixlabs/mxcli#1064"], "ce": ["CE7033"], "rules": ["MDL088"]}
{"area": "mdl/executor/microflow-layout", "date": "2026-09-23", "symptom": "MPR011 fires on EVERY `while` loop mxcli writes \u2014 'first activity at (50,80) lies outside the loop box' \u2014 single-level loops included. `mx check` passes and the app runs; the flow just renders wrong in Studio Pro. Reported from a real project as 'looks like an mxcli layout issue', with 3 MPR011 warnings still in its final lint run. mxcli's own lint rule was correctly flagging mxcli's own output.", "cause": "One missing term in the WHILE builder. addWhileStatement had `innerStartX := LoopPadding` (50) where addLoopStatement has `LoopPadding + iteratorSpace + ActivityWidth/2` (210). A microflow object's Position is its CENTRE \u2014 the builder says so itself ('Position is the CENTER point (RelativeMiddlePoint in Mendix)') \u2014 so a centre at x=50 with ActivityWidth=120 puts the left edge at -10. The doc comment says the while layout 'matches addLoopStatement but without iterator icon space': dropping the iterator space (100) was right, taking ActivityWidth/2 with it was not, because that term is not iterator space, it is what converts a centre to a left edge. The very next line, `innerStartY := LoopPadding + ActivityHeight/2`, adds the half-height for exactly this reason \u2014 so the omission was accidental, not a choice. Reported (50,80) matches term for term: 50 = LoopPadding, 80 = LoopPadding + ActivityHeight/2.", "file": "`mdl/executor/cmd_microflows_builder_control.go` (addWhileStatement: `innerStartX := LoopPadding + ActivityWidth/2`), tests `mdl/executor/loop_containment_test.go` (TestWhileLoopBox_ContainsDefaultLaidOutChildren, TestWhileLoopFirstChildLeftEdgeIsInsideTheBox)", "insight": "The containment invariant WAS already tested \u2014 loop_containment_test.go exists from #884 and asserts exactly this \u2014 but every fixture in it built a FOREACH loop. There are two loop builders; one was covered and the uncovered one shipped the violation into every project that writes a `while`. An invariant is worth what its COVERAGE is, and a file named for an invariant reads as if it covers the invariant, which is how a second code path goes unexamined for months. When a rule flags the tool's own output, believe the rule first: the reporter hedged with 'looks like an mxcli layout issue' and was exactly right. Cheap tell for this class: a term present on one axis and absent on the other in adjacent lines (`+ ActivityHeight/2` on Y, nothing on X) is almost always an omission rather than a decision. Failing test written first; it reproduced the reported geometry to the pixel, x[-10,...] at 1, 2, 4 and 7 activities. Still uncovered: addManualWhileTrueStatement, the third loop builder.", "refs": ["ako/mxcli#884", "ako/mxcli#645"]}
{"date": "2026-09-23", "area": "mdl-executor", "symptom": "upstream #1176: DESCRIBE prints `all` on an import activity that returns ONE object — `$objectResponse = import from mapping M.IMM($s) all;` — which reads as a list import. Reported on v0.23.0 / Studio Pro 11.12.3, after #881 was believed to have settled import ranges", "cause": "#881 made `formatImportMappingRange` always emit a range keyword, because at the time a missing keyword let the range fall back to the variable's cardinality and store First. The later runtime fix (unauthored range written as All explicitly) made bare and `all` build the same activity, but the describe side was never revisited, so `all` kept printing where it was only noise", "file": "`mdl/executor/cmd_microflows_format_action.go` (`formatImportMappingRange`: return \"\" for All against SingleObject); tests `mdl/executor/cmd_microflows_import_range_test.go` (`TestImportRange_ObjectResultDescribesWithoutAll`); example `mdl-examples/bug-tests/1176-import-mapping-object-describes-without-all.mdl`", "insight": "**This was not #881 regressing — it was #881's own workaround outliving its reason.** 'DESCRIBE must never emit nothing' was a guard against the builder's then-broken default; once the builder wrote a missing keyword as All explicitly, the guard became pure noise, and nothing linked the two sites. When a formatter emits something 'because the builder would otherwise infer X', put that reason in a test that asserts the builder equivalence (bare vs keyword build the same activity), so fixing the builder flags the formatter. Proving the omission safe needs that equivalence on a real project, not just the unit test: on 11.12.3 both spellings store byte-identical ResultHandling (ConstantRange{SingleObject:false} + ObjectType), `mx check` 0 errors, and exec'ing the described text reports 'Unchanged microflow'. Wrong turn to skip: a JSON diff of two EMPTY extractions prints 'IDENTICAL' — `bson dump` emits ordered Key/Value lists, not objects; check the extraction is non-empty before trusting a diff", "refs": ["#881", "#1176"]}
{"area": "mdl-executor", "date": "2026-09-24", "refs": ["#1173"], "symptom": "`ALTER ENTITY <view> ADD ATTRIBUTE Region: String(200)` passed `mxcli check -p --references`, exec printed \"Added attribute 'Region' to entity MyFirstModule.SaleStats\" with exit 0, and `mx check` then failed CE6770 \"View Entity is out of sync with the OQL Query.\" The attribute was written as DomainModels$StoredValue with no OQL column behind it", "cause": "execAlterEntity's ADD/DROP ATTRIBUTE branches treat every entity as a table: nothing asked isViewEntity, although CREATE ASSOCIATION and bulk ALTER ENTITIES already did. The check-time AlterEntityStmt case only resolved the module and enumerations", "file": "`mdl/executor/cmd_entities.go` (viewEntityAttributeSetRefusal, AlterEntityAddAttribute/DropAttribute guards), `mdl/executor/validate.go` (validateViewEntityAttributeSet); test `mdl/executor/alter_entity_view_test.go`; bug-test `mdl-examples/bug-tests/1173-alter-view-entity-attribute.mdl`", "insight": "**Measure every ALTER op on a view before choosing the fix, not just the reported one** — one mxbuild per op on 11.12.1: ADD → CE6770, DROP → CE6770, MODIFY to the wrong type → CE6770 but MODIFY to the matching type → 0 errors (so MODIFY's failure is a type mismatch, a different gap, not this one), RENAME → 0 errors (the OqlViewValue binds the column by its Reference/alias, not the attribute name), SET COMMENT → 0. Refusing all four attribute ALTERs would have blocked a working RENAME. **Refuse rather than bind**: the issue offers \"create an OqlViewValue bound to the matching alias\", but for ADD there is no matching alias — the query has no such column — so any write stays CE6770; the query is the declaration, so the refusal points at `create or modify view entity`, verified to build clean with the added column. Guard both layers: check must see a view the SCRIPT creates (sc.viewEntities) as well as a stored one (findEntity + isViewEntity), or check passes a script exec stops halfway. Bulk ALTER ENTITIES already excluded views (e.Source/OqlQuery), so the single-entity path was the only entry. Control: stubbing both guards fails all four refusal tests with \"accepted — mxbuild reports CE6770\""}
{"date": "2026-09-24", "area": "mdl-executor", "symptom": "upstream #1175: a `--` comment inside a view entity's select list produced false MDL030 — `select column 1 has no as alias: '-- the customer's running total'` plus a second one for the text after the comment's comma. Reported on v0.23.0 as an apostrophe bug", "cause": "Every static OQL check (ValidateOQLSyntax, ValidateOQLTypes, inferOQLTypes, viewAssociationColumns) works on `Query.RawQuery`, which is stored verbatim and so keeps the author's comments. parseSelectColumns splits on top-level commas with no notion of a comment, so the comment became a column and each comma in it another", "file": "`mdl/executor/oql_comments.go` (`stripOQLComments`, called at the top of the four entry points in `oql_type_inference.go` / `oql_view_associations.go`); tests `mdl/executor/validate_oql_comments_test.go`; example `mdl-examples/bug-tests/1175-oql-comment-is-not-a-select-column.mdl`", "insight": "**The apostrophe was a red herring: a comment with no apostrophe fails the same way** — measured before theorising, and it moved the fix from the quote-skipping in topLevelKeywordIndex to the comment itself. Strip at the entry points, not inside the helpers: the checks also run regexes over the whole query (division, association-path, reserved word), and a comment containing `from`, `/` or `a.B.C_D` would trip those too. Blank comments to spaces of the same length rather than deleting them, so any offset computed on the stripped text still indexes the original. Do NOT strip in the visitor — the stored query keeps the comments, which is the author's documentation. A type-check test using the same query passed without the fix (comment columns infer no type), so it was dropped rather than kept as a test that detects nothing", "refs": ["mendixlabs/mxcli#1175"], "rules": ["MDL030"]}
{"area":"mdl/executor","date":"2026-09-24","symptom":"`alter page M.P { insert into dv { use fragment SaveCancelFooter } }` passed `check` and failed in exec: \"failed to insert: failed to build widgets: failed to build widget SaveCancelFooter: unsupported widget type: USE_FRAGMENT\". Same for REPLACE, and for a fragment nested inside an inserted container.","cause":"Fragment/building-block expansion (pageBuilder.expandFragments) was called only on the CREATE PAGE / snippet / layout paths. ALTER PAGE's applyInsertWidgetMutator / applyReplaceWidgetMutator passed op.Widgets straight to buildWidgetsFromAST, whose pageBuilder even carried ctx.Fragments — the registry was wired, the expansion call was not. Second defect found on the way: cloneWidget copied only Type/Name/Properties/Children, dropping Specialization and TypeIsGeneric, so a cloned `template for` lost its routing.","file":"`mdl/executor/cmd_alter_page.go` (expandAlterFragments, called first in the INSERT and REPLACE mutators), `mdl/executor/cmd_pages_builder_v3.go` (cloneWidget copies the whole struct)","insight":"Expand before ANYTHING inspects the widget list, not just before the build: the duplicate-name check, allColumns and allListViewTemplates all read op.Widgets, and seeing the sentinel they check the fragment's name instead of its widgets' names. When a registry is threaded into a builder, grep for the call that consumes it (`expandFragments`), not for the field — the field being set on every pageBuilder is what made the ALTER path look covered. A field-by-field clone is a silent-drop hazard; `c := *w` then deep-copy the reference fields.","refs":["#572"]}
{"area":"mdl/executor","date":"2026-09-24","symptom":"`describe layout Atlas_Core.Phone_BottomBar` emitted `-- Forms$SimpleMenuBar (simpleMenuBar1) -- NOT re-executable: mxcli cannot author this widget, so re-running this script would drop it`, so a phone layout written in MDL had no bottom bar and a describe -> exec copy of Atlas's lost it","cause":"No keyword, builder, writer or describer for Forms$SimpleMenuBar, although modelsdk/gen already had SimpleMenuBar and MenuDocumentSource. And the widget's whole point is WHICH menu document it renders (a Forms$MenuDocumentSource in MenuSource), a source kind no menu widget could express — menubar/navigationtree always wrote a Forms$NavigationSource, so one pointed at a menu document described as `menubar m` and replayed onto the Responsive profile. Layouts were also never reference-checked, so a new `Menu:` typo would have passed --references and hit CE1613 at build","file":"mdl/grammar/MDLLexer.g4 + domains/MDLPage.g4 + domains/MDLSettings.g4 (SIMPLEMENUBAR), mdl/executor/cmd_pages_builder_v3.go (buildSimpleMenuBarV3, menuSourceV3), mdl/backend/modelsdk/widget_write.go (menuSourceToGen), mdl/executor/cmd_pages_describe_parse.go + cmd_pages_describe_output.go, mdl/executor/helpers.go + validate.go (menu refs, CreateLayoutStmt), sdk/pages/pages_widgets_advanced.go","insight":"**Measure the stored shape before designing the syntax** — dumping the widget from a blank project (`mxcli new --version 11.14.0`, `bson dump --type layout`) showed the source was a menu DOCUMENT, not a profile, which is what made this a source-kind feature rather than a one-keyword copy of `menubar`. Treat MenuSource as one slot with two subtypes on all three menu widgets (one helper each side), or the siblings keep silently rewriting a menu-document source to Responsive. The control that justified the reference check: `menu: Bug573.No_Such_Menu` -> `mxcli check --references` passed, `mx check` CE1613 'The selected menu ... no longer exists.' at Simple menu bar. MDL has no `show menus`, so the not-found message lists the menus that exist. Adding reference validation to CreateLayoutStmt is new coverage for EVERY widget reference in a layout, not just menus","refs":["ako/mxcli#573"],"ce":["CE1613"]}
Loading
Loading