Skip to content

Workflow feedback + issues #948

Description

@ako

mendix-workflows skill — improvement & bug report

Nine issues against .claude/skills/mendix-workflows/SKILL.md (523 lines) and its companion example.mdl, raised from one full workflow build (ProcessCore.WF_CreditCardDisbursement) plus the recorded end-to-end run of 2026-08-20. Each is written in the shape of mendixlabs/mxcli#918 so it can be filed as-is.

Every item below was hit on this project. Nothing here is speculative.

Severity: S1 the skill’s guidance leads to a corrupted or unloadable model · S2 the skill is silently wrong or incomplete and the failure is invisible to every static gate · S3 a real gap with a workaround · S4 friction.

# | Title | Section | Sev -- | -- | -- | -- 1 | Version banner is stale and bans a form that is now safe | header | S3 2 | CREATE OR REPLACE WORKFLOW silently deletes hand-placed constructs | §6, §8 | S1 3 | ALTER WORKFLOW is absent from the skill entirely | §7 | S3 4 | “Returns Boolean, enum, or nothing” is necessary but not sufficient | §6 | S2 5 | Boundary timers are filed as “not verified” when they are known unwritable | §Scope | S2 6 | Warning 3 assumes mxbuild is installed and gives no fallback gate | §8 | S3 7 | No runtime-observability section — every workflow failure lands there | §10 | S2 8 | “Run it. Click one task through end to end” is not a verification method | §10 | S3 9 | The error table is compile-time only | §9 | S4

Issues 2, 4 and 7 are the ones worth fixing first: each is a case where the skill is confidently wrong or silent about a failure no static gate reports.

mendix-workflows skill — improvement & bug report Nine issues against .claude/skills/mendix-workflows/SKILL.md (523 lines) and its companion example.mdl, raised from one full workflow build (ProcessCore.WF_CreditCardDisbursement) plus the recorded end-to-end run of 2026-08-20. Each is written in the shape of [#918](https://github.com//issues/918) so it can be filed as-is.

Every item below was hit on this project. Nothing here is speculative.

Severity: S1 the skill’s guidance leads to a corrupted or unloadable model · S2 the skill is silently wrong or incomplete and the failure is invisible to every static gate · S3 a real gap with a workaround · S4 friction.

Title Section Sev

1 Version banner is stale and bans a form that is now safe header S3
2 CREATE OR REPLACE WORKFLOW silently deletes hand-placed constructs §6, §8 S1
3 ALTER WORKFLOW is absent from the skill entirely §7 S3
4 “Returns Boolean, enum, or nothing” is necessary but not sufficient §6 S2
5 Boundary timers are filed as “not verified” when they are known unwritable §Scope S2
6 Warning 3 assumes mxbuild is installed and gives no fallback gate §8 S3
7 No runtime-observability section — every workflow failure lands there §10 S2
8 “Run it. Click one task through end to end” is not a verification method §10 S3
9 The error table is compile-time only §9 S4
Issue 1 — Version banner is stale, and bans a construct that is now safe
Description
The skill opens with a blocking banner: “⚠ THIS MACHINE FAILS THE VERSION REQUIREMENT. This project’s mxcli is v0.16.0 … do not script a CALL MICROFLOW inside a CREATE WORKFLOW.” The machine now runs v0.18.0. The banner instructs the reader to delete it once the version passes, which is a maintenance step no one performs, so the skill’s loudest instruction is false and prohibits the single most common workflow construct.

Environment
mxcli v0.18.0 (2026-08-14) — banner asserts v0.16.0
Skill self-declares “Verified on: Mendix 11.13.0, mxcli v0.17.0”
Mendix 11.13.0, macOS 26.6, arm64
Steps to Reproduce
Load the skill on this machine.
Read the banner: do not script CALL MICROFLOW inside CREATE WORKFLOW.
Run mxcli --version → v0.18.0.
Expected vs. Actual
Expected: version-conditional guidance that resolves itself against the installed binary, or a version matrix the reader consults.

Actual: a hardcoded machine-specific assertion inside a reusable skill. Three different versions are named in the first 30 lines (v0.16.0 banner, v0.17.0 verified-on, v0.16.0 “older binaries”), and none is the installed one.

Suggested fix
Replace the banner with a version matrix and a one-line self-check the reader runs first:

Construct ≤ v0.16.0 ≥ v0.17.0
CALL MICROFLOW in a workflow body unsafe — pre-11.9 $Type, red pin, app won’t boot safe, fully-qualified WITH key
Fully-qualified WITH parameter key null ParameterId, unloadable correct
Machine-specific state belongs in CLAUDE.local.md, not in the shared skill.

Severity
S3 — hard blocker with a workaround (ignore the banner), but it teaches the reader to distrust the document’s other warnings, which are load-bearing.

Issue 2 — CREATE OR REPLACE WORKFLOW silently deletes every construct the deparser cannot see
Description
The skill documents CREATE WORKFLOW as the way to write a workflow and never states what happens when you re-run it against an existing one. mxcli’s deparser cannot see an Event Sub-Process or a boundary timer — §Scope admits it has not verified them — so CREATE OR REPLACE WORKFLOW rewrites the definition from what it can read and drops the rest. On this project that is a standing project-level ban (WF_CreditCardDisbursement carries a hand-placed Event Sub-Process with an interrupting timer), and the ban exists in CLAUDE.local.md and in session memory but nowhere in the skill that a fresh reader loads.

The same blind spot is already acknowledged in §8 (“DESCRIBE MICROFLOW lies about CALL WORKFLOW”) and §Scope, but the destructive consequence for a round-trip write is never drawn.

Environment
mxcli v0.17.0 / v0.18.0, Mendix 11.13.0, macOS 26.6 arm64
Split-format model (.mpr + mprcontents/)
Steps to Reproduce
Build a workflow in MDL; add an Event Sub-Process with an interrupting timer by hand in Studio Pro (the only way — see Issue 5).
DESCRIBE WORKFLOW Mod.Wf → the Event Sub-Process is absent from the output.
Re-run the original CREATE OR REPLACE WORKFLOW script.
strings .mxunit | grep -c eventSubProcess1 → 0.
Expected vs. Actual
Expected: the skill warns that any OR REPLACE write is bounded by what DESCRIBE can render, and names the constructs outside that boundary.

Actual: §6 presents CREATE WORKFLOW as the workflow-authoring statement with no re-run caveat, and §8’s three warnings do not include the one that destroys work.

Workaround
Use the targeted mutator instead — verified on a full project copy on 2026-08-19: after ALTER WORKFLOW … REPLACE ACTIVITY, eventSubProcess1, InterruptingTimerEventSubProcessStart, its inner activities and the ESP caption were all still in the unit, and mx check matched the pre-write baseline.

ALTER WORKFLOW Mod."Wf" REPLACE ACTIVITY "jump1" WITH
call microflow Mod."SUB_X" comment 'caption' with ( … );
Three properties of that statement the skill must carry with it:

It drops the activity’s annotation, unrecoverably. annotation '…' is a parse error both before call and between call and comment. On a converted workflow whose annotations carry source-provenance notes, that loss is usually worse than the edit was worth. A JumpToActivity has a null annotation, so it is the one activity type REPLACE ACTIVITY rewrites at no cost.
The caption keyword is comment, and its position is fixed: call microflow → comment → with → outcomes. comment after with is a parse error; a trailing -- text is discarded as an MDL comment and leaves the caption empty with no warning.
SET ACTIVITY cannot set a caption at all — it accepts only PAGE, TARGETING, DUE, DESCRIPTION.
Harmless side effects: the activity is renamed (jump1 → callMicroflow6_2) and its stored RelativeMiddlePoint/Size reset to default.

Do the whole experiment on a cp -R of the project — never on the live .mpr, never while Studio Pro has it open.

Severity
S1 — the guidance as written leads a reader to destroy hand-modelled process structure that no static gate will report missing.

Issue 3 — ALTER WORKFLOW is absent from the skill entirely
Description
§7 is titled “the 11 workflow microflow statements” and covers the runtime API. §6 covers CREATE WORKFLOW. Nothing covers editing an existing workflow definition, which is what every session after the first one does. The ALTER WORKFLOW surface is mentioned only once, in §Scope, as unverified annex material — yet it is the only safe way to touch a workflow that carries hand-placed constructs (Issue 2).

Environment
mxcli v0.18.0, Mendix 11.13.0
Steps to Reproduce
grep -n "ALTER WORKFLOW" SKILL.md → one hit, in the “treat as a starting point to probe” annex pointer at line 517.
A reader who needs to fix one activity’s caption has no documented route and reaches for CREATE OR REPLACE.
Expected vs. Actual
Expected: a §6b with the verified mutator surface and its limits.

Actual: the reader must discover it, then probe it on a project copy, to learn what this project already established.

Suggested content
Statement Status
ALTER WORKFLOW … REPLACE ACTIVITY WITH verified surgical; drops annotation (Issue 2)
ALTER WORKFLOW … SET ACTIVITY … only PAGE, TARGETING, DUE, DESCRIPTION
ALTER WORKFLOW … INSERT … does not support USER TASK activities (BUG-40)
CREATE OR REPLACE WORKFLOW destructive on unrenderable constructs — see Issue 2
Severity
S3 — real gap, workaround exists (probe it yourself), cost is one session per team.

Issue 4 — “Returns Boolean, an enumeration, or nothing” is necessary but not sufficient
Description
§6 states two hard constraints on a workflow-called microflow, the first being: “It must return Boolean, an enumeration, or nothing. Returning an entity is rejected.” A reader satisfies this by checking DESCRIBE MICROFLOW or SELECT ReturnType FROM CATALOG.MICROFLOWS and reading Void. Both can report Void on a microflow whose end events still carry a return-value expression. mxcli drops the returns clause when a microflow mixes a valueless return; on a fault arm with return $Flag; on the happy arm — and writes the value onto the end event anyway. The runtime honours the end event.

Called from a workflow CallMicroflowActivity whose outcome is VoidConditionOutcome, the instance dies at execution:

Unexpected type of condition. Trying to compare VoidConditionValue$('') to BooleanValue('true').
at MendixEvaluatedCondition.matches(MendixConditionParser.scala:37)
at CallMicroflowAction$$anon$1.applyOrElse(CallMicroflowAction.scala:69)
Environment
mxcli v0.18.0, Mendix 11.13.0 runtime, macOS 26.6 arm64
Steps to Reproduce
Write a workflow-called microflow with return; on a fault branch and return $Flag; on the happy branch.
DESCRIBE MICROFLOW → declared Void. CATALOG.MICROFLOWS.ReturnType → Void.
mx check → 0 errors. mxcli lint → clean.
Start an instance → the stack trace above; the instance stops.
Expected vs. Actual
Expected: §6 tells the reader that the declared return type is not the thing that matters, and gives a detection recipe.

Actual: the reader checks the declared type, sees Void, and ships a workflow that cannot run.

Workaround / detection
Read the end events, not the declaration:

mxcli -p App.mpr -c "DESCRIBE MICROFLOW Mod.Name" | grep -oE 'return [^;]+;'
Single-quote that grep. "return $[A-Za-z]*;" in double quotes gets eaten by shell $ expansion and reports a clean sweep on microflows that are provably dirty.

Also worth carrying: reading the runtime message correctly. The order is $value to $other, so the receiver (VoidConditionValue) is the stored outcome’s condition and the argument (BooleanValue('true')) is what the microflow actually returned. Getting this backwards sends you hunting for a phantom Boolean outcome on the canvas.

Severity
S2 — silently wrong, invisible to DESCRIBE, CATALOG, mx check and mxcli lint, and only observable by starting an instance and reading the runtime log.

Issue 5 — Boundary timers are filed as “not verified” when they are known unwritable
Description
§Scope says workflow timer and wait-for-notification activities and boundary events “have not been verified here; probe with mxcli check before relying on any form.” That under-reports what is known: MDL’s workflow vocabulary has no END and no JUMP, so every MDL-written interrupting timer boundary event is malformed (CE0105) with no workaround (TL-04 d). The construct is not unverified — it is unavailable, and the only route is hand-placing it in Studio Pro.

“Probe with mxcli check” is also the wrong instruction for this class: check passes on writes that produce unloadable models (§8 Warning 1 is exactly that).

Environment
mxcli v0.17.0 / v0.18.0, Mendix 11.13.0
Expected vs. Actual
Expected: §Scope distinguishes unverified (probe it) from known unavailable (don’t try; here is the hand-build handoff), and links the static verification recipe for hand-placed work.

Actual: a reader spends a session probing forms that cannot work, then discovers the gap.

Suggested addition — verifying a hand-placed construct without a live run
DESCRIBE WORKFLOW is blind to boundary timers, Complete task and Call workflow activities, but their expressions are stored as plain strings in the workflow’s model unit:

u=$(LC_ALL=C grep -rla '' mprcontents/ | head -1)
LC_ALL=C strings -n 4 "$u" | grep -iE 'timer|duration|PT[0-9]'
The .mxunit is uncompressed BSON, so this is a direct before/after probe. Check git status -- '*.mpr' and the .mpr mtime first: an unmodified .mpr means no model edit was saved at all, whatever was typed into Studio Pro.

What this does not prove is wiring — that the timer’s outgoing branch calls the right thing and ends correctly is still only observable by letting it fire (~16 min of wall clock on this project). Say so, so nobody mistakes a string match for a passing test.

Severity
S2 — the scope note reads as “unknown” when it is “known impossible”, and sends the reader down a dead end.

Issue 6 — Warning 3 assumes mxbuild is installed and offers no fallback gate
Description
§8 Warning 3 makes the native check mandatory — “After building any workflow, run the native check … /modeler/mx check YourApp.mpr” — and correctly insists on the eyeball step in Studio Pro. On this machine ~/.mxcli/mxbuild does not exist, so the mandatory gate cannot be run, and the skill provides no alternative. The reader either fabricates a passing gate or stalls.

There is a second gap: mx check crashes (InvalidCastException) on some corrupt workflow writes (BUG-25, null ContentsBlob). A reader who sees a crash reads it as a broken toolchain rather than as the finding it is.

Environment
macOS 26.6 arm64; ~/.mxcli/mxbuild absent; Studio Pro present but its mx not on any documented path
mxcli v0.18.0
Steps to Reproduce
Follow §8 Warning 3.
ls ~/.mxcli/mxbuild → No such file or directory.
The skill’s step 7 of §10 (“Native mx check + open in Studio Pro”) cannot be performed.
Expected vs. Actual
Expected: the skill names the setup command, and gives a degraded-mode gate for when the native loader is unavailable.

Actual: an unconditional “run this” against a binary that may not exist.

Suggested fix
mxcli setup mxbuild -p App.mpr # one-off, downloads for the project's Mendix version
mxcli docker check -p App.mpr # alternative, auto-downloads
And, when neither is available, the degraded gate: strings on the workflow’s .mxunit diffed against the pre-write baseline (§8 already carries this technique — promote it from “when you suspect a storage-level defect” to “the fallback gate”). State plainly that a crash from mx check is a result, not an environment problem.

Severity
S3 — blocks a mandatory step; workarounds exist but the reader has to invent them.

Issue 7 — No runtime-observability section, and every workflow failure lands there
Description
The skill’s own §10 step 8 concedes the point: “The two silent failures in this pattern — the missing back-reference and an unloadable DECISION — are both invisible to every static gate.” Issue 4 adds a third. Yet the document contains no way to observe a running workflow: no query for live instances, no way to see which task is open on which case, no way to read the exception that stopped an instance.

This project verified all of that in the recorded end-to-end run of 2026-08-20 (31/31 assertions, including “no workflow instances are left over” and “the signing task is in the Banker’s own inbox”), using facilities the skill never mentions.

Environment
Mendix 11.13.0 running locally, M2EE admin on 8091, app on 8081
Expected vs. Actual
Expected: a §11 “Observing a live workflow” with the three queries a reader always needs.

Actual: the reader is told to click a task and see what happens.

Suggested content
Instances and tasks over OQL, without Studio Pro, via the M2EE admin port:

POST http://localhost:8091/dev/preview_execute_oql
Header: X-M2EE-authentication: ""
Body: {"oql": "...", "numberHandling": "asString"}
Aliases are required, and Case is a reserved word — alias it. Useful queries: count live System.Workflow rows for a context object; list System.WorkflowUserTask by State and Name; confirm the context ⇄ System.Workflow back-reference from §3 is actually populated (the fastest possible check for the blank-task-page failure).

The exception that stopped an instance is only in the runtime log: pull the admin password from ps eww, base64 the header, register a log subscriber, and read the trace (Issue 4’s stack trace was obtained this way). No Studio Pro console needed.

One caveat worth printing: the admin port answers preview_execute_oql with no password check on a local run.

Severity
S2 — the skill acknowledges that its failure modes are runtime-only, then stops.

Issue 8 — “Run it. Click one task through end to end” is not a verification method
Description
§10 step 8 is the last line of the build order and the only functional gate in the document. As written it is a suggestion, not a procedure: no criteria, no evidence, no record. Two failure modes this project hit make the informal version unreliable:

Scoped UI probes miss modals. A probe scoped to the card or the task page cannot see Mendix’s own confirmation dialog — action: DELETE opens it as a page-level modal. A row count taken straight after the click is a count taken while the dialog is still waiting for an answer. That produced a false defect that stood for weeks (“an administrator cannot remove a rule row”), retracted 2026-08-19 when the dialog was searched for across the whole document: grid 1 → 0, OQL 1 → 0, no console errors. Delete had always worked. Scope the interaction to its container; search the whole document for modals, dialogs and toasts.
Browser drivers must log out. A leaked session hits the trial-licence cap, after which every login answers HTTP 560 while FailedLogins stays 0. Assert the session count against the baseline recorded at the start of the run, and note that the page you are on decides whether logout works — a user parked on a page their role cannot open never boots the client, so window.mx is absent and the logout never fires.
Environment
Playwright 1.60.0, Mendix 11.13.0 on 8081, M2EE on 8091
Expected vs. Actual
Expected: step 8 gives a repeatable procedure — drive the task, assert the state transition in the database, assert the instance count, log out, assert no leak.

Actual: “click one task through end to end”, which is exactly the informal check that produced the false defect above.

Severity
S3 — the gate exists but is unspecified; both failure modes are cheap to prevent and expensive to diagnose.

Issue 9 — The error table is compile-time only
Description
§9 lists thirteen errors, all of them CE* / MDL* / MPR* codes raised by a checker. None of the runtime failures this project hit appear, and those are the ones that cost real time — a compile error names its own fix, a runtime one does not.

Expected vs. Actual
Expected: a second table, “Runtime symptoms”, keyed by what the reader actually sees.

Actual: the reader searching for the symptom finds nothing and assumes it is unknown.

Suggested rows
Symptom Cause Fix
VoidConditionValue$('') vs BooleanValue at instance start mixed return; / return $X; — declared Void, end event returns a value Issue 4
Instance starts, then stops with no error in the UI called microflow threw; only the runtime log has it Issue 7
Task page renders blank back-reference never populated (already in §9 — cross-link it to the OQL check) §3
Task never appears in any inbox targeting XPath matched no user; role composition, not role name §5
Workflow instance parked, not failed correct behaviour for a wait/timer branch — check AvailabilityAttempts-style counters before calling it a hang —
CE0105 on an interrupting timer boundary event no END/JUMP in MDL’s workflow vocabulary Issue 5
CE7247 on the context parameter a chosen parameter name is legal in a microflow, rejected as a workflow context parameter; the declared name is not retained (DESCRIBE reads back $WorkflowContext) rename
Severity
S4 — friction, but it is the friction that dominates a workflow build’s debugging time.

Provenance
Source What it contributed
ProcessCore.WF_CreditCardDisbursement build, mdlsource/9-workflow/ (scripts 58–66) Issues 2, 3, 5
Runtime stack trace, 2026-08-19 Issue 4
mdlsource/9-workflow/66-jump-caption-typo.mdl + project-copy probe, 2026-08-19 Issue 2
Rule-table Delete retraction, 2026-08-19 Issue 8
Recorded end-to-end run, 2026-08-20 (docs/e2e-recorded-run-2026-08-20.md, 31/31) Issues 7, 8
docs/TOOL-LIMITATIONS.md, mxcli-feedback/02-bug-index.md Issues 1, 3, 5, 6, 9
Issues 2, 4 and 7 are the ones worth fixing first: each is a case where the skill is confidently wrong or silent about a failure no static gate reports.

The three worth fixing first are 2, 4 and 7 — each is a case where the skill is confidently wrong or silent about a failure that no static gate reports:

  • DROP ATTRIBUTE will trigger .mxunit ref empty error #2 is the only S1. The skill presents CREATE WORKFLOW as the authoring statement with no re-run caveat, and its own §Scope admits the deparser can't see Event Sub-Processes or boundary timers.
    Those two facts together destroy hand-modelled structure, and nothing in the document joins them. The issue carries the verified alternative (ALTER WORKFLOW … REPLACE ACTIVITY) plus its three
    traps — dropped annotation, comment position, SET ACTIVITY can't set captions.
  • MPR data corruption after attribute drop/add with enumeration default (data loss risk) #4 is the one that cost the most here: a microflow reading Void in both DESCRIBE and CATALOG while its end event still returns a value, killing the instance at runtime. §6's constraint is the
    right rule checked against the wrong artefact.
  • GALLERY widget is completely unusable via MDL in mxcli v0.2.0-dirty #7 is the structural gap. The skill's own step 8 concedes its failure modes are invisible to every static gate, then offers no way to look at a running instance. The OQL and log-subscriber
    recipes we used for the 20 Aug run fill it.

Issues 5, 6 and 8 draw directly on this week's work — the strings-on-.mxunit trick for hand-placed timers, the missing mxbuild on this machine, and both verification failures from the recorded
run (the scoped-probe false defect and the session leak). Each item's provenance is listed at the bottom so a triager can trace it back to the script or document it came from.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions