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
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"area": "cmd/mxcli", "date": "2026-10-09", "symptom": "Every mxcli project's docker stack was the Compose project `docker`: containers docker-db-1 / docker-mendix-1 and volume docker_postgres-data were shared, so a second app's `docker up` replaced the first app's containers and two apps used one Postgres volume", "cause": "docker init wrote a compose file with no top-level `name:`, and mxcli runs compose with cmd.Dir = <app>/.docker, so Compose derived the project name from the folder, which is `.docker` for every app (normalised to `docker`).", "file": "cmd/mxcli/docker/composename.go + init.go + templates/docker-compose.yml", "fix": "Template v3 has a top-level `name:` filled in by docker init (mxcli-<sanitised app folder>). A stack's name is the key to its data, so it is chosen only when the compose file is created from nothing: --force on an existing file keeps its name: line, and on an old file with none pins the name Compose was using (COMPOSE_PROJECT_NAME from the old .env if set, else the normalised folder name, `docker`) and prints how to opt in.", "insight": "Anything that changes a Compose project name strands the old named volumes silently (new empty volumes appear, nothing errors), so a fix to a naming default must be new-files-only and the regeneration path must read the old file before overwriting it. Also check what the regeneration overwrites besides the file: --force rewrites .env, which is where the pre-fix workaround (COMPOSE_PROJECT_NAME) lived. The name goes in the stamped compose file, not .env, so a .env that is recreated by itself can never rename a stack. Verified read-only with `docker compose config` in temp dirs; no code path hardcodes container or volume names (all compose calls address services, not containers).", "refs": ["mendixlabs/mxcli#1364"]}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"date": "2026-10-12", "area": "mdl/executor", "symptom": "`describe snippet` printed a data view sourced from a snippet parameter (`dataview dv (DataSource: $Parent)` inside `Params: ($Parent: …)`) with no DataSource; re-executing the output stored SourceVariable: null and mx check failed CE0488 \"No entity configured for the data source of this data view\".", "cause": "parseContextSource (cmd_pages_describe_datasource.go) read only SourceVariable.PageParameter. Inside a snippet the name lives in the SnippetParameter slot with PageParameter empty. The association-source reader beside it (associationSourcePath, #721 L3) and the pluggable reader already fell back to SnippetParameter; this was the one copy that did not.", "fix": "parseContextSource falls back to SourceVariable.SnippetParameter and returns the same `parameter` source.", "insight": "A slot-pair bug fixed in one reader is a list of every reader of that BSON field: `grep -rn '\\[\"PageParameter\"\\]' mdl/` finds four, and one had been missed. The decisive check is a round trip, not the describe text: exec the describe output on a copy and mx check it. Here the old output gave CE0488 and the fixed output gave 'Unchanged snippet' (write elided) and 0 errors. Found by eye while dumping BSON for an unrelated issue (#1379): the stored data view had SnippetParameter set but the describe text showed no source.", "issue": "ako/mxcli#1138", "file": "mdl/executor/cmd_pages_describe_datasource.go (parseContextSource)", "test": "mdl/executor/cmd_pages_describe_datasource_test.go TestParseDataSourceCoversEveryStoredType/snippet_parameter, mdl-examples/bug-tests/1138-describe-snippet-parameter-dataview-source.mdl"}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"date": "2026-10-12", "area": "mdl/executor", "symptom": "mendixlabs/mxcli#1252: a Studio Pro 11.12.3 Aggregate list activity with a custom error handler (with or without rollback) described as a bare `$count = count($TEYAs);`, and `on error` was a parse error (mismatched input 'on' expecting ';') on list operations, aggregates, create list, add/remove, cast, rollback and show home page. MDL077 additionally refused the clause on list operations and aggregates.", "cause": "THREE LAYERS, one false premise. (1) Grammar: eight statement rules had no onErrorClause. (2) MDL077 asserted Microflows$ListOperationsAction and AggregateAction have no ErrorHandlingType 'in the metamodel' — the Model SDK declares it on the base Microflows$MicroflowAction, which every one of them extends, with an unbounded StructureVersionInfo; the vendored gen types and the #591 writers carried it too. (3) Reader: actionFromGen never read ErrorHandlingType off RollbackAction or ShowHomePageAction (and their sdk structs had no field), so DESCRIBE's gate saw \"\" and dropped the whole handler branch. The list activities DID read it, so on HEAD their describe emitted a live clause the grammar then refused — the #1078 trap again.", "file": "mdl/grammar/domains/MDLMicroflow.g4; mdl/visitor/visitor_microflow_statements.go; mdl/visitor/visitor_microflow_actions.go; mdl/executor/cmd_microflows_builder_actions.go; mdl/executor/validate_microflow_error_handling.go; mdl/executor/nanoflow_validation.go; mdl/backend/modelsdk/microflow_read_actions.go; mdl/backend/modelsdk/microflow_write.go; sdk/microflows/microflows_actions.go", "fix": "onErrorClause on the eight rules; AST fields; builders use ehType(clause) + finishCustomErrorHandler (rollback/home page use explicitErrorHandling so no-clause BSON is unchanged); add/remove to a reference set pass the clause to the Change object activity; MDL077 (error-handling) removed; MDL076 and the nanoflow without-rollback-only rule extended from a measured matrix; reader/writer carry the type on rollback and show home page.", "test": "TestAuthorOnError_1252_*, TestMDL076_1252_ContinueRefusedWhereMxbuildRejectsIt, TestNanoflow_1252_ListActivitiesTakeOnlyWithoutRollback, TestDescribe_1252_ErrorHandlerSurvives, TestActionFromGen_1252_ReadsErrorHandlingType, TestMicroflowRoundTrip_1252_*; mdl-examples/bug-tests/1252-on-error-list-activities.mdl", "insight": "**Settle 'not in the metamodel' against the Model SDK before writing a rule on it.** MDL077's premise took one grep to refute: `npm pack mendixmodelsdk`, then errorHandlingType sits on the base MicroflowAction class every action extends — a property on a base class is invisible to a per-type search. A refusal rule built on an unverified absence turns a capability gap into an enforced one. **Measure, do not extrapolate, the accepted forms**: one 11.12.3 project with one microflow AND one nanoflow per (statement x clause) cell — 85 documents, a single `mx check -j out.json` (the JSON names the document; the text output does not) — gave the whole matrix in one build. It is not guessable: in a microflow all eight take custom handlers and reject continue EXCEPT rollback, which takes continue; in a nanoflow the list activities and cast share create/commit's 'without rollback only' row while rollback takes everything; show home page is microflow-only. **Default-preservation is per writer**: rollback's writer never stored a type and show home page's always stored a literal Rollback, so both use explicitErrorHandling; the list activities already wrote ehType(nil) and keep ehType. Controls: stub the visitor assignment -> builder test reports 0 Custom activities; drop the reader line -> ErrorHandlingType \"\"; drop the MDL076 rows -> continue accepted. describe -> exec of the matrix reports every document already in sync."}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"date": "2026-10-12", "area": "mdl/grammar", "symptom": "`column (Attribute: \"MyModule\".\"AssocA_2\"/\"MyModule\".\"AssocB_2\"/\"NId\")` in a Data grid 2 over a junction entity failed `mxcli check` with MDL-WIDGET34 \"Attribute: &{…} is not an attribute binding MDL can store\" plus MDL-WIDGET42 \"cannot hold the expression\" (mxcli 0.25.0, Mendix 11.12.4).", "cause": "Same root as the 2026-10-11 qualified-association-hop finding: attributePathV3 took only bare identifiers between slashes, so module-qualified hops fell through to `keyword COLON expression` and parsed as a division. Already fixed by 05a05aad, which shipped after the 0.25.0 the reporter ran; its tests pinned two qualified hops at the parser but only one hop at the builder.", "fix": "No code change. Added the reported shape as tests at the check layer (the issue's script verbatim through visitor.Build + validateWidgetTree) and at the builder (two qualified hops -> Attribute MyModule.AssocB.NId with two IndirectEntityRef steps, equal to the bare spelling), plus a bug-test MDL. mx check on 11.12.4: 0 errors with and without widget normalisation.", "insight": "Before fixing a report, check whether HEAD already passes the reporter's script verbatim and build the commit before the suspected fix as a control: here the pre-fix binary reproduced the exact `&{…}` WIDGET34 text, which proves the match rather than assuming it from the error code. A 'two hops' parser case does not cover the builder walking two hops — pin each layer with the reported depth. Wrong turn to skip: the reporter's entities are external (OData); the path resolution is the same for persistent entities, so the repro does not need an OData service. Side observation: DESCRIBE of a snippet data view sourced from a snippet parameter prints no DataSource although the BSON stores SnippetParameter.", "issue": "mendixlabs/mxcli#1379", "file": "mdl/grammar/domains/MDLPage.g4 (attributePathV3), mdl/executor/cmd_pages_builder_input.go (resolveAssociationPathIn)", "test": "mdl/executor/cmd_pages_builder_multihop_column_test.go, mdl-examples/bug-tests/1379-datagrid-column-multi-hop-qualified-path.mdl"}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"area":"mdl/visitor","date":"2026-10-12","symptom":"`show message … type error` stored Microflows$ShowMessageAction Type \"error\"; the runtime showed an Information dialog for an error message while `mx check` reported 0 errors (mendixlabs/mxcli#1280). `type ok` was stored as \"ok\" the same way","cause":"buildShowMessageStatement copied the `identifierOrKeyword` text verbatim; the stored enumeration value is case-sensitive and nothing between the visitor and the writer validated it","file":"`mdl/visitor/visitor_microflow_actions.go` (buildShowMessageStatement), `mdl/executor/cmd_microflows_builder_calls.go` (addShowMessageAction), `sdk/microflows/microflows_actions.go` (CanonicalMessageType), `mdl/executor/validate_microflow_show_message.go` (MDL-SHOWMSG01)","insight":"**An enum value taken from a free-form `identifierOrKeyword` needs case-folding to the gen enum's spelling AND a check for non-members** — skills write keywords in lower case, so the lower-case spelling is the common one, and `mx check` accepts any string for an enumeration property, so neither mx check nor a build catches it; only the stored bytes or the running app do. Normalise in the visitor (so declared-vs-described AST comparison agrees) and again in the writer. Verified on 11.12.4: the pre-fix binary stores `Type = error`, the fixed one `Error`, and `docker check` passes both. Grep other `identifierOrKeyword` enum slots for the same shape","refs":["mendixlabs/mxcli#1280"],"ce":[]}
13 changes: 8 additions & 5 deletions .claude/skills/mendix/docker-workflow/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,11 @@ description: "Build, run and validate a Mendix app in Docker from inside a devco
| 53-68 | Architecture |
| 69-85 | Quick Start: One Command |
| 86-113 | Creating an Empty Mendix App |
| 114-272 | Step-by-Step Workflow |
| 273-303 | Common Workflow: Edit, Rebuild, Test |
| 304-415 | Hot Reload |
| 416-434 | Environment Variables |
| 435-454 | Troubleshooting |
| 114-274 | Step-by-Step Workflow |
| 275-305 | Common Workflow: Edit, Rebuild, Test |
| 306-417 | Hot Reload |
| 418-436 | Environment Variables |
| 437-457 | Troubleshooting |

<!-- /skill-index -->
# Docker Build & Run Skill
Expand Down Expand Up @@ -167,6 +167,8 @@ mxcli docker init -p app.mpr

This creates a `.docker/` directory with Docker Compose configuration for the Mendix app + PostgreSQL.

**Stack name:** `docker init` writes a Compose project name into `.docker/docker-compose.yml` (`name: "mxcli-<app folder>"`), so containers and volumes of different apps are separate (`mxcli-myapp-db-1`, `mxcli-myapp_postgres-data`). Never change `name:` on a stack that holds data you need: Compose then starts new containers with an empty database and leaves the old volume behind. Projects initialised by older mxcli have no `name:`, so Compose calls them `docker` (`docker-db-1`, `docker_postgres-data`), shared with every other such project; `docker init --force` keeps that name rather than stranding the data.

**Port conflicts:** If default ports (8080/8090/5432) are already in use, check with `ss -tlnp | grep -E '808|809|543'` and use `--port-offset N` to shift all ports:

```bash
Expand Down Expand Up @@ -450,5 +452,6 @@ All defaults can be overridden in `.docker/.env`.
| `password should not be empty (debugger)` | Add `RUNTIME_DEBUGGER_PASSWORD` — re-run `mxcli docker init --force` |
| `security level should be set to CHECKEVERYTHING` | App in Production mode without security — set to Development mode or configure security |
| Port 8080 not accessible | Check `forwardPorts` in devcontainer.json includes 8080 |
| Two projects overwrite each other's containers or share one database (both show as project `docker`) | Old compose file without `name:`. Back up the data, set a unique `name:` at the top of `.docker/docker-compose.yml` (e.g. `mxcli-myapp`), then `docker compose up`; the new stack starts with an empty database |
| Database errors on startup | Try `mxcli docker up -p app.mpr --fresh` to reset volumes |
| OQL: "Action not found: preview_execute_oql" | Runtime needs `-Dmendix.live-preview=enabled` JVM flag — re-run `mxcli docker init --force` to get the updated docker-compose.yml |
4 changes: 2 additions & 2 deletions .claude/skills/mendix/write-microflows/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -712,8 +712,8 @@ close page on error begin return; end error;
of them; `continue` is fine on `declare`, `set`, `retrieve`, `delete` and
`call microflow`. Measured on 11.14.0 — note that create-*variable* and
change-*variable* accept `continue` while change-*object* does not.
- **List operations and aggregates** (`$x = head $l;`, `$n = count $l;`)
have no error handling in Mendix at all — **MDL077**.
- **List/aggregate activities, `create list`, `add`/`remove`, `cast`, `show home
page`**: no `continue` (MDL076, 11.12.3, #1252); `rollback` takes every form.

**In a nanoflow, almost none of them take a clause at all.** `change`, `log`,
`show page`, `close page`, `show message` and `validation feedback` are CE6035
Expand Down
19 changes: 10 additions & 9 deletions .claude/skills/mendix/write-nanoflows/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,11 @@ description: "Nanoflow syntax in MDL — shared with microflows, but client-side
| 482-552 | NanoflowCommons JavaScript Actions Reference |
| 553-591 | Disallowed Activities |
| 592-595 | Return Type Restrictions |
| 596-639 | Error Handling in Nanoflows |
| 640-647 | Security (GRANT/REVOKE) |
| 648-660 | Management Commands |
| 661-671 | Common Mistakes |
| 672-686 | Validation Checklist |
| 596-640 | Error Handling in Nanoflows |
| 641-648 | Security (GRANT/REVOKE) |
| 649-661 | Management Commands |
| 662-672 | Common Mistakes |
| 673-687 | Validation Checklist |

<!-- /skill-index -->
# Mendix Nanoflow Skill
Expand Down Expand Up @@ -620,13 +620,14 @@ END ERROR;

### Which activities take which clause in a nanoflow

Measured on Mendix 11.14.0; every other form is **CE6035** "Error handling type
is not supported", and mxcli refuses it (MDL091):
Measured on Mendix 11.14.0 (the list activities, cast and rollback on 11.12.3,
mendixlabs/mxcli#1252); every other form is **CE6035** "Error handling type is
not supported", and mxcli refuses it (MDL091):

| Activity in a nanoflow | Accepted clauses |
|---|---|
| `DECLARE`, `SET`, `RETRIEVE`, `DELETE` | every form |
| `CREATE`, `COMMIT`, `CALL NANOFLOW`, `CALL MICROFLOW` | only `ON ERROR WITHOUT ROLLBACK BEGIN … END ERROR` |
| `DECLARE`, `SET`, `RETRIEVE`, `DELETE`, `ROLLBACK` | every form |
| `CREATE`, `COMMIT`, `CALL NANOFLOW`, `CALL MICROFLOW`, list operations (`head`, `filter`, …), aggregates (`count`, `sum`, …), `CREATE LIST OF`, `ADD … TO $List`, `REMOVE … FROM $List`, `CAST` | only `ON ERROR WITHOUT ROLLBACK BEGIN … END ERROR` |
| `CHANGE`, `LOG`, `SHOW PAGE`, `CLOSE PAGE`, `SHOW MESSAGE`, `VALIDATION FEEDBACK` | none |

mxcli refuses the clause rather than writing a nanoflow mxbuild rejects. The
Expand Down
Loading
Loading