retrieve … first is the object; limit 1 is a list of one under mdl 1 (#734) - #740
Merged
Merged
Conversation
…t mandatory under mdl 1 Each Studio Pro List operation / Aggregate list activity is one statement whose operand is a variable, so nesting cannot be written: $Open = filter $Orders by Status = M.Status.Open; $Big = filter $Orders where $currentObject/Total > 1000; $N = count $Open; $Csv = reduce $Orders from '' as String using $currentResult + $currentObject/Name; The statement forms parse under every version. The call forms keep parsing: a respelling everywhere except find/contains, registered as MDL-DEPR003 (list operations) and MDL-DEPR004 (aggregates), both building the same AST. find(...)/contains(...) clash with the string functions, so they, a list call after `set`, and a nested call are version-gated (MDL-V1-LIST): kept and warned under mdl 0, refused under mdl 1, where `set $x = find(...)` is always the string function. A reassignment without `set` is refused under mdl 1 and warned under mdl 0 (MDL-V1-SET). `where` is always Find/Filter by expression; `by` and the call form keep the by-member reading when the condition is `Member = value` (ast.IsMemberEquality, shared by visitor and flow builder). Part of #733. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
describe writes the language of the newest frozen version, or of the script it runs in when that is newer. While mdl 1 is a preview a plain describe keeps the call form; inside an `mdl 1;` script it prints the statement form, which executes back to the same microflow (PedApp test). Part of #733. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…forms Part of #733. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…lementIDs, Writer.UpdateRawUnitPatch) TransplantIDs pairs elements by type and position, which re-pairs the surviving flows of a patched microflow onto their neighbours' $IDs after a drop. A write that started from the stored bytes skips it; elision and the storage-GUID guard still apply. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…e, replace, drop) Splices a fragment into the raw stored unit: appends only the fragment's objects and flows, rewires the flow around the target by its pointers and the rewired end, moves nodes past the insertion point to make room, and never rewrites an $ID. Save refuses a unit in which anything still points at a removed element or two elements share an $ID. Refuses what it cannot do safely: after a decision, before a join, inside a loop body, drop/replace of a decision or of an activity with an error handler, and a placement that would overlap an object. The modelsdk backend writes through UpdateRawUnitPatch (canon.Reconcile). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…#736) alter microflow|nanoflow M.F { insert after|before <target> { … } replace <target> with { … } drop <target>; }. Targets are the #713 content addresses, resolved against the stored flow before any operation runs. Fragments are built with the create-microflow builder, seeded with the flow's variables, and cut out of their start and end events. A fragment variable that clashes with one the flow has, or one it reads that is not declared upstream of the insertion point, is an error; so is dropping an activity whose output is still read. Acceptance on PedApp VAL_Feedback: only the new log, the two flows around it and the shifted positions differ; an empty alter writes nothing. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… handle
A handle has to parse as an alter target, and a target ends at the { of a
fragment.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Runs the shared splice on the stored flow and sends the difference as ped_update_document path operations in one update, after checking the live document still matches the .mpr (PED addresses entries by index). Drop and replace are refused: PED does not roll back a removal when an update fails. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Loop body flows are stored in the unit's Flows list, not in the loop, so removing a Studio Pro loop with two or more body activities left them pointing at removed objects and Save refused the unit (TestApp ACT_ConflictedWorkflowHelper_ApplyJumpTo). mx check 11.14 on the dropped and replaced copies gives the baseline error list. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Each operation was checked against the flow as stored only. Measured with mx check 11.14 on TestApp: two fragments declaring the same variable gave CE0111, and a fragment reading a variable a drop in the same statement removed gave CE0109, after "Altered microflow". The context now tracks what earlier operations declared, read and removed. Also count a fragment loop's iterator as the fragment's own, so loop fragments are no longer refused. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…dl 1 `first` is Mendix's "First object" range in every language version. Under the mdl 1 header a bare `limit 1` is a Custom range, a list of one; without it the alpha meaning (the object) is kept and warns MDL-V1-LIMIT1. The visitor resolves the meaning into RetrieveStmt.First, so the writer, the variable typing and MDL-RETRIEVE01 no longer read limit text. describe prints the object range as `first`. `first` on an association retrieve is refused (that source has no range). MDL-RETRIEVE01 keys on the object range and names CE0100 for a loop, as measured with mx check 11.13. Tests: visitor per version, builder range per version, check per version, describe spelling, and a PedApp round trip of both forms with a headerless control. Part of #734. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…nder mdl 1 CLAUDE.md idiom 5, the quick reference, the syntax topic, the skills and docs-site pages that taught `limit 1` for an object now write `first` and say what `limit 1` means per language version. CHANGELOG entry. Part of #734. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This was referenced Sep 27, 2026
Closed
11 tasks
ako
added a commit
that referenced
this pull request
Sep 27, 2026
…forms On top of #740/#745/#741/#738, `fmt --upgrade --header` refused almost every example script: those PRs gated new constructs on the header without a rewrite. Each construct with a mechanical, meaning-preserving rewrite now has one, computed from the parse tree by the visitor that records it (ast.Fix, rune-offset edits, mdl/visitor/visitor_upgrade_fixes.go): - MDL-V1-SEMI: add the missing `;`. MDL-V1-SLASH: delete the `/` line. - MDL-V1-ESCAPE: write the literal's mdl 0 value with '' as the only escape; no edit inside an expression stored as written (mdl 0 already passed the backslash through); an escaped line break in a re-rendered expression is reported, since writing it into the literal changes what the builder stores (measured: a log message becomes a `{1}` template parameter). - MDL-V1-LIMIT1: `limit 1` -> `first`. MDL-V1-SET: add `set`. - MDL-DEPR003/004 and MDL-V1-LIST: call form -> statement form; find and contains on a declared String keep the call and gain `set`; a nested call, or an operand whose type the script does not state, is reported. - MDL-V1-REPLACE02: `create or replace user role|demo user` -> `create`. MDL-V1-PROP, MDL-V1-PROPVALUE and MDL-V1-REPLACE01 have no mechanical rewrite; they are listed in `unrewritable` (may only shrink) and block the header with HeaderBlockedError, which names each construct and why. TestGatedRegistryIsComplete holds every visitor.LanguageChanges() code to exactly one of the two lists. The examples test lists the two corpus scripts that keep mdl 0 (keepsItsVersion) and the two whose AST differs but whose model is the same (buildsTheSameModelNotTheSameAST), both may only shrink. The execute-both property test upgrades a blocked script without the header, and by default skips scripts whose only edits are the header and terminators, which never reach the AST: 249 scripts execute (6 min); MXCLI_UPGRADE_ALL=1 runs all 701, 587 of them executing to the same model. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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.
Closes #734 (plan item 2.5 of PROPOSAL_mdl_beta_syntax_freeze.md, §5 item 2; ADR-0011).
What changes
retrieve … firstis Mendix's "First object" range (ConstantRange, SingleObject) in every language version.mdl 1;, a bareretrieve … limit 1is a Custom range: a list of one.limit 1keeps its alpha meaning, the object.check/execwarn MDL-V1-LIMIT1, alangver.Changedeclared inmdl/visitor/visitor_retrieve_range.go.describeprints the object range asfirst. That text means the same under any header.RetrieveStmt.First. The writer, the flow builder's variable typing, the validator's typing and MDL-RETRIEVE01 all read that field and never read limit text.check --referencesdoes not otherwise catch. The message now says CE0100 for a loop:mx check11.13 reports a loop over an object as CE0100, not CE0097.syntaxtopic, the skills and the docs-site pages now teachfirstfor an object. There is also a CHANGELOG entry.Design choices the ADRs did not settle
firstdoes not combine withlimitoroffset. The grammar isfirst | [limit n] [offset n], because Mendix's object range has no offset.firston an association retrieve is refused (a new construct, so this is no new rejection of an existing script). Mendix gives that source no range, so the keyword would be dropped.limit 1" is the exact condition the alpha writer used: limit text1and no offset.limit (1),limit $Oneandlimit 1 offset nwere already lists, and they neither change nor warn.limit 1 offset nas an object while the writer stored a list. It now follows the stored range (a list), because it readsFirst.1and no offset still describes aslimit 1. Re-running that output without a header therefore turns it into an object, as it did before. The difference now is that the run warns MDL-V1-LIMIT1. mdl 0 has no spelling for that range. It round-trips once describe emits themdl 1;header at beta (langver.Frozen).languageVersionOf(tree)reads the header from the parse-tree root. This is the same approach as MDL list operations as one statement per Studio Pro activity; set mandatory under mdl 1 (#733) #737'sscriptLanguageVersion. Whichever PR merges second should fold one into the other.Test plan (what I ran)
make build;make lint(Go + TS) passed.go test -count=1 ./mdl/visitor/ ./mdl/executor/ ./mdl/ast/ ./mdl/grammar/... ./mdl/formatter/ ./mdl/linter/... ./cmd/mxcli/...passed.go test -tags integration ./mdl/roundtrip/passed. The allowlist is unchanged: PopulateUserAttributes stays getput-allowlisted for Round-trip harness: untracked describe → exec losses on the Studio Pro fixture (microflows, entities, pages, snippets, menus, roles, JS actions) #721.go test -tags integration ./mdl/executor/ -run TestRoundtripMicroflow_RetrieveWithpassed. Its expectation was updated tofirst.mdl/visitor/retrieve_first_test.go: each spelling under both versions, plus the refusals.mdl/executor/cmd_microflows_retrieve_first_test.go: the stored range and variable type per version, thedescribespelling, andcheck(MDL-V1-LIMIT1 / MDL-RETRIEVE01) per version.mdl/roundtrip/retrieve_range_test.goon the Studio Pro-authored PedApp (FeedbackModule.PopulateUserAttributes):describeprintsfirst, and exec under both headers keeps the ConstantRange and PutGet.mdl 1stores a CustomRange with limit 1, and itsdescribe→ exec undermdl 1keeps it (PutGet).limit 1is always the object) failedTestRetrieveLimitOne_Mdl1IsAListOfOne.describeprintlimit 1for the object range failedTestFormatAction_Retrieve_RangeSpellingandTestPedAppRoundTrip_RetrieveRange.TestRetrieveRange_WrittenPerLanguageVersion.mxcli docker check, mxbuild 11.13.0) on a copy of the Studio Pro project TestApp (baseline: 0 errors):mdl 1;script with a loop overlimit 1and afirstobject gave 0 errors.check/exec(MDL-RETRIEVE01). Forced with--no-check, mx reportsCE0100 'Users' is of type System.User, but should be of type List.🤖 Generated with Claude Code