Skip to content

Sync ako/mxcli: describe flow round-trips, check false refusals, per-file findings - #1353

Merged
ako merged 60 commits into
mendixlabs:mainfrom
ako:main
Oct 8, 2026
Merged

ako merged 60 commits into
mendixlabs:mainfrom
ako:main

Conversation

@ako

@ako ako commented Oct 8, 2026

Copy link
Copy Markdown
Collaborator

Syncs ako/mxcli:main into mendixlabs/mxcli:main: 24 commits since #1340, landed in the fork through PRs ako#1045–ako#1061.

Note on the diff size: almost all of the 1,752 changed files are the findings migration below. The bug-findings .jsonl shards become one file per finding.

Describe: flows that did not round-trip

Found by rebuilding the describe output of every flow in a large real-world project and comparing graphs. Each of these re-executed to a different flow than the one stored.

  • A loop's back-edge is kept when its header sits inside a branch, or is also a split's join (pagination and poll loops were lost).
  • A crossed merge is joined wherever a branch reaches it, a region two case arms share is printed once, and every crossing of an interleaved if whose entries are merges is joined.
  • An error handler two activities share is printed once. An error handler walks through a merge with one way in, instead of stopping there and dropping the rest.
  • A loop-body arm that stops dead ends the iteration with continue; instead of falling into the join's path.
  • An empty decision caption survives describe → exec (roundtrip split caption does not work #1254).

Check: false refusals on Studio Pro-authored flows

  • MDL-FLOW02 counts joins inside a REST call's error handler. Retry loops in Studio Pro-built OIDC / AWS modules were refused.
  • A void microflow/nanoflow call's output name declares nothing (MDL063). Measured on mxbuild 10.24.15 and 11.13.0.
  • MDL004 on a stored void microflow's return value is a warning for a microflow the project already stores that way. New code keeps the error.
  • : is lexed as Mendix division in the expression checker, so hoursBetween(…) : 4 < $Max types as Boolean (false E009).
  • An underscore-led enumeration value in XPath (_True) is stored as a string literal (CE0161).
  • Performance: check -p lists only the document kinds the script's statements ask about (~9 s saved on a 140 MB MPR v1 project), and reads project settings only for scripts that write translations.

MDL

Contributor tooling

  • Findings are one file per finding (findings/<area>/<date>-<slug>.json) instead of shared .jsonl shards. GitHub's server-side merge does not run merge=union, so every merged fix-PR put the other open ones into conflict. scripts/split-findings.py did the migration byte for byte, and skills, commands, CLAUDE.md and the wiki point at the new layout.
  • Wiki: the rebuild comparison is describe's missing oracle, and a flow describer is several walks that have to agree

ako and others added 30 commits October 8, 2026 08:41
getErrorHandlerBody kept its own list of statements with an ON ERROR
clause and lacked the REST statements (and call workflow, the mapping
statements, commit, delete, …). The merge/join label walk read handler
bodies through it, so a `join` in a REST call's custom error handler went
unseen and its merge was reported as "nothing joins it". That is the
retry loop of Studio Pro-authored OIDC.GET/POST/PUT/PATCH/DELETE and
AWSAuthentication.POST_v1_GetCallerIdentity_ValidateCredentials, which
build at 0 errors; exec refused their describe output.

getErrorHandlerBody now reads the shared StatementErrorHandling table
(mdl/exprcheck/adapters), as stmtErrorHandling already does.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…hing

#953 taught the duplicate-variable check (MDL063) that a call to a void
Java/JavaScript action keeps an inert output name. Studio Pro stores calls
to void microflows and nanoflows the same way (ResultVariableName with
UseReturnVariable=true), and the resolver never looked at them: Evora's
OIDC.webCallback, OIDC.SUB_HandleUserProvisioning and two Teamcenter
microflows (0 errors under mx check) failed check, and describe headed
webCallback with "duplicate output variable … model is invalid".

Measured on mxbuild 10.24.15 and 11.13.0: two void microflow calls of one
name 0, void call then declare 0, two void nanoflow calls 0, reading a
void microflow call's output CE0109, two String microflow calls CE0111.
voidCodeActions now resolves called microflows and nanoflows (script
first, then the project), which also extends MDL093 and describe's
duplicate-output header to them.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ning

Studio Pro keeps an end event's return value when a microflow's return
type is set to Nothing, and hides the field. Evora stores five such
microflows; describe renders the value faithfully and MDL004 refused it,
so exec would not re-apply an untouched microflow. mxbuild 10.24.15 and
11.13.0 build the shape at 0 errors.

The value is not inert — the runtime returns it, and a workflow branching
on the call fails at instance start — so new code keeps the error. Like
DropSettledCommitNotes for MDL067, check -p and exec now downgrade it to a
warning only for a microflow the project already stores with at least as
many valued end events.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…eral

Studio Pro names a True/False enumeration's values `_True` and `_False`
and stores a constraint on one as `[A = '_True']`; describe prints it as
`[A = M.E._True]`. The stored-form rewrite (normalizeXPathEnumRefs)
matched only value names starting with a letter, so exec wrote the
qualified name verbatim — CE0161 on mxbuild 10.24.15 — and `check -p`
correctly refused it as "neither an attribute nor an association". Evora's
AgentCommons.DS_Agent_GetDeployedModels is the instance.

The value part of xpathEnumRefRe now allows a leading underscore, as
xpathQualifiedNameRe already did.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…e as Boolean

`mxcli check -p` reported E009 (Boolean slot, Decimal expression) on
`hoursBetween(...) : 4 < $Max` in Evora's ConversationalUI.*_GetGranularity
microflows, which Studio Pro and mxbuild accept.

The expression checker's lexer did not know `:` and emitted a TokError.
parseMul saw no operator, parseCmp saw no comparison, and Parse skips the
trailing-token error for TokError, so the expression was silently truncated
to `hoursBetween(...)` and typed as Decimal. The MDL grammar itself already
parsed `:` at the multiplicative level; only the checker's second parser
had drifted.

`:` is now TokColon, parsed at the one left-associative multiplicative
level with `*`, `div` and `mod`, typed as Decimal like `div`, and treated
as a recovery boundary.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…translations

checkTranslationTargets read the project settings (for the source
language) on every `check -p`, and only consults them for a
`create translations` statement. Skip the read when the script has none.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… about

`check -p` on a 140 MB MPR v1 project spent ~9 s in CheckProjectConflicts
for a one-flow `create or modify` script: loadProjectNameSets listed all
~40 document kinds, and every module's security, before reading a
statement — though only a plain CREATE is ever flagged.
CheckProjectNameClashes then listed its 13 kinds up front too.

- projectNameSets.setFor lists a kind on first use and caches it.
  CheckProjectConflicts asks only for the kinds a plain CREATE names; a
  DROP records a dropped project name only when a plain CREATE of its kind
  follows (droppedFromProject is read nowhere else).
- CheckProjectNameClashes lists a kind only if a create, rename or move
  can read it (the union of nameSpaceKinds over those statements). A drop
  on another kind updates a throwaway set nothing reads.

Semantics unchanged: a lazily-loaded set plus the same mutations equals
the eagerly-loaded one. `create or modify microflow` still lists the flow
name space (microflow/nanoflow/rule), since a cross-kind clash is CE0122
whether or not the statement modifies.

Tests count the listings through a MockBackend whose List* fields are
wrapped by reflection: on main the or-modify script issues 36 listings;
now 0 for a kind with no shared name space, 3 for a microflow.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Brings in #1036, #1037 and #1042 and resolves the findings-file conflict
GitHub reports for appends to mdl-executor.jsonl (merge=union keeps both
locally; GitHub's server-side merge does not run merge drivers).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Brings in #1036, #1037 and #1042 and resolves the findings-file conflict
GitHub reports for appends to mdl-executor.jsonl (merge=union keeps both
locally; GitHub's server-side merge does not run merge drivers).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Brings in #1036, #1037 and #1042 and resolves the findings-file conflict
GitHub reports for appends to mdl-executor.jsonl (merge=union keeps both
locally; GitHub's server-side merge does not run merge drivers).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A crossed merge (Mode 2) is joined on arrival by traverseFlow,
traverseFlowUntilMerge and continueAfterSplitJoin, but five other places
walked THROUGH a merge without asking: continueAfterNestedSplitJoin, the
split-type continuation at both levels, and the guard-clause false-branch
skip at both levels. When the crossing sat inside another branch, the
crossed merge's suffix was printed inline there, and then either

- the walk marked it visited, its section was never declared, and the
  description ended in `merge shared1; join shared2;` with nothing
  declaring shared2 (Evora SnowflakeIntegration / SnowflakeRESTSQL
  .Request_Validation: MDL-FLOW02, MDL003), or
- the walk ran on a case/split-type arm's private visited copy, so the
  section printed the suffix a second time (Evora
  AgentCommons.Tool_Validate: one stored MicroflowSelection_GetCreate
  call, two in the description, MDL063).

Every pass-through now asks joinCrossedMerge first, and a labelled merge
is no longer skipped by the guard-clause continuation.

The faithful Tool_Validate description then failed check for a second
reason: the section after the `split type` reads $IsValid, declared in
the arm that joins it. The check-time validator scoped variables by text
order; a `merge <label>` now sees what its joins had declared, and the
linear MDL005 branch-scope warning stops at a merge, where scope comes
from the joins it cannot see.

assertDescriptionRebuildsGraph is the invariant every test here holds a
description to: parse it, build it with the real builder, and compare
the graph with the stored one modulo exclusive merges - every stored
activity exactly once, every stored sequence flow present.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A `case` or `split type` walks every arm with its own copy of the
visited set until the split's join, so a region that two arms reach
before that join was printed in both. Evora OIDC.handleAuthorizationCode
has one token REST call where two of its four arms meet; the description
had two, both with output $TokenResponse (MDL063), and re-executing it
builds an activity the model does not have.

Mode 2 did not label it because a fourth arm returns: the split's
post-dominator is then the exit, the overlap has two entries (the shared
region and the join), and Analyze classifies it interleaved, which
labelCrossedMerges skips. sharedArmEntries computes the overlap from the
describer's own stop instead, and when it holds a real activity and is
entered through merges, labels the entry and the split's join as crossed:
the arms `join` the region, which is printed once in its section. Arms
that meet on a bare merge chain print nothing twice and are left alone.

The MDL-FLOW01 "NOT equivalent, must not be re-executed" warning is
retired when every entry of the finding was declared, not only a single
one - otherwise it stays on a description that is now faithful.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A manual loop - a merge the path comes back round to - is written as
`while true` by traverseFlow at the top level. Reached inside an `if`
branch, traverseFlowUntilMerge walked through it as a plain merge, so
the way round came back to an activity already printed and stopped in
silence. The description had no back-edge and re-executed to a flow that
ends where the original goes round again: Evora
AmazonBedrockConnector.AmazonBedrockAgent_Sync and
AmazonBedrockKnowledgeBase_Sync lost their pagination loop (MDL003).

The branch walker now writes the same `while true`. Only a merge whose
way round avoids the enclosing stop and everything already printed is a
header: the merges on the path BACK to it are on the cycle too, and
treating them as headers nested empty loops inside the body.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Brings in #1036, #1037 and #1042 and resolves the findings-file conflict
GitHub reports for appends to mdl-executor.jsonl (merge=union keeps both
locally; GitHub's server-side merge does not run merge drivers).

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

Since mendixlabs#1269 lint reads modules().domain_model_documentation and
project_security().admin_user_name, but MDL could write neither:

  the doc comment before `create or modify module MyFirstModule` is lost
  ... [MDL089]
  alter app security has no property "AdminUserName"

- The doc comment on `create [or modify] module` is stored as the
  module's domain model documentation (DomainModels$DomainModel.
  Documentation; Projects$Module has none). No comment preserves what is
  stored, `/** */` clears it, and a plain create of an existing module
  stays a no-op. `describe module` prints it back as that comment.
- `alter app security ( AdminUserName: '<name>' )` renames the built-in
  administrator. The password stays out of MDL (#624).

New backend methods SetDomainModelDocumentation and
SetProjectAdminUserName change only that property on the stored unit.
Checked on a fresh 11.12.5 app: exec, describe round-trip, the catalog
column lint reads, and mx check with 0 errors, same as an untouched
control.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Uj8k5uUCi1zKZ3Edj8MZ8h
…L stored first

mendixlabs#1182 (v0.23.0, Mendix 11.12.2): "SET Caption wrote Dutch,
and restating the snippet deleted the other languages."

Both halves are already fixed on main — SET by setTextsTextTranslation
writing the project default language's entry (33249a3), the restate by
CarryTranslations pairing a text by its owning named element. Neither had a
test of the reported shape: a caption whose default language (en_US) is
listed SECOND after nl_NL, and a restate that changes the English and the
widget tree at once. This adds them.

Verified against the reporter's version: upstream v0.23.0 built from its tag
and run on a fresh 11.12.2 project with nl_NL listed first —
  SET:     nl_NL "Opslaan" -> "Save changes", en_US stays "Save"
  restate: nl_NL deleted
and on this branch —
  SET:     en_US -> "Save changes", nl_NL "Opslaan" kept
  restate: en_US "Save all changes", nl_NL "Opslaan" kept; mx check passes.

Controls: writing the first listed translation fails the mutator test with
map[en_US:Save nl_NL:Save changes]; disabling owning-element pairing fails
the canon test with map[en_US:Save all changes].

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Sg2HXaNgpZDxkHr9CYwEXp
The describe-round-trip-gaps page said the read path has no oracle. For a
flow it does: rebuild the description and compare graphs. Note it, and
note that `Unchanged` from `create or modify` on a describe output is the
splice verdict, not evidence the description is the stored graph.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Every fix appended a line to a shared findings/<area>.jsonl, and nearly
every fix lands in mdl-executor. merge=union resolved that locally, but
GitHub's server-side merge does not run merge drivers, so each merged
fix-PR put every other open one into conflict: eight PRs re-conflicting
after each merge, each needing a local merge, rebuild and re-test.

Findings are now findings/<area>/<date>-<slug>.json, one JSON object on
one line. Two fixes add two different files; nothing conflicts.

- scripts/split-findings.py did the migration and stays as the remedy
  for a pre-split branch that brings a shard file back on merge. It
  writes each line verbatim, so the result is the multiset of unique
  original lines, byte for byte (1,579 lines -> 1,573 files: six
  records were in mdl-executor.jsonl twice, a union-merge artefact).
- check-findings validates the files (name pattern, one line, fields)
  and refuses a stray *.jsonl, naming the converter.
- digest-status reads the new glob; the merge=union line is removed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… per-file layout

Recording a fix now means adding a file, never appending; lookups use
grep -r and DuckDB's read_json_auto over findings/*/*.json. Bug-pattern
page sources name the area directories. Historical logs (SYNC_LOG,
CHANGELOG, proposals, bug reports) keep the paths they were written with.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
TestRepoClaudeMDStaysWithinItsContextBudget caps CLAUDE.md at 28,000
bytes and main sat at 27,991, so the per-file wording has to be
byte-neutral. The why (GitHub does not run merge=union) lives in the
findings README; CLAUDE.md keeps only the instruction.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
main appended two findings to the old shards (cmd-mxcli, mdl-executor)
since this branch was cut; scripts/split-findings.py turned exactly
those two lines into files and removed the re-created shards.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Brings in #1037 (describe parse fixes), #1042 and #1044, and resolves
the findings-file conflict GitHub reports for appends to the shards.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
`alter page M.P { add parameters $X: T; }` was a parse error ("mismatched
input 'parameters' expecting VARIABLES_KW"), so adding a parameter to an
existing page meant CREATE OR REPLACE, which rebuilds every widget and
loses what describe does not round-trip.

- Grammar: `add parameters <pageParameter>` / `drop parameters $X`, reusing
  CREATE's `pageParameter` rule; the visitor shares `buildPageParameter`.
- PageMutator gains AddParameter/DropParameter. The raw-BSON mutator gets
  the element from a new Deps.SerializeParameter, which the codec backend
  implements with CREATE's own pageParameterToGen/snippetParameterToGen, so
  the 11.5+ IsRequired/DefaultValue gate holds. Key set matches a Studio
  Pro-authored parameter on the 11.6 fixture.
- Exec refuses an add that leaves a page's Url without the new parameter's
  segment (CE5601, measured on 11.12.1) unless the statement sets a Url
  that names it; a primitive snippet parameter (MDL087/CE0046, also at
  check time); a drop while a data source or expression still uses it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017ZiACMmuCejcDnzsvG3PKQ
When two or more activities' error flows land on one merge that no normal
path reaches, every `on error … begin end error` block came out empty and
the handler body they share disappeared from the description. The handler
walk stops at the first merge it meets and emits `join <label>` only when the
merge has a label, and labelRejoinMerges names only merges the normal path
also reaches. Re-executed, the description deletes the shared handler.

Label such a merge as a crossed merge: each block now ends in `join sharedN;`
and the body is printed once, in its own section after the main flow. The
builder already accepts a forward join out of a handler, so this needs no
new syntax.

Evora: PrePopulateData.ASU_CheckForWorkforce (three calls, one log + end)
and GenAICommons.ToolCall_ProcessAndExecuteTool (two calls, four activities
rejoining the main path) now rebuild their stored graphs.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
In a loop body, an activity with no outgoing flow ends the iteration. When
such an activity closed a branch whose join goes on (to a `break`, or to more
activities), the description closed the branch and printed the join's path
after it, so the arm fell into that path. GenAICommons.Trace_GetModelSpanInput
then broke out of its loop at the first ToolSpan instead of collecting them
all.

The arm now ends in `continue;` when anything is printed after the branch
closes, at any enclosing level up to the loop body. That is not a local
question (IVK_RecalculateSize's dead end sits in an `if` with no join, inside
a `case` arm whose join goes on), so it is carried on the ExecContext: set when
a branch walk's stop merge goes on, inherited by nested walks, extended for a
guard's THEN arm by the false continuation, and reset per loop body. A dead end
whose branch is the last thing in the body gets no `continue`, which keeps the
commonest loop shape (last body activity is a dead end) unchanged.

Evora: GenAICommons.Trace_GetModelSpanInput, MxModelReflection.IVK_RecalculateSize
and TeamcenterToolkit.ExpandGRMInput_Create now rebuild their stored graphs
(a ContinueEvent stands in for the dead end, which the invariant treats as
the same ending).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
An error handler's path can run through ExclusiveMerges with a single
incoming flow, which join nothing (Studio Pro leaves them behind when a flow
is re-routed). firstReachableErrorHandlerMerge settled on the first of them as
the handler's stop, and the handler walk stops at any merge, so everything
after it was dropped. In SnowflakeRESTSQL.GET_v1_RetrievePartition that was
`set $Retry = false` and the way back to the loop header: re-executed, a token
retry became a fall-through.

A merge with one way in is now walked through: by the handler walk, by
firstReachableErrorHandlerMerge, and by firstMergeFrom, which decides where a
handler rejoins and so which merges need a label. The two traverse tests that
modelled a rejoin merge with only the handler's flow into it now give it the
main path's flow too, which is what makes it a rejoin.

The faithful description then failed `mxcli check` with "merge rejoin1:
nothing joins it" (MDL-FLOW02), for two reasons. The label walk read handler
bodies through getErrorHandlerBody, which does not know a REST call has one;
it now uses the builder's statementErrorHandling. It also treated every
`while` as a loop activity, but `while true` around a return is a merge and a
back-edge in the enclosing graph, so its labels belong to that graph. A label
in a handler inside a real loop is now refused (MDL-FLOW04) instead of being
missed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Studio Pro can draw one merge that both closes an `if` and heads a loop. The
describer paired it with the `if` and walked on after `end if`, so the way
round came back to an activity already printed and stopped there. In
SnowflakeRESTSQL.POST_v1_ExecuteStatement that dropped the 202 poll: re-executed,
the description calls the service once and carries on as if it had answered.

When a split's join is also an undescribed loop header (the same test #1055
uses for a header inside a branch: a way round that avoids the stop and
everything already printed), continueAfterSplitJoin and
continueAfterNestedSplitJoin now write it as `while true` after the split.
MDL spells the two jobs as two statements, and the rebuild's two merges
contract to the stored one.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… are merges

An `if` whose arms reach a shared region at different depths was left alone
when the overlap had more than one entry ("interleaved"): it kept the #923
MDL-FLOW01 "NOT equivalent" warning and was nested as fall-through. In
OIDC.GetLoginEndpoint that sent an arm two decisions deep past the
authorization-URL region straight to the nonce cookie.

When every entry of an interleaved `if` is a merge, label each entry and the
join as crossed merges: every arm that reaches one ends in `join`, and each
region is printed once in its own section that joins the next. This also
needed armFallsInto. When no arm falls out of the `if` (they all return or
join), the crossed join is no longer written after `end if`, where it was an
edge out of nothing that the builder then invented a source for. The textbook
A -> {B, C}, B/C -> {D, E} fixture fails without it.

An interleaved overlap entered at an activity is still left with MDL-FLOW01,
because there is no merge to name. TestLabelCrossedMerges_IgnoresInterleaved
encoded the old decision, so it is replaced by one test for each side of the
new line.

On Evora this fixes GetLoginEndpoint and retires a stale MDL-FLOW01 on six
flows that already rebuilt correctly, with no new invariant violations.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Resolves the findings-file conflict GitHub reports for appends to the
shards (merge=union keeps both locally; GitHub's server-side merge does
not run merge drivers).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
ako and others added 28 commits October 8, 2026 17:25
Resolves the findings-file conflict GitHub reports for appends to the
shards (merge=union keeps both locally; GitHub's server-side merge does
not run merge drivers).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Resolves the findings-file conflict GitHub reports for appends to the
shards (merge=union keeps both locally; GitHub's server-side merge does
not run merge drivers).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Resolves the findings-file conflict GitHub reports for appends to the
shards (merge=union keeps both locally; GitHub's server-side merge does
not run merge drivers).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Converts the findings main appended to the old shards since the last
merge into per-finding files with scripts/split-findings.py.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
fix(check): a void microflow/nanoflow call's output name declares nothing (MDL063)
Resolves the findings-file conflict GitHub reports for appends to the
shards (merge=union keeps both locally; GitHub's server-side merge does
not run merge drivers).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
fix(check): MDL004 on a stored void microflow's return value is a warning
Resolves the findings-file conflict GitHub reports for appends to the
shards (merge=union keeps both locally; GitHub's server-side merge does
not run merge drivers).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Converts the findings main appended to the old shards since the last
merge into per-finding files with scripts/split-findings.py.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
chore(findings): one file per finding, so fix-PRs stop conflicting
main now stores findings one file per finding (#1054). This branch's
appended line in mdl-executor.jsonl is converted with
scripts/split-findings.py into its own file.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
main now stores findings one file per finding (#1054). This branch's
appended findings lines are converted with scripts/split-findings.py
into their own files.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
main now stores findings one file per finding (#1054). This branch's
appended findings lines are converted with scripts/split-findings.py
into their own files.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
fix(xpath): store an underscore-led enumeration value (`_True`) as a string literal
main now stores findings one file per finding (#1054). This branch's
appended findings lines are converted with scripts/split-findings.py
into their own files.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…invariant-remaining

main now stores findings one file per finding (#1054). This branch's
appended findings lines are converted with scripts/split-findings.py
into their own files.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…82-xlma7z

# Conflicts:
#	.claude/skills/fix-issue/findings/mdl-backend.jsonl
…34-2z9io3

# Conflicts:
#	.claude/skills/fix-issue/findings/mdl-grammar.jsonl
fix(exprcheck): lex `:` as Mendix division so comparisons over it type as Boolean
perf(check): list only the document kinds the script's statements ask about
fix(describe): irreducible microflow graphs describe as the stored flow
…14-5to4h3

# Conflicts:
#	.claude/skills/fix-issue/findings/mdl-executor.jsonl
#1056 merged into fix/describe-irreducible-graphs after #1055 had
already merged that branch into main, so its commits never reached main.
Bring the branch up to date so it can land on main directly.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Add module documentation and admin user name support (issue mendixlabs#1314)
test: pin mendixlabs#1182 — SET Caption and snippet restate with nl_NL stored first
Add ALTER PAGE/SNIPPET support for ADD/DROP parameters
…abs#1254) (#1060)

A decision whose caption was cleared in Studio Pro stores Caption ''.
Describe skipped an empty caption, and a decision with no @caption is
built with its condition as the caption, so the round trip added
"@caption 'true'" to the diagram.

Describe now prints @caption '' for an empty decision caption; the
visitor keeps an explicit empty @caption (ActivityAnnotations.CaptionSet)
and the builder applies it to if and enum splits.

Fixes mendixlabs#1254


Claude-Session: https://claude.ai/code/session_019Tfs5Hj34WTnLFtBTb8Bze

Co-authored-by: Claude <noreply@anthropic.com>
fix(describe): the 8 remaining flows describe as their stored graph (re-land of #1056)
@ako
ako merged commit 5c321d0 into mendixlabs:main Oct 8, 2026
15 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants