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
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ Sections dated before 2026-09-19 predate the cycle and stay as they are. Named m

## Unreleased

- learn(workflow, rest): **the v0.20-era workflow notes are marked superseded for mxcli v0.26.0:** `WAIT FOR NOTIFICATION` takes a name, `NOTIFY WORKFLOW` needs `TARGET Module.Workflow.Element` (CE0166 / MDL-WF16 without), mxcli writes the end-of-boundary-path marker itself so a boundary terminator is no longer a hand-add (the parallel-split terminator is a separate node and unchanged), and published REST `Authentication: (basic)` works (BUG-152 item 1: 401 / 401 / 200 proven, URL includes the resource segment). Sources: a five-app lab field run (Mendix 11.15, mxcli v0.26.0) and mxcli's public `write-workflows` skill and docs. `wf-add-path-terminators.py` already takes an `.mpr` since #294 (the reported crash was the older script). β€” TK-019 of a five-app lab report
- learn(workflow): **a "compute, then branch" pattern in `workflow-structure-rules.md`: a workflow decision reads a Boolean or enumeration on the context and cannot call a microflow, so the rule is computed by an earlier call microflow into a context attribute (TK-023).** The same paragraph names the three limits a field run hit (called microflow returns Boolean, enumeration or nothing; decision expressions on the context only; single-outcome user task holds no activities, MDL-WF02 / CE1876). The call-microflow return rule and the lone-outcome rule were already in the skill (Β§5, `learned-workflow-patterns.md` Β§6); the report's claim that an `exposed as workflow action` microflow cannot return a value was not adopted, because mxcli's own docs show one with `returns String`. β€” five-app lab report, TK-023
- learn(bug-ledger): **published REST answers 404, not 403, for a user without the allowed role** (TK-046, Mendix runtime behaviour; write 404 into the contract) β€” a five-app lab project
- learn(bug-ledger): **the Business Events consumer silently drops a record whose ce_id is over 36 characters** (TK-045, Business Events module owner; use a UUID) β€” a five-app lab project
- learn(testing): **`mxcli test --local` takes `--app-port` / `--admin-port` from v0.27.0, so it no longer refuses while another app holds 8081** (TK-044, fixed upstream; ledger only, the skill line waits on an A/B) β€” a five-app lab project
Expand Down
3 changes: 2 additions & 1 deletion bug-logs/mxcli-bugs.md
Original file line number Diff line number Diff line change
Expand Up @@ -5935,7 +5935,8 @@ list is empty. The endpoint therefore receives the characters `$Var`.
service and its callers.
**Reproducible:** yes, on scratch copies (`describe` read-back, BSON reads, lint deltas).

1. **No authentication clause.** `create published rest service` has none. An `Authentication:`
1. **SUPERSEDED on v0.26.0 (Mendix 11.15.0): `Authentication: (basic)` works.** A five-app lab run: `create or modify published rest service ... ( Path: '...', Version: '1.0.0', ServiceName: '...', Authentication: (basic) ) { resource 'r' { get 'ping' microflow M.F; } };` plus `grant access on published rest service M.S to M.Role;` and `grant execute on microflow M.F to M.Role;` answered 401 without credentials, 401 with a wrong password, 200 with the right one. The operation URL is service path + resource name + operation path (without the resource segment: 404). Upstream lists `basic`, `session`, `microflow M.F` and `none`. The v0.24.0 finding follows.
**No authentication clause (v0.24.0).** `create published rest service` has none. An `Authentication:`
property passes `check` and is silently ignored: the service is written without auth, and
every call reaches the operation microflow unauthenticated. `describe` shows no auth.
Authenticate inside the operation microflow instead. Then prove it at runtime: no credentials
Expand Down
8 changes: 5 additions & 3 deletions skills/build/workflow-example.mdl
Original file line number Diff line number Diff line change
Expand Up @@ -348,7 +348,7 @@ END;
-- OPEN WORKFLOW $Wf;
-- OPEN USER TASK $Task;
-- LOCK WORKFLOW $Wf; UNLOCK WORKFLOW $Wf; UNLOCK WORKFLOW ALL;
-- $R = NOTIFY WORKFLOW $Wf;
-- $R = NOTIFY WORKFLOW $Wf TARGET Mod.Wf.waitDone; -- v0.26.0: TARGET required (CE0166)
-- $Wfs = GET WORKFLOWS FOR $Ctx;
-- $D = GET WORKFLOW DATA $Wf AS Mod."Entity";
-- $Recs = GET WORKFLOW ACTIVITY RECORDS $Wf;
Expand Down Expand Up @@ -432,10 +432,12 @@ END;
-- };

-- 4. WAIT FOR TIMER / WAIT FOR NOTIFICATION.
-- WAIT FOR NOTIFICATION takes NO name -- the name is a Studio Pro property.
-- v0.26.0: WAIT FOR NOTIFICATION takes a name (the bare form still parses);
-- a NOTIFY WORKFLOW needs TARGET Module.Workflow.<name>. On v0.20.0 the name
-- was a parse error.
--
-- WAIT FOR TIMER 'addDays([%CurrentDateTime%], 1)';
-- WAIT FOR NOTIFICATION;
-- WAIT FOR NOTIFICATION waitDone;

-- 5. TARGETING XPATH, token form -- now proven alongside the three-segment
-- path form. Double the inner quotes.
Expand Down
20 changes: 16 additions & 4 deletions skills/learned-workflow-patterns.md
Original file line number Diff line number Diff line change
Expand Up @@ -309,10 +309,12 @@ USER TASK "Review" 'Review'
CALL MICROFLOW MyModule."SUB_Remind" WITH ("Request" = '$WorkflowContext');
};

-- wait for timer / wait for notification. WAIT FOR NOTIFICATION takes NO name --
-- 'WAIT FOR NOTIFICATION ''Ping'';' is a parse error; the name is a Studio Pro property.
-- wait for timer / wait for notification.
-- SUPERSEDED on v0.26.0: WAIT FOR NOTIFICATION takes a name (`wait for notification waitDone;`),
-- and a NOTIFY WORKFLOW that targets it needs `TARGET Module.Workflow.waitDone`. On v0.20.0
-- the named form was a parse error; the bare form still parses on v0.26.0. Name every wait.
WAIT FOR TIMER 'addDays([%CurrentDateTime%], 1)';
WAIT FOR NOTIFICATION;
WAIT FOR NOTIFICATION waitDone;
```

**The two signatures a workflow's own gates check and `mxcli check` does not** (v0.21.0,
Expand Down Expand Up @@ -445,7 +447,7 @@ manual Studio Pro steps built around a blocker that never existed.
| Open an instance | `OPEN WORKFLOW $Wf;` (client action) |
| Open a task | `OPEN USER TASK $Task;` (client action) |
| Lock / unlock | `LOCK WORKFLOW $Wf;` Β· `UNLOCK WORKFLOW $Wf;` Β· `LOCK WORKFLOW ALL;` Β· `UNLOCK WORKFLOW ALL;` |
| Release a wait | `$R = NOTIFY WORKFLOW $Wf;` (releases a *wait for notification* activity) |
| Release a wait | `$R = NOTIFY WORKFLOW $Wf TARGET Module.Workflow.waitDone;` (releases a *wait for notification* activity; on mxcli v0.26.0 `TARGET` is required, without it `check` refuses: CE0166 / MDL-WF16. The target may be a named wait, a notification activity or a notification boundary event.) |
| Instances for a context object | `$Wfs = GET WORKFLOWS FOR $Ctx;` |
| Instance metadata | `$D = GET WORKFLOW DATA $Wf AS Mod."Entity";` |
| Audit trail | `$Recs = GET WORKFLOW ACTIVITY RECORDS $Wf;` |
Expand Down Expand Up @@ -1013,6 +1015,16 @@ already exists.

## 19. `BOUNDARY EVENT TIMER` β€” don't add one without a documented business trigger

> **SUPERSEDED on mxcli v0.26.0 (Mendix 11.15.0, 2026-10-15): the boundary terminator is no longer
> a hand-add.** mxcli writes the end-of-boundary-path marker itself
> (`Workflows$EndOfBoundaryEventPathActivity`), so a boundary path may end in a plain
> `call microflow`; the notification boundary event exists too (`boundary event interrupting
> notification Name 'Caption' { ... }`). Source: mxcli's own `write-workflows` skill and
> `docs-site/src/language/workflow-activities.md`, and a five-app lab field run. A boundary
> terminator pass is not needed; the **parallel split** terminator (Β§18, Β§23) is a separate
> node and this note does not change it (not re-probed on v0.26.0). Re-test the notes below
> against your pinned mxcli before relying on them.

> **CORRECTION, 2026-09-03 (probed, mxcli v0.20.0 / Mendix 11.14.0).** The `'P3D'` form below
> is **wrong** and was recorded here as proven on a `mxcli check` alone. The timer takes a
> Mendix **expression**, not an ISO 8601 period: `'P3D'` passes `mxcli check` and `exec`, then
Expand Down
8 changes: 5 additions & 3 deletions skills/rest-integration-first-time-right.md
Original file line number Diff line number Diff line change
Expand Up @@ -297,9 +297,11 @@ association direction; all fine, page empty β†’ stale deployment or a different
green. Write `body '{1}' with ({1} = $Var)` (BUG-151).
- **`import from mapping M($Call/ResponseBody)` is a parse error.** Declare a String first:
`declare $Body String = $Call/ResponseBody;` then `M($Body)` (BUG-152).
- **A published REST service has no auth clause**, and an `Authentication:` property is
silently ignored. Authenticate inside the operation microflow, and prove that no credentials
and a wrong password each answer 401 (BUG-152).
- **A published REST service has no auth clause on v0.24.0**, and an `Authentication:` property is
silently ignored there. **On v0.26.0 `Authentication: (basic)` works** (also `session`,
`microflow M.F`, `none`): grant the role access to the service and execute on each backing
microflow, and note the URL includes the resource segment. On either version, prove that no
credentials and a wrong password each answer 401 (BUG-152).
- **`create or modify published rest service` replaces the resource list.** One script owns
the service and names every resource (BUG-152).
- **Extend an import mapping with `create or modify`, never drop and recreate it.** The drop
Expand Down
29 changes: 25 additions & 4 deletions skills/workflow-structure-rules.md
Original file line number Diff line number Diff line change
Expand Up @@ -272,6 +272,27 @@ Anything genuinely needing `$currentUser` is a *call microflow*, which has it
`learned-workflow-patterns.md` Β§15 adds the mxcli-side limit: no bracketed association filter in
a decision expression β€” that is a microflow with a `RETRIEVE`.

**Compute, then branch.** A *decision* reads an expression on the context; it cannot call a
microflow. When the rule needs logic (a threshold, a lookup, a count), do not wrap it in a
microflow the decision calls. Write the result to a **Boolean (or enumeration) attribute on the
context entity** in a *call microflow* that runs earlier in the flow, then branch on it:

```
call microflow Mod.ACT_Review_Evaluate with ("Case" = '$WorkflowContext'); -- sets Case/NeedsSecond
decision decSecond '$WorkflowContext/NeedsSecond'
outcomes true -> { user task ... } false -> { }; -- Boolean keyword form proven in Β§11 (BUG-76)
```

Three limits the same field run hit, none of which `check` explains well (mxcli v0.26.0, Mendix
11.15, one five-app lab project): (a) a microflow *called from a workflow* returns **Boolean, an
enumeration or nothing** (`learned-workflow-patterns.md` Β§6), so hand a computed value back by
storing it on the context entity, not through a `String` or entity return; (b) a decision's
condition is a Boolean or enumeration expression on `$WorkflowContext` or `$WorkflowInstance`
only, hence the stored attribute above; (c) a user task with a single outcome holds no
activities in that outcome (`MDL-WF02` / CE1876): put the call **after** the task (Β§5). A
"compute then branch" call microflow also needs its result attribute to exist before the
workflow script runs, so the entity script comes first.

## 9. Versioning β€” which edits break running instances

Relevant the moment a workflow is redeployed with live cases (migration cutover, any Stage 6+
Expand Down Expand Up @@ -325,7 +346,7 @@ row's stated floor is evidence against it.
| **a targeting microflow's signature** | **it must accept TWO parameters β€” `System.Workflow` AND the context entity β€” in that order.** One parameter is the natural thing to write and it is wrong: *"should accept parameters of type 'System.Workflow' and 'MOC.MOCProject'. Instead the selected microflow expects 'MOC.MOCProject'"*. Again clean through `mxcli check`, caught only by the native build. Neither this signature nor the page's appears in `mxcli syntax workflow.user-task.targeting`, whose own example is `TARGETING MICROFLOW HR.GetApprovers` with no signature stated. Measured v0.21.0, 2026-09-09 |
| **multi-user task** β€” the activity itself | **proven**. Undocumented in `mxcli syntax workflow`; works anyway |
| **`JUMP TO <activity>`** inside a user-task outcome | **proven** |
| **`WAIT FOR TIMER '<expression>'`** and **`WAIT FOR NOTIFICATION;`** | **proven**. Both undocumented; the notification takes **no name** β€” that is a Studio Pro property |
| **`WAIT FOR TIMER '<expression>'`** and **`WAIT FOR NOTIFICATION;`** | **proven**. Both undocumented. **v0.26.0: the notification takes a name** (`wait for notification waitDone;`, the bare form still parses) and `NOTIFY WORKFLOW` needs `TARGET Module.Workflow.waitDone` (CE0166 / MDL-WF16 without). On v0.20.0 the name was a parse error |
| **boundary event timer, non-interrupting** | **proven**, with an **expression**, not an ISO period |
| ~~decision on a **boolean or free-text** outcome~~ | **RETRACTED 2026-09-03 β€” see the CORRUPTING row below.** This row read "proven on mxcli β‰₯ v0.18.0" and it was wrong: BUG-76's v0.20.0 retest corrupts on a condition of literal `1 = 1`. The condition never mattered; the defect is in how the *outcome label* is written. Left visible with a strikethrough rather than deleted, because "a boolean decision is the safe kind" is the belief this table has to actively kill |
| call microflow, with or without parameters | **proven** β€” with two limits. (1) The `WITH` clause's **value must be quoted**: `WITH ("Ctx" = '$WorkflowContext')`. Unquoted (`= $WorkflowContext`) segfaults the binary, BUG-107. (2) **`$WorkflowUserTask` is NOT in scope here** β€” only `$WorkflowContext` and `$WorkflowInstance` are. Passing it is a **CE0117** that `mxcli check --references` passes completely clean; only mxbuild catches it. A microflow that needs the task looks it up by name off `$WorkflowInstance` through `System.WorkflowEndedUserTask`. Proven by sandbox A/B on a full project copy, 2026-09-04 β€” this corrects an earlier reading of Β§8 that treated *call microflow* as having the task |
Expand All @@ -335,10 +356,10 @@ row's stated floor is evidence against it.
| **parallel split**, incl. **nested** | **proven on v0.21.0 β€” with one mandatory post-exec step**, which is the whole of Β§4's warning above and `learned-workflow-patterns.md` Β§18. The structure writes correctly at any depth; the `EndOfParallelSplitPathActivity` that closes each path does not, and MDL has no keyword for it. Add it with `bin/wf-add-path-terminators.py <unit.mxunit> --apply` after this script **and after every later script that rewrites the workflow** (Β§23), then verify with a live run, never a gate. BUG-121; field-proven 8-of-16 β†’ **16-of-16 stations, 6 concurrent**. This row read RETRACTED until 2026-09-14 |
| **forward `JUMP TO`** (target later in the flow than the outcome jumping to it) | **proven on v0.24.0 / Mendix 11.13.0 β€” this row taught CE6681 until 2026-09-26, and direction was never the platform rule.** Re-probed as an intended known-bad control (a card-disbursement requirements-driven build): `check --references`, `exec` and native `mx check` all 0 errors, and `DESCRIBE` reads the jump back with its real target. The CE6681 recorded here came from mxcli's jump-named-after-its-target defect, for which forward order was the broken case; upstream `825873d6` fixed it in v0.21.0 (`learned-workflow-patterns.md` Β§25). On a binary older than v0.21.0, jump backwards or hand-add. A CE6681 on a jump now means the target does not resolve β€” see the reject-path paragraph below the table |
| **boundary event timer, interrupting** | **proven on v0.24.0 / Mendix 11.13.0** (2026-09-26, a card-disbursement requirements-driven build): a body that calls a microflow and then ends in `end workflow comment '…';`, or in `jump to <earlier activity>;`, passes check, exec and native `mx check` at 0 errors, and the read-back shows a real jump with a real target β€” BUG-109 no longer reproduces. The path must still end in *End* or *Jump* (CE0105 on an empty body), and Β§2's one-interrupting-boundary rule applies. **Before v0.24.0: hand-add** β€” `end workflow` did not parse before the post-v0.22.0 nightly, and `JUMP TO` there was BUG-109 through v0.20.0 |
| **boundary event on notification** | **hand-add** β€” the grammar admits `{TIMER, INTERRUPTING, NON}` only |
| **boundary event on notification** | **hand-add before v0.26.0** β€” the grammar admitted `{TIMER, INTERRUPTING, NON}` only. **v0.26.0: `boundary event interrupting notification` exists** (`learned-workflow-patterns.md` Β§19) |
| **event sub-process** (all four start kinds), recurrence | **hand-add** β€” no construct in the grammar, in any position |
| **multi-user decision method / completion timing** | **hand-add** β€” the activity is scriptable, its decision rule is not |
| **explicit `END WORKFLOW`, end-of-parallel-split-path, end-of-boundary-event-path** | **not expressible β€” and the "not needed" half of this row was wrong, corrected 2026-09-14.** MDL terminates by nesting and `mx check` accepts it, so the gap is invisible to every gate; the **runtime is not indifferent**. A parallel-split path with no terminator opens no task at all (BUG-121, repaired post-exec by `bin/wf-add-path-terminators.py`), and an empty outcome block meant to stop the instance rejoins the enclosing flow instead (Β§21 gap 2). One grammar gap, two silent runtime faults. Still true: **CE1844 cannot be triggered from MDL** β€” Β§4 governs the diagram and anything hand-added, not the script |
| **explicit `END WORKFLOW`, end-of-parallel-split-path, end-of-boundary-event-path** | **not expressible β€” and the "not needed" half of this row was wrong, corrected 2026-09-14.** MDL terminates by nesting and `mx check` accepts it, so the gap is invisible to every gate; the **runtime is not indifferent**. A parallel-split path with no terminator opens no task at all (BUG-121, repaired post-exec by `bin/wf-add-path-terminators.py`), and an empty outcome block meant to stop the instance rejoins the enclosing flow instead (Β§21 gap 2). One grammar gap, two silent runtime faults. **v0.26.0: the end-of-boundary-event-path marker is now written by mxcli itself; the parallel-split terminator is not re-probed.** Still true: **CE1844 cannot be triggered from MDL** β€” Β§4 governs the diagram and anything hand-added, not the script |
| **user-task `onCreatedEvent`** (the *On created* handler β€” the mechanism Β§6 names for assignment carried in data) | **not expressible in MDL, and this is grammar-level, not a docs gap.** The parser enumerates its own alternatives: after `PARAMETER` it accepts only `{BEGIN, EXPORT, DUE, OVERVIEW, DESCRIPTION, DISPLAY}`, and after a user task's `PAGE` only `;`. Four spellings probed (`ON CREATED CALL MICROFLOW`, `ON WORKFLOW EVENT`, task-level `ON CREATED`, `ONCREATEDEVENT`), all rejected at parse. **But it IS on the MCP write path** β€” `mxcli`'s `CreateWorkflow`/`UpdateWorkflow` payload carries `json:"onCreatedEvent"` on the *user-task* struct, beside `taskPage`, `outcomes` and `boundaryEvents`, and without `omitempty`. So it is a **per-task** property, not one workflow-level handler. End-to-end MCP write **not verified** β€” that needs a live Studio Pro. Treat as: hand-add in Studio Pro |
| **AI agent task activity** | **unprobed** β€” its model rules (companion microflow first, outcomes mirror its return values, Boolean/Enum/Void only) hold whichever tool writes it |

Expand Down Expand Up @@ -474,7 +495,7 @@ between people, this decides how many workflows exist and who each task targets.
| Multi-instance parallel user task | **Native** β€” Multi-User Task | proven; decision method + completion timing are a hand-add (Β§7) |
| Service / script / business-rule / manual task | **Native or workaround** β€” Call Microflow | proven; quote the `WITH` value |
| Send task | **Workaround** β€” Call Microflow + Notify Workflow | proven |
| Receive task | **Native** β€” Wait for Notification | proven; takes no name |
| Receive task | **Native** β€” Wait for Notification | proven; v0.26.0 takes a name, and NOTIFY needs `TARGET` |
| Call activity / reusable subprocess | **Native** β€” Call Workflow | proven |
| Event sub-process | **Native** | **hand-add** β€” no MDL construct, in any position (Β§3, Β§11) |
| Timer: intermediate catch, boundary interrupting/non-interrupting | **Native** | non-interrupting proven; interrupting proven on v0.24.0, **a hand-add on older binaries** (Β§2, Β§11) |
Expand Down
Loading