Skip to content

fix(flows): six silent wrong writes in flows — written correctly or refused (#591 #698 #991 #992 #870 #175) - #918

Merged
ako merged 9 commits into
mainfrom
fix/flow-silent-writes
Oct 1, 2026
Merged

ako merged 9 commits into
mainfrom
fix/flow-silent-writes

Conversation

@ako

@ako ako commented Oct 1, 2026

Copy link
Copy Markdown
Owner

Six flow statements that passed check and exec but wrote something wrong. Each one is now written correctly, or refused at both check and exec. Every one was reproduced first on an ako/TestApp copy (Mendix 11.14.0).

Fixes mendixlabs#591
Fixes mendixlabs#698
Fixes mendixlabs#991
Fixes mendixlabs#992
Fixes mendixlabs#870
Fixes mendixlabs#175

What changed

Issue Symptom (reproduced) Fix
#591 create list / add / remove / count / head / filter / sort in a nanoflow: CE6035 per activity. call nanoflow … on error continue: CE6035. check passed all of it. The list actions and cast now carry the flow type's default: Abort in a nanoflow, Rollback in a microflow. This matches what Studio Pro stores (130 nanoflow actions in TestApp are all Abort). The writer emits the value and the reader reads it back. In a nanoflow, create, commit, call nanoflow and call microflow accept only on error without rollback, measured per cell. Every other clause is refused there. ValidateNanoflow now reports the nanoflow rules that previously only exec's build enforced (MDL091), plus the annotation rules.
#698 Over --mcp, the call action's error handling was dropped. The mapper writes errorHandlingType. A custom handler, or an error-handler flow, is refused: PED's SequenceFlow has no error-handler property (checked with ped_get_schema).
mendixlabs#991 @anchor / @curve inside on error … begin … end error did nothing. describe printed no @position (or other layout) inside the handler. Handler edges now apply the anchors, and the handler's curves are applied. The handler describer uses emitObjectAnnotations. The error edge's entry side is compared against the top side when deciding whether it is the default.
mendixlabs#992 @anchor(true: (to: top)) was parsed as to ÷ top, so no anchor was set. @curve(true: …) was dropped silently on nanoflows. The grammar tries annotationParenValue before annotationValue. An @anchor parameter mxcli cannot use is refused (MDL092). Nanoflows now run MDL059/060.
#870 lock workflow all → CE1825. describe showed Studio Pro's lock (PauseAllWorkflows plus a selection, in WorkflowCommons) as lock workflow all;. lock workflow $Def|Mod.Wf [pause all] and unlock … [unpause all] (Studio Pro's "Pause / Unpause instances"). The writer always writes the named selection. A bare all is refused (MDL-WF17).
#175 call workflow … on error continue → CE6035. Added to MDL076. MDL076 is now enforced by exec. StatementErrorHandling falls back to reflection, so statements missing from the hand-kept list (call workflow, REST) are seen.

Design choices

  • ADR-0011. Every new refusal is a form that mx check rejects (CE6035 / CE1825), or an annotation that was silently dropped. That makes them silent wrong writes, so they are refused under both mdl 0 and mdl 1. pause all / unpause all and lock workflow Mod.Wf are additions: before this PR they were parse errors (and describe emitted the qualified form, which did not parse).
  • Handler annotations follow the main-path semantics. to: is the incoming edge, so on the first handler statement it is the error edge. from: and @curve apply to the outgoing edge. On the last statement that is the rejoin edge, which takes only from:.
  • Per-case @curve stays unsupported. It is now refused on nanoflows too, matching microflows.
  • Old wrong values are not rewritten. Nanoflows that an earlier mxcli wrote with Rollback on list activities stay as they are until the flow is rebuilt.

Test plan (what I ran)

  • Unit tests, written to fail first: go test ./mdl/executor/ ./mdl/visitor/ ./mdl/backend/... ./mdl/exprcheck/... ./cmd/mxcli/... ./mdl/linter/..., all green.
  • Revert checks: I reverted each fix and confirmed its test fails with the reported symptom:
  • make test-integration-roundtrip passes. make test-integration-parity and make test-integration-executor each had tests that asserted the now-refused CE6035 forms (commit … on error rollback in a nanoflow, commit … on error continue, call microflow … on error continue in a nanoflow). I moved those to measured-buildable forms and re-ran them green.
  • make check-conformance, make lint, make check-findings, make build: all pass.
  • mx check (11.14.0, TestApp copy).
    • The original repro script: before the fix, 6 errors (CE1825 ×2, CE6035 ×4). With the fix, check refuses every bad form.
    • The corrected script (mdl 1): it builds with 0 errors. It covers nanoflow list ops and a handler with @anchor/@curve. It stores error-edge sides (2,3), handler edge (1,3) with curves 0;30/0;-30, rejoin (0,2) with curves 0;-30/-30;0, and the true edge (1,0). It also covers lock … pause all with a name selection.
  • Twice-exec. Running the create or modify script a second time reported "7 documents already in sync". describe → exec of the result reported unchanged.
  • Studio Pro content. I ran describe → exec on all 11 Studio Pro nanoflows plus WorkflowCommons.ACT_WorkflowDefinition_Lock and …_Unlock: "16 documents already in sync". Before this PR, the lock flows described as lock workflow all;.
  • Not exercised live: an MCP write with errorHandlingType. It follows the ped_get_schema declaration, and the notify mapping already uses it.

🤖 Generated with Claude Code

ako and others added 9 commits October 1, 2026 19:27
…627)

The fluent API's AttributeModifier.Apply() rebuilt the attribute with
raw == nil and kept only its $ID, so the codec minted GUID = $ID - the
mendixlabs#1119 data-loss class. The write guard refused it, leaving the API
unusable on any Studio Pro-authored attribute. Carry the stored raw
bytes and export level via carryStoredAttribute, now shared with the
entity rewrite's carryAttributeIdentity.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
MoveEntity scanned only the plain associations of the source unit, so a
cross-association created by an earlier move was invisible: moving its
second endpoint left a ParentPointer naming an element absent from its
unit and Studio Pro could not open the project. Handle the three shapes:
convert back to a plain association when both endpoints share a module
(raw transform, GUID carried), let an own cross-association travel with
its FROM entity, and re-point a cross-association in another module.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…endixlabs#849)

Studio Pro does not reload the model from disk, so a write mxcli makes
while the project is open is silently discarded by Studio Pro's next
save. The writer now refuses any write that would reach storage while
Studio Pro's <project>.mpr.lock is beside the .mpr (matched without
regard to case, as Studio Pro lower-cases it). Reads and writes elided
as no-ops are never refused; exec --force or
MXCLI_ALLOW_STUDIO_PRO_OPEN=1 override.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…its mappings and its commit option (#571)

create published rest service wrote only the path's {name} placeholders as
operation parameters, each a String: a query or body microflow parameter
failed mx check with CE0350, an Integer {id} with CE6539. import mapping,
export mapping and commit parsed and were thrown away.

- Parameters are derived from the microflow as Studio Pro derives them
  (path name -> Path, object/list -> Body, HttpRequest/HttpResponse -> none,
  else Query), each with the microflow parameter's type, merged over the
  stored parameters so a header / renamed / described one survives.
- Mappings and commit go AST -> model -> BSON and back; describe prints
  them and notes parameters MDL cannot state. An unknown commit option is
  refused by exec and check (MDL-REST03).
- create or modify carries the restated operation's summary, documentation
  and object handling; the service rewrite carries the stored keys MDL cannot
  state (authentication, CORS, documentation). List markers as Studio Pro
  writes them.

TestApp's Services.OrdersRestApi leaves the round-trip allowlist.

Fixes mendixlabs#1206

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- mendixlabs#591: list activities and cast carry the flow flavour's
  error handling (Abort in a nanoflow, where "Rollback" was CE6035); create,
  commit, call nanoflow and call microflow in a nanoflow accept only a handler
  without rollback (measured), refused otherwise; check now reports the
  nanoflow rules exec's build enforces (MDL091) and the annotation rules.
- mendixlabs#698: the MCP mapper writes errorHandlingType and refuses a
  custom handler / error-handler flow PED cannot express.
- mendixlabs#991: @anchor and @Curve inside an error handler are applied;
  describe emits the handler body's layout annotations.
- mendixlabs#992: `@anchor(true: (to: top))` parsed as a division; the
  paren value now wins, unusable @anchor parameters are refused (MDL092), and
  `@curve(true: …)` is refused on nanoflows too.
- mendixlabs#870: lock/unlock name their workflow; `pause all` /
  `unpause all` is Studio Pro's "(Un)pause instances"; bare `all` is refused
  (MDL-WF17); describe no longer turns a Studio Pro lock into `all`.
- mendixlabs#175: `call workflow … on error continue` refused (MDL076),
  MDL076 exec-enforced.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@ako
ako merged commit 65ae9ba into main Oct 1, 2026
31 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment