Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
16 commits
Select commit Hold shift + click to select a range
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
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"area": "mdl/executor", "date": "2026-10-24", "symptom": "describe → exec turned a read-only data view editable: `describe page` printed `dataview dataView1 (DataSource: microflow …, ReadOnlyStyle: Text)` without Editable, and `Editable: Never` written by hand was ignored (exec: \"Unchanged page\", stored Forms$DataView.Editability stayed Always); `alter page … set (Editable: Never) on dataView1` was refused as \"a DataView has no Editable property\"", "cause": "A data view stores the enum under `Editability` (Mendix 10.7+), where input widgets store `Editable`. The page builder put the authored value on the BaseWidget like every widget, but the writer took the data view's Editability from DataView.ReadOnly, a field nothing sets; the describe parser's DataView case read neither key; the page mutator only knew the pre-10.7 boolean `Editable`", "file": "mdl/backend/modelsdk/widget_write.go (dataViewEditability); mdl/executor/cmd_pages_describe_parse.go (Forms$DataView case); mdl/backend/pagemutator/mutator.go (editabilityKey)", "fix": "Write pages.WidgetEditability(&x.BaseWidget) for a data view (so Conditional is written beside its settings element too); describe reads `Editability`; the mutator writes `Editability` when the widget stores it", "insight": "The builder was right all along, which is why `check` and the builder-level tests were green: the value was dropped one layer down by a writer reading a dead field. Grep the semantic struct's field for writers (`\\.ReadOnly =`): none in the tree is the tell. The key name is version-dependent — Model SDK version.go has Forms$DataView `editable` Deleted 10.7.0 and `editability` Introduced 10.7.0, and the mutator's own test faked a DataView with a boolean Editable, so it asserted the pre-10.7 shape only. Survey a Studio Pro fixture before trusting a test's hand-built document: PedApp has 41 of 41 data views with Editability and none with Editable", "refs": ["ako/mxcli#1676", "ako/mxcli#1163"], "test": "TestWriteDataView_Editability (mdl/backend/modelsdk/widget_write_dataview_editability_1676_test.go); TestDescribeDataView_EmitsEditability, TestBuildDataView_EditableNeverReachesTheWidget (mdl/executor/cmd_pages_describe_dataview_editability_1676_test.go); TestSetWidgetProperty_Editable_DataViewEditability (mdl/backend/pagemutator/editable_test.go); mdl-examples/bug-tests/1676-dataview-editable-never.mdl"}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"area": "mdl/executor", "date": "2026-10-24", "symptom": "An import-with-mapping activity that passes the mapping's input object (ImportMappingCall.ParameterVariableName) described as `import from mapping M.IMM($json) all;` without it; v0.28.0 refused every rewrite of the flow as \"its stored `import from mapping …` activity has no result entity, which Mendix cannot load\"; lint CONV020 reported the passed object as never committed; and mxcli itself could not call such a mapping — it wrote an empty ParameterVariableName, which mxbuild refuses with CE0244", "cause": "The writer hard-coded Commit YesWithoutEvents and ParameterVariableName \"\", the reader read neither, so nothing above them could carry the object (CONV020's reflective scan never saw the variable name). storedImportWithoutEntity treated any empty result entity as the #1418 unloadable shape, including an unbound result stored as DataTypes$VoidType or with no VariableType, both of which mxbuild loads at 0 errors", "file": "mdl/grammar/domains/MDLMicroflow.g4 (importMappingSettings); mdl/visitor/visitor_import_mapping_settings.go; mdl/executor/cmd_microflows_builder_calls.go (addImportFromMappingAction: CE0244/CE0246 refusals), cmd_microflows_format_action.go (formatImportMappingSettings), cmd_flow_modify.go (storedImportWithoutEntity); mdl/backend/modelsdk/microflow_write.go, microflow_read_actions.go (readMappingCall); sdk/microflows ResultHandlingMapping.ParameterVariable/Commit/ResultType", "fix": "`import from mapping M.IMM($Json) (Parameter: $Obj, Commit: Yes|YesWithoutEvents|No)` — an ADR-0013 settings list after the operand, checked in the visitor; written, read and described; exec refuses a missing parameter on a mapping that declares one and a parameter on one that does not; the splice refuses only an object or list result type naming no entity", "insight": "No fixture had the shape (PedApp and TestApp hold no import activity with a parameter), so mxbuild was the oracle: build the activity with mxcli, then BSON-edit one property at a time and run mx check — missing parameter CE0244, unexpected one CE0246, undefined variable CE0109, unbound result as VoidType or with no VariableType 0 errors. That last measurement is what showed the 'no result entity' refusal was false: the #1418 guard keyed on the symptom (empty entity) rather than the shape that breaks loading (an Object/List type naming no entity). A lint false positive from a rule that scans every string is a READER bug: the variable name was never in the model", "refs": ["ako/mxcli#1681", "mendixlabs/mxcli#1418", "mendixlabs/mxcli#1518", "ako/mxcli#265"], "test": "TestImportFromMapping_Settings, TestImportFromMapping_SettingsRejectsBadInput (mdl/visitor); TestImportXmlActionToGen_ParameterAndCommit (mdl/backend/modelsdk); TestImportMappingParameterAndCommitRoundTrip, TestImportMappingParameterMismatchRefused, TestImportMappingVoidResultIsNotRefused (mdl/executor/import_mapping_parameter_1681_test.go); TestCreatedNeverPersisted 'import mapping parameter' (mdl/linter/rules); mdl-examples/bug-tests/1681-import-mapping-parameter-and-commit.mdl"}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"area": "mdl/executor", "date": "2026-10-24", "symptom": "On a native page, describe → exec turned `'Color': 'Brand primary'` from Forms$OptionDesignPropertyValue (Option) into Forms$CustomDesignPropertyValue (Value); `Size` on the same widget stayed an option. mx check 0 errors either way. check -p warned MDL-WIDGET12 that the native values 'Brand primary' and 'Smallest' were not allowed", "cause": "loadThemeRegistry read only themesource/*/web/design-properties.json, so every widget was typed against the web definitions. Atlas Core 4.1.3 declares DynamicText Color as a web ColorPicker (swatch 'Brand Primary') and a native Dropdown (option 'Brand primary'); a value off the web list on a ColorPicker resolves to custom", "file": "mdl/executor/theme_reader.go (NativeWidgetProperties, ForPlatform); cmd_pages_builder_v3.go (buildPageV3: layoutIsNative); cmd_pages_layout_v3.go; cmd_pages_create_v3.go (storedSnippetIsNative); design_property_routing.go (documentTheme, used by alter page, alter pages styling, alter styling, the snippet-call check); mdl/backend/pagemutator/probe.go (DocumentPlatform); validate_design_properties.go (validateDesignProperties, nativeLayoutResolver)", "fix": "Load the native design-properties.json files beside the web ones and select by the document's platform: a page by its layout's Native flag (FormCall.Form for a stored page), a layout by its layout type, a snippet by its stored Type. check -p resolves the layout from the script or the project", "insight": "When one property changes kind and its siblings do not, diff the two platforms' theme files for that key before reading writer code: `jq '.DynamicText[] | select(.name==\"Color\")'` on web/ and native/ showed ColorPicker vs Dropdown and a one-letter case difference in the option, which is the whole bug. The value's spelling matching the native option exactly, and missing the web one by case, is what made it custom. mx check does not flag a custom value on a native Dropdown, so the write layer is the only place to test it; the full run needs a BSON edit to put Studio Pro's Option shape back first, because mxcli had written Custom at create time", "refs": ["ako/mxcli#1679", "ako/mxcli#511"], "test": "TestThemeRegistry_LoadsNativeDefinitions, TestNativePageDesignPropertyKeepsOptionKind, TestNativePageAlterSetDesignPropertyKeepsOptionKind, TestValidateDesignProperties_NativePageUsesNativeDefinitions (mdl/executor/design_props_native_1679_test.go); mdl-examples/bug-tests/1679-native-design-property-kind.mdl"}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"area": "mdl/executor", "date": "2026-10-24", "symptom": "On a native page, describe → exec turned a heading into body text: Forms$DynamicText.NativeTextStyle Heading3 came back as Text. Describe printed `dynamictext text4 (Content: 'JTS boat log')`; `NativeTextStyle: Heading3` written by hand passed parsing with an MDL-WIDGET07 'will be silently dropped' warning", "cause": "MDL had no spelling for NativeTextStyle: no model field, the builder read nothing, describe read nothing, and both writers (modelsdk widget_write.go and the MCP backend's page_widgets.go) hard-coded \"Text\"", "file": "sdk/pages/pages_widgets_display.go (NativeTextStyle, CanonicalNativeTextStyle); mdl/executor/cmd_pages_builder_v3_widgets.go (buildDynamicTextV3); mdl/executor/cmd_pages_describe_parse.go, cmd_pages_describe_output.go; mdl/backend/modelsdk/widget_write.go; mdl/backend/mcp/page_widgets.go; mdl/backend/pagemutator/mutator.go (setDynamicTextNativeTextStyleMut); mdl/executor/validate_widgets.go", "fix": "Add `NativeTextStyle: Text | Heading1..Heading6` to dynamictext (case-insensitive, canonical on write, unknown refused with the list), print it when not Text, write it in both backends, accept it in `alter page … set`, and add it to the built-in widget vocabulary", "insight": "A hard-coded literal in a writer is the cheapest tell for this class: `grep -n 'Set\\w*(\"' widget_write.go` lists every property the writer can never vary, and the generated enum (modelsdk/gen/pages/enums.go) says whether the literal is the only member. Copy fixes to every writer: the MCP backend had the same literal. A fixture survey shows why no round-trip test caught it: PedApp stores Text on all 284 dynamic texts, so a Studio Pro fixture without native headings cannot fail; the repro needs a BSON edit", "refs": ["ako/mxcli#1677", "mendixlabs/mxcli#1228"], "test": "TestDescribeDynamicText_EmitsNativeTextStyle, TestBuildDynamicText_NativeTextStyle, TestBuildDynamicText_NativeTextStyleRejectsUnknown, TestValidateDynamicText_NativeTextStyleIsKnown (mdl/executor/dynamictext_native_text_style_1677_test.go); TestWriteDynamicText_NativeTextStyle (mdl/backend/modelsdk); TestSetWidgetProperty_NativeTextStyle (mdl/backend/pagemutator); TestMapPageWidget_DynamicTextNativeTextStyle (mdl/backend/mcp); mdl-examples/bug-tests/1677-native-text-style.mdl"}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"area": "mdl/executor", "date": "2026-10-24", "symptom": "describe → exec dropped the design properties stored on a page itself (Forms$Page Appearance.DesignProperties): native pages lost 'Spacing top': 'Inner large', 'Spacing bottom': 'Inner large', 'Background color': 'Background Primary'. describe printed none in the header, exec wrote an empty list, and a page-level `alter page … set ('Background color': …)` was refused as 'unsupported page-level property'", "cause": "MDL had no page-level spelling: the page header grammar had no DesignProperties, pages.Page no field, pageToGen passed nil to newAppearance, describe's header read only Class and Style, and page-level SET routed every key to applyPageLevelSetMut", "file": "mdl/grammar/domains/MDLPage.g4 (pageHeaderPropertyV3); mdl/visitor/visitor_page_v3.go; mdl/executor/cmd_pages_builder_v3.go (buildPageV3), cmd_pages_describe.go (page header); mdl/backend/modelsdk/page_write.go (pageToGen); mdl/executor/theme_reader.go (pageThemeKey: Document + Page groups); design_property_routing.go + mdl/backend/pagemutator/mutator.go (findStyleableWidget(\"\") is the page); validate_design_properties.go", "fix": "Add `DesignProperties: ( … )` to the page header, typed against the theme's Document and Page groups after the layout fixes the platform; describe prints it; page-level `alter page … set ('Key': value)` writes the page's own Appearance; check -p validates the header", "insight": "The values looked like a custom theme until every design-properties.json was grepped for the key across ALL groups: Atlas Core's native file has a 'Document' group no widget type is named after, and it holds exactly the reported values. Grep by property name, not by the group you expect. Which groups a page takes was settled by mxbuild, not by reading: a page-level 'Spacing left': 'Small' (valid in Widget, not in Document) is CE6085 'Unknown option Small', so a page takes Document and not Widget — including Widget made check warn on the reporter's own values", "refs": ["ako/mxcli#1678", "ako/mxcli#714", "ako/mxcli#1679"], "test": "TestCreatePage_DesignProperties (mdl/visitor/visitor_page_class_test.go); TestPageToGen_WritesPageDesignProperties (mdl/backend/modelsdk); TestPageDesignProperties_RoundTrip, TestPageDesignProperties_AlterPageLevelSet, TestValidateDesignProperties_PageHeader (mdl/executor/page_design_properties_1678_test.go); mdl-examples/bug-tests/1678-page-design-properties.mdl"}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"area": "mdl/executor", "date": "2026-10-24", "symptom": "`create or replace microflow M.F …` refused by the splice said \"create or modify microflow M.F: this change cannot be spliced into the stored flow …\", naming a statement the author never wrote; same for nanoflows, and the same text in check's 'exec would refuse this statement'", "cause": "The visitor folds `or replace` into CreateOrModify (the two splice alike) and kept no record of the spelling, so flowRefusal had only a literal 'create or modify' to print", "file": "mdl/visitor/visitor_microflow.go (CreateOrReplace); mdl/ast/ast_microflow.go; mdl/executor/cmd_flow_modify.go (flowDecl.replace, verb); mdl/executor/flow_verdict.go (flowRefusal)", "fix": "Record CreateOrReplace beside CreateOrModify for microflows and nanoflows, carry it on flowDecl, and print the verb as written", "insight": "When one AST flag stands for two spellings, every message keyed on the flag names one of them wrongly; keep the spelling beside the meaning. A user who wrote `create or replace` and read 'create or modify' reasonably concluded that their statement was silently re-interpreted, which is the opposite of what the refusal is for", "refs": ["ako/mxcli#1681"], "test": "TestFlowRefusalNamesTheStatementAsWritten (mdl/executor/flow_verdict_test.go)"}
37 changes: 27 additions & 10 deletions .claude/skills/mendix/json-structures-and-mappings/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,16 +14,16 @@ description: "Create and manage JSON structures, import mappings and export mapp
| 111-122 | Export Mappings |
| 123-187 | JSON Structures |
| 188-229 | Message Definitions |
| 230-390 | Import Mappings |
| 391-488 | Export Mappings |
| 489-506 | Starting a Mapping Below the Payload Root |
| 507-576 | Microflow Actions |
| 577-589 | Browse |
| 590-637 | Export Workflow: PE → NPE → JSON |
| 638-644 | Realistic Example: Countries REST API |
| 645-668 | Placing Documents in Folders |
| 669-684 | Documentation |
| 685-699 | Common Mistakes |
| 230-393 | Import Mappings |
| 394-491 | Export Mappings |
| 492-509 | Starting a Mapping Below the Payload Root |
| 510-593 | Microflow Actions |
| 594-606 | Browse |
| 607-654 | Export Workflow: PE → NPE → JSON |
| 655-661 | Realistic Example: Countries REST API |
| 662-685 | Placing Documents in Folders |
| 686-701 | Documentation |
| 702-716 | Common Mistakes |

<!-- /skill-index -->
# JSON Structures, Import Mappings & Export Mappings
Expand Down Expand Up @@ -386,6 +386,9 @@ Using `parameter` without declaring one is refused — the build reports it as
CE0279. The declared entity must match the microflow's parameter type, which the
build checks as CE0282.

The **activity** that calls such a mapping passes the object in its settings
list, `(Parameter: $Obj)` — see "Settings" under Import from Mapping below.

---

## Export Mappings
Expand Down Expand Up @@ -516,6 +519,20 @@ $PetResponse = import from mapping Module.IMM_Pet($JsonContent);
import from mapping Module.IMM_Pet($JsonContent);
```

#### Settings — the input object and the commit mode

A settings list after the source variable holds the activity's dialog:

```sql
import from mapping Module.IMM_Embed($Json) (Parameter: $Collection);
$Pets = import from mapping Module.IMM_Pets($Json) (Commit: No) all;
```

- `Parameter: $Obj` — the mapping's input object. **Required** for a mapping
declared with `parameter Module.Entity` (without it mxbuild reports CE0244),
and refused on a mapping without one (CE0246); `exec` refuses both.
- `Commit: Yes | YesWithoutEvents | No` — omitted means YesWithoutEvents.

#### Range — how much of the result to bind

Optional trailing clause, matching Studio Pro's **All / First / Custom** setting
Expand Down
11 changes: 10 additions & 1 deletion cmd/mxcli/syntax/features_microflow.go
Original file line number Diff line number Diff line change
Expand Up @@ -923,9 +923,17 @@ func init() {
Keywords: []string{
"import from mapping", "export to mapping", "import mapping activity",
"range", "all", "first", "limit", "offset", "single object",
"mapping parameter", "input object", "commit",
},
Syntax: "[$Var =] IMPORT FROM MAPPING Module.IMM ($SourceVar) [<range>];\n" +
Syntax: "[$Var =] IMPORT FROM MAPPING Module.IMM ($SourceVar) [( <settings> )] [<range>];\n" +
"$Var = EXPORT TO MAPPING Module.EMM ($EntityVar);\n\n" +
"<settings> — the activity's dialog, any of:\n" +
" Parameter: $Obj the mapping's input object, for a mapping\n" +
" declared with `parameter Module.Entity`.\n" +
" Required there (mxbuild CE0244 without it),\n" +
" refused on any other mapping (CE0246)\n" +
" Commit: Yes | YesWithoutEvents | No\n" +
" omitted = YesWithoutEvents\n\n" +
"<range> — Studio Pro's Range on the import activity:\n" +
" ALL bind the whole result\n" +
" FIRST bind ONE object (not a one-element list)\n" +
Expand All @@ -941,6 +949,7 @@ func init() {
Example: "$Pets = import from mapping Shop.IMM_Pets($Json) all;\n" +
"$Pet = import from mapping Shop.IMM_Pets($Json) first;\n" +
"$Page = import from mapping Shop.IMM_Pets($Json) limit 10 offset 5;\n" +
"import from mapping Shop.IMM_Embed($Json) (Parameter: $Collection, Commit: Yes);\n" +
"$Json2 = export to mapping Shop.EMM_Pet($Pet);",
SeeAlso: []string{"import-mapping", "export-mapping", "json-structure"},
})
Expand Down
Loading
Loading