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": "mdl/executor", "date": "2026-10-21", "symptom": "mendixlabs/mxcli#1597 (case B): `@annotation('Why this commit is here.')` in an `alter microflow … insert after $Result begin … end;` fragment passed check and exec (\"Altered microflow\") and no note was written; the same line in `create or modify microflow` is refused as MDL079", "cause": "the annotation rules (MDL059 unknown name, MDL060 bad curve, MDL079 bad note, MDL092 bad anchor) ran only over create bodies — ValidateProgram had no AlterFlowStmt case, and execAlterFlow ran no statement validation — while the fragment goes through the same flowBuilder, which drops what the visitor could not read", "file": "mdl/executor/validate_alter_flow.go (ValidateAlterFlow, validateAlterFlowRules); validate_program.go (AlterFlowStmt case); cmd_alter_flow.go (execAlterFlow refuses the exec-enforced subset before loading)", "test": "TestValidateProgram_ChecksAnnotationsInAnAlterFlowFragment (all four rules, insert and replace, nested in an if), TestValidateProgram_AlterFlowFragmentValidNotesStayClean (control), TestExecAlterFlow_RefusesADroppedAnnotation", "insight": "The reported rule was one of four with the same hole: probing `@postion`, `@curve(from: (a, b))` and `@anchor(from: sideways)` in the same fragment showed all four passing, which is what moved the fix from one rule to walkAnnotations. Only the annotation rules are run on a fragment — the rest of the microflow rule set reads parameters, declares and later uses of the whole flow, which a fragment cannot see, so running ValidateMicroflow on it would trade a silent drop for false refusals. MDL079 is still not exec-enforced for create or alter, so `-c` and the REPL write past it in both; exec of a file is covered by its check preflight", "refs": ["mendixlabs/mxcli#1597", "ako/mxcli#1507"], "rules": ["MDL059", "MDL060", "MDL079", "MDL092"]}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"area": "mdl/executor", "date": "2026-10-21", "symptom": "mendixlabs/mxcli#1597 (case A): `@curve(from: (39, 0), to: (0, 61))` on a statement inside a `loop` or `while` body passed check and exec with no warning, and `describe` printed the body with no `@curve` — the same annotation after the loop was kept", "cause": "a loop body is built by its own flowBuilder; applyPendingAnnotations recorded the curve on that sub-builder's curveByOrigin, and addLoopStatement / addWhileStatement hoisted flows, annotationFlows, errors and the label registry to the parent but not the curves. applyFlowCurves runs once, on the builder that owns the flows, so the body's curves were never applied", "file": "mdl/executor/cmd_microflows_builder_annotations.go (adoptCurves); cmd_microflows_builder_control.go (addLoopStatement, addWhileStatement); cmd_microflows_builder_flows.go (addErrorHandlerFlow now calls the same helper)", "test": "TestLoopBody_KeepsCurve, TestWhileBody_KeepsCurve, TestNestedLoopBody_KeepsCurve (cmd_microflows_loop_curve_test.go)", "insight": "#991 fixed exactly this for the error-handler builder a month earlier, at one of the three sites that hoist a sub-builder's flows — each site copies a hand-picked list of fields, so a fix at one is not a fix at the others. When a child builder's state is lost, grep `&flowBuilder{` for every sub-builder and diff the field lists their merge tails copy, rather than following the reported statement. The merge is now one helper (adoptCurves) the three sites share, and a nested loop test proves it chains through two levels. The main-line curve after the loop is the in-test control: it passed before the fix, so the test isolates the body", "refs": ["mendixlabs/mxcli#1597", "mendixlabs/mxcli#991", "ako/mxcli#1507"]}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"area": "mdl/grammar", "date": "2026-10-21", "symptom": "mendixlabs/mxcli#1588: `create java action` had no way to set a parameter's description or the return value's name; `describe` printed both as `--` comments (`Input: String not null -- The text to convert.`, `-- return NAME: 'UpperText'`), and a script copying that form passed check and exec with the description empty and the return name `ReturnValueName`", "cause": "no grammar slot for either: javaActionParameter had no docComment and javaActionReturnType no `AS VARIABLE`, so the only spelling describe could offer was a comment the lexer discards. The writers carried the stored values (#705, #731), which hid the gap on round trips of Studio Pro-authored actions and made it total on new ones", "file": "mdl/grammar/domains/MDLMicroflow.g4 (javaActionParameter: docComment?, javaActionReturnType: AS VARIABLE); mdl/visitor/visitor_javaaction.go (buildJavaActionParams, buildJavaActionReturn — one helper for the Java and JavaScript handlers); mdl/executor/cmd_javaactions.go + cmd_javascript_actions_write.go (carriedDocumentation per parameter, stated ReturnName wins); cmd_javaactions.go writeCodeActionParams / describeCodeActionReturnName shared by both describers", "test": "cmd_javaactions_param_description_test.go: create, rewrite (stated replaces, unstated carries) and describe round trip, for Java and JavaScript actions", "insight": "When describe prints a value as a comment, ask whether create has a slot for it — a comment in describe output is either decoration or an unspellable property, and only the second is a bug. Pick the spelling the language already uses for the same thing elsewhere (a member's `/** */` as on an attribute, `returns T as $Name` as on a microflow): the reporter had tried exactly those two first. Java and JavaScript actions share the grammar rules, so a field read by only one handler would have been a fresh silent drop in the other — both visitors now go through one helper and both describers through one emitter. The `--` form had also kept only the first line of a multi-line description; the doc comment carries all of it, and carriedDocumentation's normal form keeps a CRLF description from churning on describe → exec", "refs": ["mendixlabs/mxcli#1588", "ako/mxcli#1496"]}
35 changes: 29 additions & 6 deletions .claude/skills/mendix/java-actions/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,12 @@ description: "Create and call custom Java actions — extending Mendix with serv
| 28-39 | Reference files |
| 40-48 | When to Use This Skill |
| 49-55 | Overview |
| 56-332 | Part 2.5: Creating Java Actions in MDL |
| 333-526 | Part 3: Calling Java Actions from MDL |
| 527-540 | Validation Checklist |
| 541-551 | Common Errors |
| 552-559 | Related Documentation |
| 560-641 | Quick Reference |
| 56-355 | Part 2.5: Creating Java Actions in MDL |
| 356-549 | Part 3: Calling Java Actions from MDL |
| 550-563 | Validation Checklist |
| 564-574 | Common Errors |
| 575-582 | Related Documentation |
| 583-664 | Quick Reference |

<!-- /skill-index -->
# Mendix Java Actions Skill
Expand Down Expand Up @@ -78,6 +78,29 @@ return false;
$$;
```

### Parameter Descriptions and the Return Value Name

What a modeler sees when dragging the action into a microflow: each parameter's
description, and the return value's name (the default output variable name).
A description is a `/** … */` doc comment **before** the parameter, as on an entity
attribute; the return value's name is `returns T as $Name`, as on a microflow:

```mdl
mdl 1;
/** Converts text to upper case. */
create java action Module.ToUpper(
/** The text to convert. */
Input: string not null
) returns string as $UpperText
as $$
return Input.toUpperCase();
$$;
```

A trailing `-- text` after a parameter is an ordinary comment and is **not stored**.
On `create or modify`, leaving either out keeps what is stored; a new action gets
no descriptions and `ReturnValueName`. The same forms apply to JavaScript actions.

### Type Parameters (Generics)

Type parameters let Java actions accept any entity type dynamically. Use `entity <pEntity>` in a parameter type to declare the type parameter inline. That parameter becomes the **entity type selector** (receives the entity type name, e.g., `'Module.Entity'`). Bare `pEntity` parameters become **parameterized entity** params (receive entity instances, e.g., `$Variable`).
Expand Down
21 changes: 12 additions & 9 deletions .claude/skills/mendix/javascript-actions/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,14 @@ description: "Create, call and drop JavaScript actions — client-side logic cal
|---|---|
| 31-39 | When to Use This Skill |
| 40-51 | Overview |
| 52-94 | Syntax |
| 95-156 | Examples |
| 157-168 | Calling from a nanoflow |
| 169-193 | The generated .js file |
| 194-202 | Inspecting |
| 203-211 | Validation Checklist |
| 212-221 | Common Errors |
| 222-226 | Related Documentation |
| 52-97 | Syntax |
| 98-159 | Examples |
| 160-171 | Calling from a nanoflow |
| 172-196 | The generated .js file |
| 197-205 | Inspecting |
| 206-214 | Validation Checklist |
| 215-224 | Common Errors |
| 225-229 | Related Documentation |

<!-- /skill-index -->
# Mendix JavaScript Actions Skill
Expand Down Expand Up @@ -53,7 +53,7 @@ normally returns a `Promise`.

```sql
CREATE [OR MODIFY] JAVASCRIPT ACTION Module.Name ( parameters )
RETURNS type
RETURNS type [AS $ReturnName]
[EXPOSED AS 'caption' IN 'category']
[PLATFORM Web | Native | Hybrid | All] -- default Web
AS $$
Expand Down Expand Up @@ -82,6 +82,9 @@ Same type system as Java actions:
- Type parameter (generics): declare `EntityType: ENTITY <pEntity>`, then use the
bare `pEntity` for instance parameters
- Append `NOT NULL` to mark a parameter required.
- A `/** … */` doc comment **before** a parameter is its description; a trailing
`-- text` is an ordinary comment and is not stored. `RETURNS type AS $Name`
names the return value. Left out on `OR MODIFY`, both keep what is stored.

### Parameter names in the body

Expand Down
8 changes: 4 additions & 4 deletions .github/workflows/nightly.yml
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ jobs:

- uses: actions/setup-go@v7
with:
go-version: '1.27.2'
go-version: '1.27.3'

- name: Cache ANTLR4 JAR
uses: actions/cache@v6
Expand Down Expand Up @@ -89,7 +89,7 @@ jobs:

- uses: actions/setup-go@v7
with:
go-version: '1.27.2'
go-version: '1.27.3'

# mxbuild 9 draws through System.Drawing; without libgdiplus every mx call
# dies with "The type initializer for 'Gdip' threw an exception."
Expand Down Expand Up @@ -144,7 +144,7 @@ jobs:
- uses: actions/checkout@v7
- uses: actions/setup-go@v7
with:
go-version: '1.27.2'
go-version: '1.27.3'
- name: Cache ANTLR4 JAR
uses: actions/cache@v6
with:
Expand Down Expand Up @@ -175,7 +175,7 @@ jobs:

- uses: actions/setup-go@v7
with:
go-version: '1.27.2'
go-version: '1.27.3'

- uses: oven-sh/setup-bun@v2

Expand Down
8 changes: 4 additions & 4 deletions .github/workflows/push-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ jobs:
- uses: actions/checkout@v7
- uses: actions/setup-go@v7
with:
go-version: '1.27.2'
go-version: '1.27.3'
- name: Test the tunnel seam
shell: bash
# Scoped with -run to the seam's own tests. The full test binaries are still
Expand Down Expand Up @@ -78,7 +78,7 @@ jobs:
- uses: actions/checkout@v7
- uses: actions/setup-go@v7
with:
go-version: '1.27.2'
go-version: '1.27.3'
- name: Test the Windows process helpers
shell: bash
# Scoped with -run: the rest of the docker package's tests assert POSIX
Expand Down Expand Up @@ -122,7 +122,7 @@ jobs:
submodules: true
- uses: actions/setup-go@v7
with:
go-version: '1.27.2'
go-version: '1.27.3'
- name: Cache ANTLR4 JAR
uses: actions/cache@v6
with:
Expand Down Expand Up @@ -256,7 +256,7 @@ jobs:
submodules: ${{ matrix.suite == 'roundtrip' || matrix.suite == 'parity' }}
- uses: actions/setup-go@v7
with:
go-version: '1.27.2'
go-version: '1.27.3'
- name: Cache ANTLR4 JAR
uses: actions/cache@v6
with:
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ jobs:
fetch-depth: 0
- uses: actions/setup-go@v7
with:
go-version: '1.27.2'
go-version: '1.27.3'
- uses: oven-sh/setup-bun@v2
- name: Cache ANTLR4 JAR
uses: actions/cache@v6
Expand Down
6 changes: 3 additions & 3 deletions cmd/mxcli/syntax/features_integration.go
Original file line number Diff line number Diff line change
Expand Up @@ -646,8 +646,8 @@ DESCRIBE DATABASE CONNECTION Ops.Erp;`,
"create java action", "or modify java action", "type parameter", "entity parameter",
"exposed as", "returns", "generics", "drop java action",
},
Syntax: "CREATE [OR MODIFY] JAVA ACTION Module.Name[<T, ...>] [FOLDER 'path'](\n Param: Type [NOT NULL],\n EntityType: ENTITY <pEntity> NOT NULL,\n Obj: pEntity\n) RETURNS ReturnType\n[EXPOSED AS 'Label' IN 'Category'\n [ICON 'icon.png'] [ICON DARK 'icon-dark.png']\n [IMAGE 'image.png'] [IMAGE DARK 'image-dark.png']]\n[NOT EXPOSED]\nAS $$\n// Java code — AS $$ ... $$ is mandatory, cannot be omitted\n$$;\n\nOR MODIFY: updates signature/body in-place, preserves UUID.\n\nEXPOSED AS puts the action in Studio Pro's toolbox. The icon is a 64x64 PNG\nand the image a 256x192 PNG, read from disk relative to the .mdl file's own directory.\nAn OMITTED clause preserves what is stored — including bitmaps set in Studio\nPro — so removing an entry is NOT EXPOSED, and clearing one bitmap is\nDROP ICON / DROP IMAGE [DARK].\n\nType parameters: an ENTITY <T> parameter declares T. A type parameter no\nENTITY <T> parameter names is declared in the header, Module.Name<T>; an\nunqualified type that is neither is refused (MDL-JA01), since it would be\nwritten as an entity with no module (CE1613).",
Example: "mdl 1;\nCREATE JAVA ACTION Utils.FormatCurrency(\n Amount: Decimal NOT NULL\n) RETURNS String\nEXPOSED AS 'Format Currency' IN 'Formatting'\n ICON 'assets/currency-64.png'\n IMAGE 'assets/currency-256.png'\nAS $$\nreturn String.format(\"%.2f\", Amount);\n$$;\n\n-- Generic entity validator with type parameter\nCREATE JAVA ACTION Utils.IsValid(\n EntityType: ENTITY <pEntity> NOT NULL,\n Obj: pEntity NOT NULL\n) RETURNS Boolean\nAS $$\nreturn Obj != null;\n$$;\n\n-- Type parameter with no entity selector: the argument's entity binds it\nCREATE JAVA ACTION Utils.Describe<T>(\n Obj: T NOT NULL\n) RETURNS String\nAS $$\nreturn Obj.toString();\n$$;\n\n-- Idempotent update (preserves UUID)\nCREATE OR MODIFY JAVA ACTION Utils.FormatCurrency(\n Amount: Decimal NOT NULL,\n Decimals: Integer NOT NULL\n) RETURNS String\nAS $$\nreturn String.format(\"%.\" + Decimals + \"f\", Amount);\n$$;",
Syntax: "CREATE [OR MODIFY] JAVA ACTION Module.Name[<T, ...>] [FOLDER 'path'](\n [/** description */] Param: Type [NOT NULL],\n EntityType: ENTITY <pEntity> NOT NULL,\n Obj: pEntity\n) RETURNS ReturnType [AS $ReturnName]\n[EXPOSED AS 'Label' IN 'Category'\n [ICON 'icon.png'] [ICON DARK 'icon-dark.png']\n [IMAGE 'image.png'] [IMAGE DARK 'image-dark.png']]\n[NOT EXPOSED]\nAS $$\n// Java code — AS $$ ... $$ is mandatory, cannot be omitted\n$$;\n\nOR MODIFY: updates signature/body in-place, preserves UUID.\n\nEXPOSED AS puts the action in Studio Pro's toolbox. The icon is a 64x64 PNG\nand the image a 256x192 PNG, read from disk relative to the .mdl file's own directory.\nAn OMITTED clause preserves what is stored — including bitmaps set in Studio\nPro — so removing an entry is NOT EXPOSED, and clearing one bitmap is\nDROP ICON / DROP IMAGE [DARK].\n\nA parameter's description is a /** ... */ doc comment before it, as on an\nentity attribute; AS $ReturnName names the return value (the default name of\nthe variable a microflow call stores it in), as on a microflow. Leaving either\nout keeps what is stored; a new action gets no description and ReturnValueName.\n\nType parameters: an ENTITY <T> parameter declares T. A type parameter no\nENTITY <T> parameter names is declared in the header, Module.Name<T>; an\nunqualified type that is neither is refused (MDL-JA01), since it would be\nwritten as an entity with no module (CE1613).",
Example: "mdl 1;\nCREATE JAVA ACTION Utils.FormatCurrency(\n /** The amount to format. */\n Amount: Decimal NOT NULL\n) RETURNS String AS $FormattedAmount\nEXPOSED AS 'Format Currency' IN 'Formatting'\n ICON 'assets/currency-64.png'\n IMAGE 'assets/currency-256.png'\nAS $$\nreturn String.format(\"%.2f\", Amount);\n$$;\n\n-- Generic entity validator with type parameter\nCREATE JAVA ACTION Utils.IsValid(\n EntityType: ENTITY <pEntity> NOT NULL,\n Obj: pEntity NOT NULL\n) RETURNS Boolean\nAS $$\nreturn Obj != null;\n$$;\n\n-- Type parameter with no entity selector: the argument's entity binds it\nCREATE JAVA ACTION Utils.Describe<T>(\n Obj: T NOT NULL\n) RETURNS String\nAS $$\nreturn Obj.toString();\n$$;\n\n-- Idempotent update (preserves UUID)\nCREATE OR MODIFY JAVA ACTION Utils.FormatCurrency(\n Amount: Decimal NOT NULL,\n Decimals: Integer NOT NULL\n) RETURNS String\nAS $$\nreturn String.format(\"%.\" + Decimals + \"f\", Amount);\n$$;",
SeeAlso: []string{"java-action", "javascript-action"},
})

Expand All @@ -658,7 +658,7 @@ DESCRIBE DATABASE CONNECTION Ops.Erp;`,
"javascript action", "javascript", "js action", "call javascript",
"platform", "exposed as", "javascriptaction",
},
Syntax: "LIST JAVASCRIPT ACTIONS [IN Module];\nDESCRIBE JAVASCRIPT ACTION Module.Name;\nCREATE [OR MODIFY] JAVASCRIPT ACTION Module.Name [FOLDER 'path'](...) RETURNS Type [PLATFORM Web|Native|Hybrid|All] AS $$ ... $$;\nDROP JAVASCRIPT ACTION Module.Name;\n\nWrites the unit plus javascriptsource/<Module>/actions/<Name>.js. AS $$ ... $$ is mandatory. PLATFORM defaults to Web. The body sees each parameter with its first character lower-cased (ReadOnly is readOnly, URL is uRL), as mxbuild generates the signature.",
Syntax: "LIST JAVASCRIPT ACTIONS [IN Module];\nDESCRIBE JAVASCRIPT ACTION Module.Name;\nCREATE [OR MODIFY] JAVASCRIPT ACTION Module.Name [FOLDER 'path'](...) RETURNS Type [AS $ReturnName] [PLATFORM Web|Native|Hybrid|All] AS $$ ... $$;\nDROP JAVASCRIPT ACTION Module.Name;\n\nParameters, their /** ... */ descriptions and AS $ReturnName are written as for a Java action (see java-action.create).\n\nWrites the unit plus javascriptsource/<Module>/actions/<Name>.js. AS $$ ... $$ is mandatory. PLATFORM defaults to Web. The body sees each parameter with its first character lower-cased (ReadOnly is readOnly, URL is uRL), as mxbuild generates the signature.",
Example: "mdl 1;\nCREATE JAVASCRIPT ACTION Utils.IsStrictMode() RETURNS Boolean\nPLATFORM Web\nAS $$\nreturn Promise.resolve((function(){ return !this; })());\n$$;\n\n-- Exposed, native, with parameters\nCREATE JAVASCRIPT ACTION Utils.ShowToast(\n Message: String NOT NULL,\n Duration: Integer\n) RETURNS Boolean\nEXPOSED AS 'Show Toast' IN 'UI'\nPLATFORM Native\nAS $$\nreturn Promise.resolve(true);\n$$;",
SeeAlso: []string{"java-action"},
})
Expand Down
1 change: 1 addition & 0 deletions docs-site/src/appendixes/quick-reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -330,6 +330,7 @@ CREATE OR MODIFY NAVIGATION Responsive
| Describe Java action | `DESCRIBE JAVA ACTION Module.Name;` | Full MDL output with signature |
| Create Java action | `CREATE JAVA ACTION Module.Name(params) RETURNS type AS $$ ... $$;` | Inline Java code |
| Create with type params | `CREATE JAVA ACTION Module.Name(EntityType: ENTITY <pEntity>, Obj: pEntity) ...;` | Generic type parameters |
| Describe a parameter, name the return value | `CREATE JAVA ACTION Module.Name(/** The text. */ Input: String) RETURNS String AS $Result AS $$ ... $$;` | `/** … */` before a parameter is its description; `AS $Name` the return value's name |
| Create exposed action | `... EXPOSED AS 'Caption' IN 'Category' AS $$ ... $$;` | Toolbox-visible in Studio Pro |
| Drop Java action | `DROP JAVA ACTION Module.Name;` | Delete a Java action |
| Call from microflow | `$Result = CALL JAVA ACTION Module.Name(Param = value);` | Inside BEGIN...END |
Expand Down
Loading
Loading