Repository navigation
Sync ako/mxcli: error handlers in branches, REST body expressions, rename fixes, IMPORT LINK, pluggable widgets patched in place - #1647
Merged
Merged
Conversation
The issue-triage skill only classified mirrored upstream issues and left the fork's own issues unlabelled, so a cluster showed only half of what the maintainer tracks. Fork issues (open, not `mirror`, not the board) now get the same cluster and class labels and triage comment, ahead of mirrors within MAX_TRIAGE. They are never closed, parked or retitled in any mode; a mirror that duplicates a fork issue is the one closed. The board shows mirrors and fork issues per cluster in separate columns. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NTDRrgJhKKmSUnBFaDUw62
Triage the fork's own issues alongside the mirrors
…uggable widgets and layouts (#549) Where Studio Pro stored DisabledDuringExecution false on an unused action slot (a Forms$NoAction), a rewrite still wrote true over it in two places the #721 L2 carry did not reach: - a pluggable widget the statement changes, rebuilt from its template: its slots are WidgetValues inside the Object, not child actions of a named widget. On ako/TestApp, re-running describe of WorkflowCommons.Snip_TaskDashboard_Header with only dropDown2's emptyOptionText changed passed check, reported "Replaced snippet" and turned dropDown2's onEnterEvent and onLeaveEvent true. - a layout rewrite, which never called the carry. The rebuilt pluggable widget's NoAction slots now take the stored widget's flag, paired by property key through each side's own Type; a slot the statement fills with an action keeps its own settings. Layout rewrites carry like page and snippet rewrites. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NUbjJ5DeS2vysn9hfKir4Y
fix(pages): keep a stored DisabledDuringExecution false on rebuilt pluggable widgets and layouts (#549)
PostgreSQL treats AS as optional in UPDATE ... FROM; SQLite requires it. Spelling it out changes nothing against a Mendix database and lets the import pipeline run end to end against an in-process SQLite database in unit tests, which the fix for #1454 relies on. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ebuilding it (#832) A pluggable widget the passthrough declined was rebuilt whole from the template, so every property the statement did not mention lost what Studio Pro had stored. The passthrough declines an edited widget, and also an unchanged one whenever describe prints it imperfectly: on ako/TestApp, re-running `describe page WorkflowCommons.TaskInbox` unchanged rebuilt both data grids, turning the Assignee(s) column's Administration.Account.FullName into System.User.FullName (CE1613) and the row click that opens the task into a NoAction. Rebuilding every pluggable widget of TestApp changed 23 distinct untouched properties. Layout rewrites never loaded the passthrough at all. A declined widget is now patched: it is built a second time from describe's own text for the stored widget, the two builds are compared by property key, and only the properties they disagree on are written, onto the stored Type. A property bound to the widget's own data source is kept while that data source is; any other property naming model elements is kept only while it names what is stored, so a data view moved to another entity still rebinds. Widgets properties always come from the statement's build. Layouts load the passthrough, and the docs say a rewrite keeps a widget's stored schema (use `mxcli widget sync`). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NUbjJ5DeS2vysn9hfKir4Y
…#1652) `textfilter tf (Attributes: [Name], Placeholder: 'Search name', Adjustable: false)` inside `create page` passed check and exec and stored neither value; describe printed a bare `textfilter tf (Attributes: [Name])`. `alter page … set (placeholder: …, adjustable: …)` on the same widget wrote both. Mirror of mendixlabs#1643. Cause: the filters' hand-written definitions map neither key, so both go through the widget engine's explicit-property pass, which looked the authored name up in the template's property keys byte-for-byte. `Adjustable` is not `adjustable`, and `Placeholder:` is a grammar keyword the visitor always stores as "Placeholder", so no spelling reached the template's `placeholder` -- not even the lower-case one describe prints. check's allow-list and its "persisted by the explicit pass" verdict, the mapping pass (lookupProperty) and ALTER's setter (#1069) all fold case; the create writer was the one reader that did not. Fix: explicitPropertyKey resolves the authored name to the template's own key -- exact match first, then a case-only match that is unique and of a kind the pass writes correctly (the set check already calls persisted) -- and the pass writes and scopes by that key. Authored names are iterated sorted, since two spellings can now land on one key. The number and date filters' identical placeholder/adjustable pair and the text/number filters' `delay` are fixed by the same change, as is any unmapped key on any pluggable widget written in a different case. Verified on Mendix 11.13.0: the repro stores the placeholder (identical to the value ALTER writes, ids stripped) and adjustable "false", describe prints both, describe -> exec reports "Unchanged page", raw mx check 0 errors. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…their line breaks (#1506) `alter entity Mod.Ent rename attribute Old to New` rewrote the stored XPath constraint of every document whose constraint carries a line break between predicate groups -- in every module, whether or not it named the attribute -- turning `[Status = 'Open']\n[Code = 'X']` into `[Status = 'Open'][Code = 'X']` and reporting each as "Updated N XPath constraint(s) in N document(s)". On a larger project one rename wrote 147 units in 21 modules (mendixlabs#1596). Cause: rewriteConstraint split the constraint with SplitXPathPredicateGroups, which returns the groups without the whitespace between and around them, and re-joined them with "" even when no group changed. The scanner decides whether a unit changed (and counts a rewrite) by comparing that result with the stored string, so the lost separators alone made a write. canon.Reconcile could not elide it: the content really differed. Fix: the splitter gains XPathPredicateGroupSpans, which returns each group's offsets in the string as given. rewriteConstraint returns the stored string itself when no group changed, and otherwise splices each rewritten group back at its offset, copying every byte outside the groups -- so a constraint that does name the attribute keeps its layout and changes only the name token. Tests: byte-identical results for unrelated multi-line constraints (another entity's same-named attribute, a string literal, a longer name sharing the prefix), layout kept on a real rename, a scan test whose control unit is written, and an executor test pinning the reported count. Reverting the fix fails them with the reported symptom ("Updated 3 XPath constraint(s) in 3 document(s)", unrelated units written without line breaks). Verified on Mendix 10.24.24 and 11.13.0 with stored line breaks: before, 4 constraints in 4 units written; after, 2 in 2 (the referencing retrieve and the access rule), line breaks kept, mx check 0 errors. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Upstream: mendixlabs#1557. Cause: the import read mendixsystem$association.storage_format into a string and compared it with "Column" / "Table". The runtime stores a smallint there: 1 for an FK column on the owner's table, 2 for a junction table. Measured with psql on apps booted by Mendix 10.24.24, 11.12.2, 11.13.0 and 11.14.0, all four store the same values. Neither comparison ever matched, so a column-stored LINK left the FK out of the INSERT, a table-stored LINK skipped the junction insert, and the import printed "<Assoc>: 1 storage" and finished with exit 0. This was not an 11.14 change. LINK had never linked anything against a real database, for either storage kind. Fix: - LookupAssociationInfo decodes storage_format: 1/2, their decimal text, and the model names (any case). Anything else, NULL included, is an error that names the association. - ExecuteImport refuses a LINK whose StorageFormat is neither Column nor Table before it writes the first row, so an unplaceable link can no longer pass silently. - The executor prints a link count for every LINK, zero included. Tests: sql/import_assoc_test.go runs the import pipeline end to end against SQLite fixtures that mirror the measured system rows. With the fix reverted, both storage kinds report "0 linked / 0 missed, want 4 / 1", storage_format 1 decodes to "1", and an unrecognised format imports without error. End to end on 10.24.24, 11.12.2, 11.13.0 and 11.14.0, psql showed 0 FKs and 0 junction rows with the old binary and 4 + 4 with this one. On 11.14 an OQL read through the booted runtime showed the same links. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
fix(pages): patch a stored pluggable widget per property instead of rebuilding it (#832)
…1443) `rename page Shop.Order_Overview to Order_List` (MDL or `mxcli rename`) on a page in a folder named 'Order_Overview' renamed the folder, left the page as it was, printed "Renamed page: ..." and had already pointed every caller at the new name, so mx check reported CE1613 "The selected page 'Shop.Order_List' no longer exists". The same held for every kind RENAME handles as a document: microflow, nanoflow, constant, enumeration, Java action and workflow. Mirror of mendixlabs#1545. Cause: the handlers resolved the document among the requested kind only to set a `found` flag, then called RenameDocumentByName(module, old, new), which looked the bare name up again over every unit in the module's containers, whatever its $Type, and rewrote the first match. A Projects$Folder is such a unit, and a FOLDER clause inserts the folder before the document it holds, so the folder won. The reference sweep ran first, so the damage was done either way. Fix: remove the second lookup. The handlers keep the ID of the document they found and call RenameDocument(id, oldName, newName), which replaces RenameDocumentByName on the backend interface and refuses a folder or module unit and a stored Name other than oldName. The document is now renamed before the reference sweep, so a refused rename leaves every reference untouched. Verified on a blank 11.12.2 app with the issue's repro plus nanoflow, constant, enumeration, Java action and workflow: folders keep their names, references follow the documents; the bug-test script goes from 4x CE1613 (main) to 0 errors. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…text (#1444) `call rest service post '…' (Body: $Payload)` was stored as the custom request template '$Payload' with no parameters, so the request body was the eight characters "$Payload". check, exec and mx check all passed; mx check only warned that $Payload was never used (CW0094). The deprecated `body $Payload` clause built the same statement, and a URL written as an expression (`@M.BaseUrl + '/rates'`) was stored as that text, so the request never reached the server. Upstream: mendixlabs#1546. Cause: addRestCallAction turned any URL or custom body that was not a string literal into fb.exprToString(...) and stored that source text as the Microflows$StringTemplate's Text. Fix: an expression is now the template '{1}' with the expression as its one parameter. This is what Studio Pro stores (ako/TestApp Mappings.PostCustomRequestTemplate_2, 11.13.0: Text '{1}' plus one TemplateParameter), and it is the rule log, show message, validation feedback and send email already follow. A string literal is still the template text, including one that spans lines (a SourceExpr around the literal). An expression followed by `with (…)` has no {n} to fill, so it is refused as MDL-REST05. checkRestCallTemplatesStmt makes that decision for both the check pass and exec. declaredMatches normalizes both spellings the same way, so `create or modify` re-run against the stored flow reports the activity unchanged. Describe is unchanged: it prints `Body: template '{1}' with ({1} = $Payload)`, which re-executes to the same document. Verified on an 11.13.0 app. The CW0094 for $Payload is gone and mx check reports 0 errors. Describe -> exec reports "Unchanged microflow", and so does re-running the original `Body: $Payload` spelling. At runtime an echo server received `{"number": "N-1444"}` where the baseline sent "$Payload", and the expression-URL request now arrives (the baseline's never did). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…low instead of continuing after the failing activity (#1459) Upstream: mendixlabs#1565. A custom error handler that does not end in RETURN or RAISE ERROR on an activity inside an IF branch was wired to the wrong place. With a result variable (`$N = call microflow …`, `$Obj = create …`, `$R = call rest service … returns response`) the handler's last activity flowed into a merge spliced in front of the enclosing split, so a call that kept failing re-entered the decision for ever. Without one it flowed into a new end event and nothing after the failure ran; with `returns … as $Result` that end returned a variable that does not exist (CE0109). In a THEN ending in RETURN the handler skipped to the code after the IF. The same held in CASE and type-split arms, nanoflow loop/while bodies and `while true`. check, exec and mx check passed. Cause: pending fall-through rejoins live in one builder-wide slot that only buildFlowGraph's top-level loop settled, on the flow leaving the failing activity. The branch body loops settled only an empty handler, so a non-empty one leaked out of the branch and the enclosing loop settled it on the next flow it drew: the skip-var path (any activity with an output variable) ignores the origin and spliced it into `previous -> split`; without a variable nothing matched and terminatePendingErrorHandlersAtEnd gave it an end event; a loop sub-builder dropped it. Fix: scope the slot to each branch body (enterBranch/leaveBranch). Every flow a branch draws settles the branch's own handlers (settleBranchFlow), including its exit into the IF/CASE merge; only what a branch cannot settle is handed back to the enclosing path — the last activity of an arm that continues without a merge. The enclosing path's handlers are no longer visible to a branch's flows either. In an arm that ends in RETURN a skip-var handler that would have to skip a statement reading the failed output still continues where the IF does (a30881b's shared continuation), but the IF's merge is now created only when an arm actually leaves such a handler, instead of whenever the arm has a continuing handler. A rejoin merge spliced into a branch's exit stays on the branch's row. Verified: describe -> exec round trip unchanged on every shape, mx check 0 errors on 11.12.2, and at runtime a failing call logs 'failed', 'after call', 'end' once each (before: 3063 'failed' lines in five seconds). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…pped fix: a filter's Placeholder/Adjustable in create page are not written (#1652)
…-xpath fix: rename attribute rewrites unrelated XPath constraints and drops their line breaks (#1506)
fix: IMPORT ... LINK reports success and links nothing (#1454)
fix: rename of a document in a same-named folder renames the folder (#1443)
fix: call rest service sends an expression body or URL as its source text (#1444)
fix: a fall-through error handler inside a branch loops or ends the flow (#1459)
The storage_format section added for #1454 grew "Step 4" by seven lines without regenerating the skill's line index, so every range after it pointed seven lines early. `make build` (skill-index) rewrites it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…1455) Inside `on error … end error`, an `if` without `else` whose THEN ends in RETURN passed check and exec, but its decision left on `true` and NoCase: mx check reported CE0079 at the decision and CE0773 at the flow, and describe read the NoCase edge back as an `else`. The same `if` in the main flow was stored with true/false. Mirror of mendixlabs#1558. Cause: such an `if` has no merge, so it leaves its FALSE edge pending — the case (and @anchor) the NEXT edge must carry. addErrorHandlerFlow's handler-body loop kept the case in lastErrCase but drew the edge to the next statement with a plain newHorizontalFlow; every other body loop takes the pending case and anchor there. Fix: draw that edge with newHorizontalFlowWithCase when a case is pending, and apply the deferred anchor through pendingFlowAnchors, as the IF arms do. Tests assert the decision's edge cases directly (describe text hides the defect), with a main-flow decision as the control and a describe round trip. Reverted fix: every handler decision leaves on ["" "true"]; reverting only the anchor half fails 1→3 vs bottom→top. On Mendix 11.13.0 the bug-test script goes from 10 errors (5× CE0079, 5× CE0773) to 0, describe → exec is Unchanged, and re-running the script leaves all documents in sync. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ld one (#1671) `rename constant M.C_Root to C_Base` printed "Renamed constant" with no reference count, and mx check then reported CE1613 "The selected constant 'M.C_Root' no longer exists" wherever an expression used it. A cross-module `move constant` the same. `rename enumeration` renamed a value that was the whole expression but not one inside a longer one (CE0117). Cause: the reference sweep's two primitives (replaceQNInValueCounted for RENAME, replaceQualifiedNameRef for cross-module MOVE) rewrite a stored string only when it equals the old qualified name or starts with it plus '.'. An expression always writes a constant as @Module.Name, so not even a constant that is the whole expression matched; an enumeration value matched only as the whole expression. Fix: types.RewriteExpressionReferences rewrites, as whole tokens, @oldQN (a constant) and oldQN.Member (an enumeration value) not preceded by a name byte, '.', '/', '$' or '@' — so neither a longer name, another module's element, nor an association path step — and skips single-quoted literals ('' escapes included). Both primitives apply it to any string the whole-string match leaves alone. A module rename's prefix pair is left to its own path. The `mxcli syntax rename` topic says what is now covered. Tests: 20 rewriter cases (XPath form included, which MDL cannot author yet) and an executor test on PedApp covering rename constant, rename enumeration and a cross-module move, with a string literal that must stay verbatim. Reverted fix: every expression use stays stale; reverting only the MOVE primitive fails only the move assertions. Mendix 11.13.0: the issue's rename goes from 3x CE1613 to 0 ("Updated 3 reference(s)"), the enumeration rename from CE0117 to 0, the bug-test script from 4 errors to 0. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…the old module (#1674) `move enumeration Shop.Status to Other;` printed "Moved enumeration", but every reference outside the domain models kept naming the old module: mx check reported CE1613 at a parameter of the enumeration type, a create-variable activity (type and value), an end event returning a value, and the return type. Entity attributes of the type were updated. Cause: execMove's dispatch did `return moveEnumeration(…)`, returning before the shared cross-module reference sweep (updateQualifiedNameRefs) that every other document's move falls through to. moveEnumeration itself only updated domain-model attribute types. Fix: the enumeration case falls through like the others, so a cross-module enumeration move runs the sweep and reports "Updated references in N document(s)". Test: an executor test on PedApp moving an enumeration used as a parameter type, return type, variable type and value, with the entity attribute as the control; against the unfixed code every microflow reference stays on the old module. Mendix 11.13.0: the issue's repro and the bug-test script go from 5x CE1613 to 0 errors. An enumeration value inside a LONGER expression additionally needs the expression-aware sweep of #1671 — measured: this fix alone leaves CE0117 on `$In = X.Open or …`, both together 0 errors. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
fix: an if without else inside an error handler loses its false case (#1455)
fix: rename constant leaves @Module.Const in expressions naming the old one (#1671)
fix: move enumeration to another module leaves its references naming the old module (#1674)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Syncs
ako/mxcli:maininto upstream: 10 commits from fork PRs ako#1662–ako#1670.Fixes #1545, #1546, #1557, #1565, #1596 and #1643.
Microflows
call rest service … (Body: $Payload)sends the expression, not its source text (call rest service … (Body: $Var)is stored as the literal template'$Var', so the request body is the text "$Var" #1546). The body was stored as the literal template'$Payload', so the request sent those eight characters. check, exec and mx check all passed, and mx check only warned with CW0094. A URL written as an expression had the same problem.Renames
rename pagerenames a folder with the same name instead of the page, reports success and repoints every reference to a page that does not exist #1545).rename page Shop.Order_Overview to Order_List, on a page in a folder calledOrder_Overview, renamed the folder instead. It printed "Renamed page" and repointed every caller, and mx check then reported CE1613. This affected every kind RENAME handles.alter entity … rename attributeno longer rewrites unrelated XPath constraints (alter entity ... rename attributerewrites XPath constraints in documents that never reference the attribute and drops their line breaks #1596). Every stored constraint with a line break between predicate groups was rewritten, in every module, whether or not it named the attribute, and lost its line breaks.Data import
IMPORT … LINKlinks again (IMPORT ... LINK silently links nothing on Mendix 11.14 (storage_format is integer 1, code expects "Column") #1557). It reported success and linked nothing.mendixsystem$association.storage_formatwith "Column" / "Table", but the runtime stores a smallint there: 1 for an FK column, 2 for a junction table.AS. That changes nothing against PostgreSQL, and it lets the import run end to end on SQLite in unit tests. It is a refactor, in its own commit.Pages
Placeholder:andAdjustable:increate pageare written (textfilterPlaceholder:/Adjustable:in create page pass check and exec but are not written;alter page setdoes write them #1643). They passed check and exec but weren't stored, whilealter page … setwrote both.System.User.FullName, which gave CE1613.DisabledDuringExecution = falseis kept on rebuilt pluggable widgets and layouts.Fork tooling
issue-triageskill classifies the fork's own issues alongside the upstream mirrors