From 460f3ec177413f6ba8e18384b79e5c4e7d9afef5 Mon Sep 17 00:00:00 2001 From: MendixMau Date: Thu, 15 Oct 2026 22:59:04 +0800 Subject: [PATCH 1/3] learn(workflow skills): a jump has one fixed target; three probed workflow build errors MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit workflow-structure-rules.md §1: a Jump to cannot return to the step that sent it, so one failure handler for N steps is a child workflow reached through Call workflow (the child ends normally, failure is data, a parent jump aborts a running child). A five-app lab project (Mendix 11.15, mxcli v0.26.0) drew the shared-node form and had to redesign at Stage 3. learned-workflow-patterns.md §9: three rows probed tonight with native mx check on mxcli v0.26.0 / Mendix 11.15.0: - CE0117 on a System.Workflow argument: map `$WorkflowInstance` (builds clean); `$Workflow` passes check and fails the build. - CE1613 at Call workflow: the called workflow's parameter must be `$WorkflowContext`; describe drops the mapping. - `returns` goes before `exposed as workflow action`; a called microflow can return Boolean/enum. Corrects the reading that it cannot. Co-Authored-By: Claude Opus 5.5 --- CHANGELOG.md | 1 + skills/learned-workflow-patterns.md | 3 +++ skills/workflow-structure-rules.md | 21 +++++++++++++++++++++ 3 files changed, 25 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6e238acd..5f41612e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,6 +19,7 @@ Sections dated before 2026-09-19 predate the cycle and stay as they are. Named m ## Unreleased +- learn(workflow skills): **a Jump has one fixed target, so one failure handler for N steps is a child workflow, not a shared node; plus three probed workflow errors (CE0117 on a `System.Workflow` argument means map `$WorkflowInstance`, CE1613 means the called workflow's parameter must be `$WorkflowContext`, and `returns` goes before `exposed as workflow action`).** `workflow-structure-rules.md` §1 and `learned-workflow-patterns.md` §9. The §9 rows were probed on mxcli v0.26.0 / Mendix 11.15.0 with native `mx check`. The third row corrects the reading that a called microflow cannot return a value. Source: a five-app lab project (Mendix 11.15, mxcli v0.26.0) - new(mxpatch, exec): **`project-bin/mxpatch.py`, the shared patch core, and a `mxcli fix hashes` check around every `exec.sh --patch` run: the two shipped workflow patchers no longer leave a stale ContentsHash.** mxpatch finds a unit by type + name (+ module) with a byte pre-filter, finds an element by Name, gets/sets a value by key, and writes the unit and its `.mpr` ContentsHash (base64 SHA-256 of the file) in one step; dry run by default, `already applied` on a re-run, and a refusal (exit 3) when the current value is not the expected old one. `wf-set-call-captions.py` and `wf-add-path-terminators.py` now write through it, refuse drift (a caption is replaced only while it is still the stamped microflow name or the `--expect-caption` value; `--expect-paths N` for terminators), and also take the `exec.sh --patch` form (` Module.Workflow`). The index is committed before the file swap, so a kill mid-write leaves a MISMATCH that `fix hashes` reports and a re-run repairs. `mxunit_bson.py` reads BSON 0x09 (UTC datetime). `exec.sh --patch` runs `mxcli fix hashes` (v0.25.0+, probed via `fix --help`) before and after the script and restores the snapshot on any issue the baseline did not have, before mxbuild, which cannot see it. Field evidence: captured mxcli v0.25.0/v0.26.0 runs on a public model (`tests/wave2/fixtures/mxpatch/CAPTURE.txt`): master's patchers leave a MISMATCH, the ported ones verify clean; the hash routine agrees with mxcli on 67 of 67 mismatched and 302 matching units of a second model. Widget-shape copying and pluggable-widget properties wait on a captured page unit. Field run ([#390](https://github.com/MendixMau/mxcli-project-toolkit/issues/390), mxcli v0.26.0, macOS): PASS, and the patch repaired a ContentsHash MISMATCH an old-style `--patch` had left on the unit it rewrote. Fixes from it: the backup of a rewritten unit is kept outside `mprcontents/` (in `$MXPATCH_BACKUP_DIR`, which `exec.sh --patch` points into the snapshot it just took, else a 0700 `/mxpatch-bak-/`; refused when it resolves inside `mprcontents/`, through a symlink or another case spelling; named with pid and counter so two writes in one second keep both), no longer as an untracked `.mxunit.bak` that `git add mprcontents` would commit; the BUILD-LOG row carries the hash bracket (`hashes: baseline 1 mismatch (pre-existing), verify clean (1 pre-existing repaired by this patch's rewrite)`); and the baseline message says that a unit the patch rewrites gets a fresh hash, and to repair the others with `fix hashes --repair`. A baseline MISMATCH that is still reported on a unit the patch rewrote counts as new (restore, exit 1), not as pre-existing. — jopterhorst ([#294](https://github.com/MendixMau/mxcli-project-toolkit/issues/294), [#295](https://github.com/MendixMau/mxcli-project-toolkit/issues/295)) - learn(pipelines/mendix-mpr/samples): **three minimal mxcli repros from the native-module bug hunt, the files the upstream draft cites** (split from [#293](https://github.com/MendixMau/mxcli-project-toolkit/pull/293), 6/6). `fsm-template-9.24.2/native-bughunt/` holds CAPTURE.txt and D1–D3, which are written from scratch in a `Repro` module: `describe entity` printing `index ( desc);`, the native Repeater `content` slot, and no flow writable to a 9.24.2 project. They are the repros for U3, U2 and U14 in `bug-logs/pending-github-issues/2026-10-12-mxcli-0.26-round-trip-report.md`. The other 472 sample files of #293 had no citing reader, because the skills and Rehost splits state their field results inline, so none of them ships. — MendixMau, Decompose/Rehost lab runs 2026-10-12 - learn(inbox, pending-github-issues): **ten field-note drops from the Decompose/Rehost lab runs, and one not-filed draft of 53 mxcli 0.26 defects for mendixlabs/mxcli** (split from [#293](https://github.com/MendixMau/mxcli-project-toolkit/pull/293), 5/6). `contrib/inbox/2026-10-1{2,3}-*.md`: increments 1–8 of the split-a-monolith runs on a public Marketplace template (FSM 9.24.2), the native-module bug hunt, and the GenAI Showcase regeneration (0.26.0 and the 2026-10-13 nightly); each now has the template's front matter and is at most 600 words. `bug-logs/pending-github-issues/2026-10-12-mxcli-0.26-round-trip-report.md`: the U1–U53 table the inbox drops refer to, marked DRAFT — NOT FILED, to be split into one issue per defect and retested before anything is posted. No BUG numbers assigned; triage promotes. — MendixMau, Decompose/Rehost lab runs 2026-10-12/13 diff --git a/skills/learned-workflow-patterns.md b/skills/learned-workflow-patterns.md index ea88c211..011f1045 100644 --- a/skills/learned-workflow-patterns.md +++ b/skills/learned-workflow-patterns.md @@ -651,6 +651,9 @@ varying **only** the suspected cause — see [[sandbox-ab-tool-defect-probe]]. | **CE0053 / MDL040** | `DECLARE $L List of Mod.Entity = empty;` | Use `$L = CREATE LIST OF Mod."Entity";` | | **CE0108** | A `RETRIEVE` inside an `IF` shadows the named return variable, leaving it out of scope on the `ELSE` path | Use early `RETURN empty;` on failure branches so only the success path reaches the named-return retrieve | | **MDL-WF04** | Standalone `ANNOTATION` in a workflow activity flow (produces an unloadable model) | Use a plain MDL comment | +| **CE0117** at a *Call microflow* whose microflow takes a `System.Workflow` parameter | You passed a variable the workflow does not have (`Workflow = $Workflow`). `check --references` passes it. Only native `mx check` refuses it (mxcli v0.26.0, Mendix 11.15.0, probed 2026-10-15) | Map it to `$WorkflowInstance`: `call microflow M.ACT_X(Workflow = $WorkflowInstance, Ctx = $WorkflowContext)`, which builds clean (same probe). Don't look the instance up through `System.Workflow_ParentWorkflow` instead. That lookup has to guess when more than one child is in progress | +| **CE1613** "The selected parameter '…' no longer exists" at a *Call workflow* | The called workflow's parameter is named something other than `$WorkflowContext` (`parameter $Task: …` with `(Task = …)`). `check --references` passes it. `describe workflow` then drops the mapping (mxcli v0.26.0, probed 2026-10-15) | Every workflow that is called declares `parameter $WorkflowContext: …`, and the call maps `(WorkflowContext = …)`. Keep the `.mdl` file as the source; don't round-trip the call through `describe` | +| parse error `mismatched input 'returns' expecting BEGIN` on a workflow call microflow | `exposed as workflow action …` written before `returns …` | Write `returns Boolean as $Ok` first, then `folder` and `exposed as workflow action`. A called microflow *can* return a value: a Boolean gives the activity `true`/`false` outcomes and an enumeration one outcome per value plus empty (Mendix docs, *Call microflow*). "Exposed call microflows return nothing" is a wrong reading of this parse error (probed 2026-10-15) | | **MPR006** | Empty container — compiles, crashes at runtime | Give every container content, or drop it | | **MPR010** | Widgets not wrapped in a `LAYOUTGRID` | Wrap them | | Workflow stuck | No `complete_task` button for one of the task's outcomes | Add the missing outcome button | diff --git a/skills/workflow-structure-rules.md b/skills/workflow-structure-rules.md index a265c7cd..bff29d42 100644 --- a/skills/workflow-structure-rules.md +++ b/skills/workflow-structure-rules.md @@ -55,6 +55,27 @@ Two failures this prevents, both invisible to `mxcli check`: The main flow is `Start → … → End`. New activities go after the start and before the end (or before a *Jump*, when the main flow ends on one). +**A *Jump to* has one fixed target. It cannot return to whichever step sent it there.** So "every +failing step goes to one shared failure node, then retries the step that failed" is not a +workflow you can draw: the retry needs a different target for each step, and there is no +"return to caller". A five-app lab project (Mendix 11.15, mxcli v0.26.0) drew exactly that for +seven calls. The Stage 3 count (§12) refused it, and the redesign cost a resolution round plus a +resync of the blueprint and three BRDs. When one failure handler must serve N steps: + +- put the step and its retry or attention loop in a **child workflow** that each step reaches + through *Call workflow*. The child holds one handler, and its backward jumps stay inside the + child, so every target is fixed; +- the child ends **normally** and writes its result on its own context (an outcome attribute). + The parent reads it in a decision after the call. A sub-workflow returns nothing, and if it + *fails* or is aborted, the parent fails too (Mendix docs, *Call workflow*). Failure must + therefore be data, never an error; +- a jump in the parent aborts a running child (same page). Keep jumps in the parent to the + points where no child is running. + +The alternative, one copied tail per step, multiplied a 7-call parent to 21 calls and 43 +decisions in that project's count. Count the child design and the copied-tail design (§12) +before you choose. + ## 2. Boundary events — type decides the terminator A boundary event hangs on one parent activity. Only these can carry one: *wait for From 82a403e435fc44e361fc011408855a611b3431b0 Mon Sep 17 00:00:00 2001 From: MendixMau Date: Fri, 16 Oct 2026 10:26:33 +0800 Subject: [PATCH 2/3] learn(fixture-seeding): resetting workflow data, runtime abort first, link tables first if SQL (WF-5) Co-Authored-By: Claude Opus 5.5 --- CHANGELOG.md | 1 + skills/fixture-seeding.md | 9 +++++++++ 2 files changed, 10 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index beb698a1..12733fc6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,6 +20,7 @@ Sections dated before 2026-09-19 predate the cycle and stay as they are. Named m ## Unreleased - learn(workflow skills): **a Jump has one fixed target, so one failure handler for N steps is a child workflow, not a shared node; plus three probed workflow errors (CE0117 on a `System.Workflow` argument means map `$WorkflowInstance`, CE1613 means the called workflow's parameter must be `$WorkflowContext`, and `returns` goes before `exposed as workflow action`).** `workflow-structure-rules.md` §1 and `learned-workflow-patterns.md` §9. The §9 rows were probed on mxcli v0.26.0 / Mendix 11.15.0 with native `mx check`. The third row corrects the reading that a called microflow cannot return a value. Source: a five-app lab project (Mendix 11.15, mxcli v0.26.0) +- learn(fixture-seeding): **resetting workflow data: abort instances through the runtime (Workflow Commons admin or *Change workflow state*); if SQL is unavoidable, delete the link tables first (`workflow_currentactivity`, `workflow_workflowdefinition`, `usertask_assignees`, `usertask_targets`, `usertask_targetgroups`), and reset a context object only after its instances are gone.** A seed reset that deleted workflow rows by SQL failed on those links (WF-5). Text only, `ondemand` skill. — five-app lab report, WF-5 - fix(app-restart): **a secret with spaces in quotes is masked whole, and `Pwd` and `Credential` join the secret words.** Follow-up to #403: `--constant "Mod.ApiKey=two words"` and `--db-password 'two words'` printed everything after the first word (the quoted-flag case leaked before #403 too). `mask()` now masks each argument before quoting it for display, so the whole quoted value goes; the executed command is untouched, and a real-launch test proves the started app receives the raw argv; an env assignment at the start of a `--cmd` string keeps the whole command visible; the per-argument seds only run for arguments holding a secret word. The substring match still over-masks names like `Monkey` (Key) and `Turkey`; that is display only, kept, and pinned by a test. `tests/wave2/test-app-restart.sh` gains 22 cases, 8 of them failing on master. — review of #403 - fix(install-manifest): **a clean toolkit checkout passes its own `project-bin/` manifest self-check, so adopters no longer see the "named by NEITHER list" warning at the top of every sync.** `coverage-check-all.sh` (found by `review-module.sh` through its `_tool` lookup in the toolkit clone) and `ts-sync.sh` (UNPROVEN and SUPERSEDED per its header) join `MXTK_PROJECT_BIN_NOINSTALL` with reasons. New `tests/wave2/test-manifest-clean-checkout.sh` fails, rather than warns, when a `project-bin/` file is in neither list. — joshuamoesa (#381) - fix(sync-project): **the "Baseline-only skills cited" warning no longer fires on the toolkit's own wiring block, and no longer sizes a section that does not exist.** The check grepped all of `CLAUDE.md` for `skills/learned-mdl-preflight.md` / `skills/query-the-model.md`, and `wire-agents.sh`'s `mxtk:wiring` block cites the first, so every sync flagged the toolkit's own text; with no "mxcli-project-toolkit Integration" heading it then printed a whole-file word count as "heading to EOF". The wiring block is now stripped before the grep, and the warning is raised only when the heading exists. `tests/wave2/test-sync-dup-heading-exit.sh` T4 flips (no heading: no warning) and gains T7-T9. — joshuamoesa (#382) diff --git a/skills/fixture-seeding.md b/skills/fixture-seeding.md index 939929b1..dc3e7ded 100644 --- a/skills/fixture-seeding.md +++ b/skills/fixture-seeding.md @@ -201,6 +201,15 @@ the answer is "re-copy the Run Locally database", say that in the project's own "re-run the seed script", the seed script has to be idempotent, which most hand-written ones are not. +### Workflow data: abort through the runtime, link tables first if SQL + +A seed reset that deletes workflow rows by SQL fails while the link tables still point at them +(`workflow_currentactivity`, `workflow_workflowdefinition`, `usertask_assignees`, `usertask_targets`, +`usertask_targetgroups`). Abort instances through the runtime (Workflow Commons admin, or a microflow +with *Change workflow state*); if SQL is unavoidable (local database only), delete the link tables +first. Reset a context object only after its instances are gone: a deleted context object makes an +instance incompatible, and that cannot be resolved (Mendix docs, *Workflow versioning*). + ## What seeding buys - set expectations Journeys PASS / FAIL / INVALID on two public template apps, same set, empty vs seeded database: From 92d826d756d4c2d57af978024f715d524aa3c2e7 Mon Sep 17 00:00:00 2001 From: MendixMau Date: Fri, 16 Oct 2026 10:27:39 +0800 Subject: [PATCH 3/3] fix(changelog): credit segment on the WF-1 entry Co-Authored-By: Claude Opus 5.5 --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 12733fc6..92ade7aa 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,7 +19,7 @@ Sections dated before 2026-09-19 predate the cycle and stay as they are. Named m ## Unreleased -- learn(workflow skills): **a Jump has one fixed target, so one failure handler for N steps is a child workflow, not a shared node; plus three probed workflow errors (CE0117 on a `System.Workflow` argument means map `$WorkflowInstance`, CE1613 means the called workflow's parameter must be `$WorkflowContext`, and `returns` goes before `exposed as workflow action`).** `workflow-structure-rules.md` §1 and `learned-workflow-patterns.md` §9. The §9 rows were probed on mxcli v0.26.0 / Mendix 11.15.0 with native `mx check`. The third row corrects the reading that a called microflow cannot return a value. Source: a five-app lab project (Mendix 11.15, mxcli v0.26.0) +- learn(workflow skills): **a Jump has one fixed target, so one failure handler for N steps is a child workflow, not a shared node; plus three probed workflow errors (CE0117 on a `System.Workflow` argument means map `$WorkflowInstance`, CE1613 means the called workflow's parameter must be `$WorkflowContext`, and `returns` goes before `exposed as workflow action`).** `workflow-structure-rules.md` §1 and `learned-workflow-patterns.md` §9. The §9 rows were probed on mxcli v0.26.0 / Mendix 11.15.0 with native `mx check`. The third row corrects the reading that a called microflow cannot return a value. Source: a five-app lab project (Mendix 11.15, mxcli v0.26.0) — five-app lab report, WF-1 - learn(fixture-seeding): **resetting workflow data: abort instances through the runtime (Workflow Commons admin or *Change workflow state*); if SQL is unavoidable, delete the link tables first (`workflow_currentactivity`, `workflow_workflowdefinition`, `usertask_assignees`, `usertask_targets`, `usertask_targetgroups`), and reset a context object only after its instances are gone.** A seed reset that deleted workflow rows by SQL failed on those links (WF-5). Text only, `ondemand` skill. — five-app lab report, WF-5 - fix(app-restart): **a secret with spaces in quotes is masked whole, and `Pwd` and `Credential` join the secret words.** Follow-up to #403: `--constant "Mod.ApiKey=two words"` and `--db-password 'two words'` printed everything after the first word (the quoted-flag case leaked before #403 too). `mask()` now masks each argument before quoting it for display, so the whole quoted value goes; the executed command is untouched, and a real-launch test proves the started app receives the raw argv; an env assignment at the start of a `--cmd` string keeps the whole command visible; the per-argument seds only run for arguments holding a secret word. The substring match still over-masks names like `Monkey` (Key) and `Turkey`; that is display only, kept, and pinned by a test. `tests/wave2/test-app-restart.sh` gains 22 cases, 8 of them failing on master. — review of #403 - fix(install-manifest): **a clean toolkit checkout passes its own `project-bin/` manifest self-check, so adopters no longer see the "named by NEITHER list" warning at the top of every sync.** `coverage-check-all.sh` (found by `review-module.sh` through its `_tool` lookup in the toolkit clone) and `ts-sync.sh` (UNPROVEN and SUPERSEDED per its header) join `MXTK_PROJECT_BIN_NOINSTALL` with reasons. New `tests/wave2/test-manifest-clean-checkout.sh` fails, rather than warns, when a `project-bin/` file is in neither list. — joshuamoesa (#381)