Skip to content
Merged
6 changes: 6 additions & 0 deletions .claude/skills/fix-issue.md

Large diffs are not rendered by default.

44 changes: 38 additions & 6 deletions .claude/skills/mendix/write-microflows.md
Original file line number Diff line number Diff line change
Expand Up @@ -421,31 +421,63 @@ Use `case` when a microflow branches on an enumeration value.
case $Status
when Open, Pending then
return true;
when (empty) then
when Closed then
return false;
else
when (empty) then
return false;
end case;
```

`(empty)` represents an unset enumeration value. Multiple values can share one `when` branch by separating them with commas. Case values are bare identifiers — do **not** quote them.

> **Every value needs a branch, including `(empty)` — and there is no `else`.**
> A Mendix enum split is an exclusive split with one outgoing flow per condition
> value, so an uncovered value fails the build with **CE0079** *"The 'X' condition
> value should be configured in properties for an outgoing flow."* `mxcli check`
> reports a missing `(empty)` branch as **MDL056**, and an `else` branch as
> **MDL008** (an `else` does not stand in for the missing flows: mxbuild reports
> CE0079 for each uncovered value *and* CE0773 on the else flow itself).
>
> The `(empty)` branch is required **even when the attribute is `not null`** —
> verified on Mendix 11.6.6. If several values share a path, put them in one
> branch (`when Open, Pending then`) rather than reaching for `else`.

### Type Split And Cast Statements

Use `split type` when a microflow branches on an object's runtime specialization.
Use `cast` inside a type branch to create the specialized variable used by the branch body.

```mdl
declare $IsSpecialized boolean = false;
split type $Input
case Sample.SpecializedInput
cast $SpecificInput;
return true;
else
return false;
set $IsSpecialized = true;
case Sample.BaseInput
end split;
return $IsSpecialized;
```

`case` values are qualified entity names. The optional `else` branch handles objects that do not match any listed specialization.
`case` values are qualified entity names.

> **Every type needs a branch — including the base entity.** An object-type
> decision gets one outgoing flow per listed type, and a type with no flow fails
> the build with **CE0090** *"The 'X' value should be configured for an outgoing
> flow."* The base entity (the split variable's own type) counts: `case
> Sample.BaseInput` above is what covers "it is not any of the specializations".
>
> **`else` does not stand in for the base-type case.** It is accepted — it
> serializes as `Microflows$NoCase` — but it does not satisfy coverage, so
> `case Spec` + `else` still fails CE0090. Once every type has a branch, `else`
> is redundant. Verified on Mendix 11.6.6 and 11.13.0.
>
> **The split needs somewhere to go afterwards.** Branch bodies converge on a
> merge that continues to the microflow's end event, so a non-void microflow
> needs a `return` after `end split;` — otherwise `mxcli check` reports MDL003
> and the build fails **CE0067** *"The 'Return value' property is required."*
> Doing the per-branch work into a variable and returning it once (above) is the
> clearest shape; returning inside every branch also works, but still needs the
> trailing `return`.

**`cast` only stores the output variable.** Studio Pro persists Microflows$CastAction with a single `VariableName` field — the source variable is implicit (the type-split's input). Use `cast $SpecificName;` to give the specialized variable its name. The two-variable form `$Output = cast $Source;` parses but `$Source` is dropped on roundtrip; prefer the single-variable form.

Expand Down
18 changes: 15 additions & 3 deletions mdl-examples/bug-tests/365-microflow-inheritance-split.mdl
Original file line number Diff line number Diff line change
Expand Up @@ -10,17 +10,29 @@ create persistent entity InheritanceSplitExample.SpecializedInput extends Inheri
);
/

-- An object-type decision needs an outgoing flow for EVERY listed type,
-- including the base entity. This example used `case Specialized` + `else`,
-- which fails the build with CE0090 ("The 'InheritanceSplitExample.BaseInput'
-- value should be configured for an outgoing flow") — `else` serializes as
-- Microflows$NoCase and is accepted, but it does not satisfy coverage.
--
-- The branches also converge on a merge that continues to the end event, so a
-- non-void microflow needs a `return` after `end split;` (otherwise CE0067
-- "The 'Return value' property is required", and mxcli check reports MDL003).
--
-- Verified with mxbuild 11.6.6 and 11.13.0: 0 errors.
create microflow InheritanceSplitExample.RouteInput (
$Input: InheritanceSplitExample.BaseInput
)
returns boolean
begin
declare $IsSpecialized boolean = false;
split type $Input
case InheritanceSplitExample.SpecializedInput
cast $SpecializedInput;
return true;
else
return false;
set $IsSpecialized = true;
case InheritanceSplitExample.BaseInput
end split;
return $IsSpecialized;
end;
/
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,13 @@
-- Validation:
-- `mxcli check` parses the script.
-- `mx check` against the resulting MPR reports 0 errors.
--
-- The base-type case (`case BugTest475.Vehicle`) is required for that: an
-- object-type decision needs an outgoing flow for every listed type, and
-- without the base entity the build fails CE0090 regardless of this bug.
-- It is deliberately a TERMINATING branch — the scenario under test is
-- "exactly ONE non-split branch continues", and giving Vehicle a falling
-- -through body would make two branches continue and lose the regression.
-- Roundtrip (describe → exec → describe) preserves the structure
-- byte-for-byte: the post-split log activity stays outside both case
-- bodies.
Expand Down Expand Up @@ -66,6 +73,8 @@ begin
case BugTest475.Boat
log info node 'BugTest475' 'Dispatching boat';
return false;
case BugTest475.Vehicle
return false;
end split;
log info node 'BugTest475' 'Dispatched';
return true;
Expand Down
56 changes: 56 additions & 0 deletions mdl-examples/bug-tests/831-xpath-variable-traversal-ok.mdl
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
-- ============================================================================
-- Issue #831 — the forms MDL055 must NOT reject (positive half)
-- ============================================================================
--
-- The negative half is 831-xpath-variable-traversal.fail.mdl. This file pins
-- the other edge: the two restructurings MDL055's message recommends, plus the
-- one-hop forms that are valid XPath and must not be flagged.
--
-- Verified against mxbuild 11.6.6: this file builds with 0 errors.
-- ============================================================================

CREATE MODULE Bug831Ok;

CREATE OR MODIFY PERSISTENT ENTITY Bug831Ok.Category ( Name: String(50) );

CREATE OR MODIFY PERSISTENT ENTITY Bug831Ok.Product ( Code: String(50) );

CREATE OR MODIFY ASSOCIATION Bug831Ok.Product_Category
FROM Bug831Ok.Product TO Bug831Ok.Category TYPE Reference;

-- Recommended form 1: retrieve the associated object first (one hop is a legal
-- retrieve SOURCE), then constrain on that variable's own attribute.
CREATE OR MODIFY MICROFLOW Bug831Ok.Form1 ($RefProduct: Bug831Ok.Product)
RETURNS list of Bug831Ok.Category
BEGIN
retrieve $Related from $RefProduct/Bug831Ok.Product_Category;
retrieve $Categories from Bug831Ok.Category where [Name = $Related/Name];
return $Categories;
END;

-- Recommended form 2: invert the constraint so the traversal starts at the
-- entity being retrieved, which XPath does support.
CREATE OR MODIFY MICROFLOW Bug831Ok.Form2 ($RefProduct: Bug831Ok.Product)
RETURNS list of Bug831Ok.Category
BEGIN
retrieve $Categories from Bug831Ok.Category
where [Bug831Ok.Product_Category/Bug831Ok.Product = $RefProduct];
return $Categories;
END;

-- One hop off a variable is valid and must not be flagged: an attribute…
CREATE OR MODIFY MICROFLOW Bug831Ok.OneHopAttribute ($RefProduct: Bug831Ok.Product)
RETURNS list of Bug831Ok.Category
BEGIN
retrieve $Categories from Bug831Ok.Category where [Name = $RefProduct/Code];
return $Categories;
END;

-- …and the associated object itself.
CREATE OR MODIFY MICROFLOW Bug831Ok.OneHopAssociation ($RefProduct: Bug831Ok.Product)
RETURNS list of Bug831Ok.Product
BEGIN
retrieve $Products from Bug831Ok.Product
where [Bug831Ok.Product_Category = $RefProduct/Bug831Ok.Product_Category];
return $Products;
END;
44 changes: 44 additions & 0 deletions mdl-examples/bug-tests/831-xpath-variable-traversal.fail.mdl
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
-- ============================================================================
-- Issue #831 — RETRIEVE WHERE traversing an association from a variable
-- ============================================================================
--
-- NEGATIVE TEST (.fail.mdl): `mxcli check` MUST reject this file. An
-- unexpected pass means MDL055 has regressed.
--
-- `where [Name = $RefProduct/ZKT39.Product_Category/Name]` passed `mxcli check`
-- and `mxcli exec`, then the build failed:
--
-- [error] [CE0161] "Error(s) in XPath constraint."
--
-- Mendix XPath reaches at most ONE hop off a variable. The boundary is narrower
-- than "a qualified name after a variable" — verified against mxbuild 11.6.6:
--
-- $Var/Attr VALID the parameter's own attribute
-- $Var/Mod.Assoc VALID one hop, the associated object
-- $Var/Mod.Assoc/Attr CE0161 two or more hops
--
-- so the rule keys on hop count. A rule that flagged any qualified segment
-- would reject the middle form, which is valid.
--
-- There is no valid serialization of the two-hop form, which is why this is a
-- rejection and not a writer fix: the constraint has to be restructured, and
-- only the author knows which shape they meant. Both restructurings are in
-- 831-xpath-variable-traversal-ok.mdl, which must PASS.
-- ============================================================================

CREATE MODULE Bug831;

CREATE OR MODIFY PERSISTENT ENTITY Bug831.Category ( Name: String(50) );

CREATE OR MODIFY PERSISTENT ENTITY Bug831.Product ( Code: String(50) );

CREATE OR MODIFY ASSOCIATION Bug831.Product_Category
FROM Bug831.Product TO Bug831.Category TYPE Reference;

CREATE OR MODIFY MICROFLOW Bug831.ACT_Find ($RefProduct: Bug831.Product)
RETURNS list of Bug831.Category
BEGIN
retrieve $Categories from Bug831.Category
where [Name = $RefProduct/Bug831.Product_Category/Name];
return $Categories;
END;
24 changes: 24 additions & 0 deletions mdl-examples/bug-tests/832-npe-validation-rules-ok.mdl
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
-- ============================================================================
-- Issue #832 — the forms MDL054 must NOT reject (positive half)
-- ============================================================================
--
-- The negative half is 832-npe-validation-rules.fail.mdl. This file pins the
-- other edge: MDL054 must not fire on a validation rule that is legitimately
-- placed, or on a non-persistent entity that carries none.
--
-- Verified against mxbuild 11.6.6: this file builds with 0 errors.
-- ============================================================================

CREATE MODULE Bug832Ok;

-- A PERSISTENT entity is exactly where validation rules belong.
CREATE OR MODIFY PERSISTENT ENTITY Bug832Ok.P (
"Name": String(100) not null error 'Name is required',
"Code": String(50) unique error 'Code must be unique'
);

-- A non-persistent entity with no validation rule is fine.
CREATE OR MODIFY NON-PERSISTENT ENTITY Bug832Ok.NpPlain (
"Name": String(100),
"Qty": Integer
);
36 changes: 36 additions & 0 deletions mdl-examples/bug-tests/832-npe-validation-rules.fail.mdl
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
-- ============================================================================
-- Issue #832 — validation rules on a non-persistent entity were accepted
-- ============================================================================
--
-- NEGATIVE TEST (.fail.mdl): `mxcli check` MUST reject this file. An
-- unexpected pass means MDL054 has regressed.
--
-- Mendix refuses a validation rule on a non-persistable entity:
--
-- [error] [CE0070] "Validations rules are not allowed on entity 'X',
-- because it is not persistable."
--
-- `not null` and `unique` ARE validation rules — Studio Pro models "required"
-- and "uniqueness" as rules on the entity, not as column constraints — so both
-- are rejected on an NPE. `mxcli check` and `mxcli exec` both accepted them and
-- only a real build caught it, which is the worst place to find out.
--
-- Verified against mxbuild 11.6.6: `not null` with a message, `not null` bare,
-- and `unique` each produce CE0070; a plain attribute does not. The message is
-- optional and does not change the verdict.
--
-- The accepted counterparts — the same constraints on a PERSISTENT entity, and
-- an NPE with no constraint — are in 832-npe-validation-rules-ok.mdl, which
-- must PASS. Together they pin both edges of the rule.
--
-- Only the CREATE path can catch this: an `ALTER ENTITY … ADD ATTRIBUTE` does
-- not carry the entity's persistence kind, so it cannot be told apart from a
-- persistent entity without a project. Same limitation as MDL020.
-- ============================================================================

CREATE MODULE Bug832;

CREATE OR MODIFY NON-PERSISTENT ENTITY Bug832.Np (
"Name": String(100) not null error 'Name is required',
"Code": String(50) unique error 'Code must be unique'
);
59 changes: 59 additions & 0 deletions mdl-examples/bug-tests/mdl009-enum-split-empty-branch-ok.mdl
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
-- ============================================================================
-- MDL009 retired / MDL056 — the enum-split forms that must NOT be rejected
-- ============================================================================
--
-- The negative half is mdl009-enum-split-empty-branch.fail.mdl.
--
-- Verified against mxbuild 11.6.6: this file builds with 0 errors, including
-- the multi-value branch that the retired MDL009 used to reject.
-- ============================================================================

CREATE MODULE BugM9Ok;

CREATE ENUMERATION BugM9Ok.Status (
Open caption 'Open',
Pending caption 'Pending',
Closed caption 'Closed'
);

-- A multi-value branch is valid — this is what MDL009 wrongly rejected.
CREATE OR MODIFY MICROFLOW BugM9Ok.MultiValue ($Status: Enumeration(BugM9Ok.Status))
RETURNS Boolean
BEGIN
case $Status
when Open, Pending then
return true;
when Closed then
return false;
when (empty) then
return false;
end case;
END;

-- One value per branch is equally valid.
CREATE OR MODIFY MICROFLOW BugM9Ok.OnePerBranch ($Status: Enumeration(BugM9Ok.Status))
RETURNS Boolean
BEGIN
case $Status
when Open then
return true;
when Pending then
return true;
when Closed then
return false;
when (empty) then
return false;
end case;
END;

-- `(empty)` may share a branch with real values.
CREATE OR MODIFY MICROFLOW BugM9Ok.EmptySharesBranch ($Status: Enumeration(BugM9Ok.Status))
RETURNS Boolean
BEGIN
case $Status
when Open, Pending then
return true;
when Closed, (empty) then
return false;
end case;
END;
43 changes: 43 additions & 0 deletions mdl-examples/bug-tests/mdl009-enum-split-empty-branch.fail.mdl
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
-- ============================================================================
-- MDL009 retired, MDL056 added — enum split branch rules
-- ============================================================================
--
-- NEGATIVE TEST (.fail.mdl): `mxcli check` MUST reject this file.
--
-- MDL009 used to error on `when Open, Pending then`, claiming Mendix required
-- exactly one value per branch. That was wrong — verified on mxbuild 11.6.6, a
-- multi-value branch covering every value builds with 0 errors, and the shipped
-- write-microflows skill documents that form. The rule rejected valid MDL, so
-- it is retired.
--
-- What actually fails the build is a MISSING branch. An enum split is an
-- exclusive split needing one outgoing flow per condition value:
--
-- [error] [CE0079] "The '(empty)' condition value should be configured in
-- properties for an outgoing flow."
--
-- MDL056 catches the `(empty)` case, which is universal and needs no knowledge
-- of the enumeration's members — it holds even on a `not null` attribute.
--
-- The valid forms are in mdl009-enum-split-empty-branch-ok.mdl, which must PASS.
-- ============================================================================

CREATE MODULE BugM9;

CREATE ENUMERATION BugM9.Status (
Open caption 'Open',
Pending caption 'Pending',
Closed caption 'Closed'
);

-- REJECTED (MDL056): every value is covered, but `(empty)` is not.
CREATE OR MODIFY MICROFLOW BugM9.NoEmptyBranch ($Status: Enumeration(BugM9.Status))
RETURNS Boolean
BEGIN
case $Status
when Open, Pending then
return true;
when Closed then
return false;
end case;
END;
Loading
Loading