fix(test): read the after-startup setting from the colon-form describe line - #934
Conversation
…e line DESCRIBE SETTINGS now writes `AfterStartupMicroflow: 'Mod.Flow',`. The test runner split only on `=`, kept the whole line as the value, failed to chain the after-startup microflow, and restored the setting to `'AfterStartupMicroflow: ''Mod.Flow'`, leaving the project on MxTest.RegisterEndpoint. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FgupMwfjsUoszFq2kSn2p2
CheckSource closed each test block's wrapper with `END; /`, a `/` the author never wrote, so every test drew an MDL-V1-SLASH note. Records findings for this and the after-startup parse. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FgupMwfjsUoszFq2kSn2p2
An association to a System entity reconciled System's domain model, which is virtual and has no stored unit, so the create failed with "no such file" after the association was already written. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FgupMwfjsUoszFq2kSn2p2
create or modify workflow printed "Created workflow" on every run, including in-place updates whose write was elided. It now reports through ReportMutation like every other document type. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FgupMwfjsUoszFq2kSn2p2
entityMemberName took only IDENTIFIER or a quoted name, so attributes named Region, Status, Title, Value or Date could be declared bare but not granted. It now includes keyword, as attributeName does. The keyword-hint tests move to a workflow activity name, which still rejects bare keywords. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FgupMwfjsUoszFq2kSn2p2
…ntity CaptionAttribute was qualified with the context entity, so a caption on an attribute inherited from Administration.Account was stored against the specialization and mxbuild failed with CE1613. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FgupMwfjsUoszFq2kSn2p2
OnChange routed a combo box into the generic pluggable branch, which emits no Attribute: or CaptionAttribute:, so describe -> exec dropped the binding. It now counts only for widgets without a dedicated describe branch. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FgupMwfjsUoszFq2kSn2p2
…s balanced The @font-face pattern stopped at the `}` of the `#{$weight}` interpolation, leaving each dropped @each block's closing brace behind, so a seeded theme on any shipped base (console: 27 { vs 29 }) did not compile. The block's end is now found by counting braces. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FgupMwfjsUoszFq2kSn2p2
rename entity left the DomainModels$ViewEntitySourceDocument under the old name and persisted the entity's stale SourceDocument pointer, giving CE6784; recreating the view then orphaned the old document (CE6786). The rename now carries the pointer and renames the document, found by type and name. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FgupMwfjsUoszFq2kSn2p2
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FgupMwfjsUoszFq2kSn2p2
The rule took the entity-level READ row, which the catalog emits when any member is readable, so a role granted read on FullName alone was reported as reading Email. It now matches unconstrained MEMBER_READ rows on the PII attributes. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FgupMwfjsUoszFq2kSn2p2
"No access rules found matching …" read as a failed lookup on the idempotent re-run of a script; it now reads "Unchanged entity access: M.E (roles) — nothing to revoke". Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FgupMwfjsUoszFq2kSn2p2
…arse Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FgupMwfjsUoszFq2kSn2p2
`create or modify … if not exists` was reported as MDL067, the id the bare-commit INFO note (a bare `commit $X;` now runs events) also uses. One id for an error and an unrelated note made it useless for filtering or looking either one up. The guard error moves to MDL085, a gap no branch has ever used; the commit note keeps MDL067, which fmt --upgrade, mdl/upgrade and the released CHANGELOG already name for it. Docs, skills, the syntax help and the quick reference follow. The guard test now also asserts the old id is never reported for it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FgupMwfjsUoszFq2kSn2p2
The bare-commit note ("a bare `commit $X;` now writes WITH EVENTS; it used
to write them off") printed on every `exec` of an idempotent script, even
on a re-run reporting the flows unchanged, where the stored commits already
have WithEvents=true. It buried the warnings that apply.
The note is a script-only check and ValidateProgram has no backend, so the
filter runs in execPreflight, which holds the connected executor:
DropSettledCommitNotes asks StoredCommitEvents (built for fmt --upgrade -p)
for the stored flags and drops the note only when every variable committed
bare is stored with the same number of commits with and without events. A
flow not stored yet, a plain create, an added commit or a stored commit
without events keeps it. `check` is unchanged.
Measured on a Verify copy: run 2 of a two-flow script prints no MDL067;
after storing one flow `without events`, run 3 notes only that flow.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FgupMwfjsUoszFq2kSn2p2
exec's pre-flight printed every violation of the semantic pass in full, with no severity filter. On report pages MDL-WIDGET15 alone was ~35 info notes a run, burying the warnings and errors that matter on a re-run. exec (and diff, which shares the pre-flight) now prints errors and warnings in full and the info notes as one line: "N info notes not shown — run `mxcli check <file>` to see them (or pass --verbose)". The text summary counts them, marked "(not shown)", via a new TextFormatter.OmittedInfos, so it no longer reads "0 info" above that line. --verbose on exec and diff prints them in full. `check` is unchanged and still prints every note; exec has no structured diagnostics output, so check --format json|sarif are unaffected. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FgupMwfjsUoszFq2kSn2p2
A conditionally editable widget stores Editable "Conditional" beside its ConditionalEditabilitySettings, and describe printed both: `Editable: Conditional, Editable: <expr>`. The key appeared twice and the first value is not authorable - the expression is what makes it conditional. Measured on a text box (Mendix 11.14). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FgupMwfjsUoszFq2kSn2p2
The rendered wrappers live in MxTest, which only a test run creates, so check --references failed with "module not found: MxTest" and never resolved anything inside a test body. The rendering now declares the module on the first wrapper's line. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FgupMwfjsUoszFq2kSn2p2
A change inside a loop body is refused by both editing modes, as intended
(ADR-0012: neither the create-or-modify splice nor the alter mutator edits
inside a loop). But the advice was circular: create or modify under mdl 1
said "change activities with alter", and alter said "rewrite the loop with
create or modify". An alter of an activity in the loop that reads the
iterator (`replace commit $c with begin commit $c without events; end;`)
failed first on "the fragment uses $c, which is not declared on the path":
the scope check walks only the top-level flows, so it saw no path inside
the loop and reported the wrong reason before the mutator's real one.
- create or modify: the in-loop refusal carries the stored loop's handle
and names the alter that makes the change:
`alter microflow M.F { replace loop $c in $Cars with begin loop $c in
$Cars begin … end loop; end; };` (`end while;` for a while loop).
- alter: an operation aimed at an activity inside a loop (at any depth)
is refused up front with the same advice, naming the top-level loop to
replace, before any scope or fragment check runs.
- mfmutator: its own in-loop message advises replacing the loop instead
of create or modify.
create or modify still refuses rather than replacing the loop itself: that
would renumber and redraw the loop silently, the loss the refusal exists
to prevent; the explicit alter states it.
Measured on a fresh Mendix 11.14 app: the advised alter kept every object
and flow outside the loop at the same $ID and position, renumbered only
the loop and its body, and mx check reported 0 errors.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FgupMwfjsUoszFq2kSn2p2
write-microflows (pitfall 11, linked from the edit-mode paragraph) and write-nanoflows now say that neither create or modify nor an alter aimed at an activity in the loop can change a loop body, and show the `alter … replace loop … with begin loop … end loop; end;` form that does, including while loops and nested loops. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FgupMwfjsUoszFq2kSn2p2
…tors The nested-loop hint fired on every loop inside a loop, so intentional iteration over two independent lists drew it on every run. It now fires when the inner loop's body compares the inner iterator with an outer one, the key-lookup shape its message describes. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FgupMwfjsUoszFq2kSn2p2
…brahmagupta-q0qhgl
`check --references` passed two page errors only mxbuild reported: a textbox bound to an enumeration (CE2421) and the classic drop-down on a React-client project (CE0582). The project-tier widget walk now also judges built-in input widgets: - MDL-WIDGET39: textbox/textarea/datepicker/checkbox/radiobuttons/ dropdown bound to an attribute type the widget does not take, by a matrix measured with mxbuild 11.14.0 (every pair built). Inherited attributes and association paths are followed; script-declared entities and associations count. A bare association bound as the attribute (CE1613, which masks every other mx check error) is reported too. HashedString and Date are unmeasured and unjudged. - MDL-WIDGET40: a `dropdown` when WebUI.UseOptimizedClient is "Yes". Measured: "No" and "MigrationMode" build it clean, so neither is reported. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FgupMwfjsUoszFq2kSn2p2
Document the measured widget/attribute-type matrix and the MDL-WIDGET39 / MDL-WIDGET40 check rules in the widget reference and the create-page skill. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FgupMwfjsUoszFq2kSn2p2
The create-page widget reference showed `datasource: microflow M.DS($Param)` and the pitfalls page `call microflow M.F($v)`; both are parse errors. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FgupMwfjsUoszFq2kSn2p2
…brahmagupta-q0qhgl
exec's pre-flight did not run the flow verdicts check -p runs, so a script with a change that cannot be spliced was written up to that statement and stopped there. The pre-flight now refuses it before anything is written; with --continue-on-error the verdict is printed and the other statements run. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FgupMwfjsUoszFq2kSn2p2
Every node was judged as a 120x60 activity, so the 40x40 merges of a nested if/else that auto-layout places edge to edge were reported as overlapping. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FgupMwfjsUoszFq2kSn2p2
writing-java.md listed entity parameters as IMendixObject, lists as List<IMendixObject> and string templates as MendixObjectReference; the field mxbuild (and now mxcli) generates is the proxy class, a list of proxies, and String. Adds the enum, type-parameter and microflow rows, the generated shape for the basic example, and a pointer from SKILL.md. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FgupMwfjsUoszFq2kSn2p2
…dule role A new page, microflow or nanoflow in a module with no module roles is granted to an auto-created <Module>.User role, silently. Document GRANT is additive, so a later admin-only grant on a stub page left it open to <Module>.User too, and nothing on screen said why. exec now prints, under the create: access: granted to auto-created role Shop.User (the module has no other roles; a later grant adds to it — revoke it to narrow access) Only for a document exec creates and grants by default; a module that manages its own roles gets no default grant and no note. Documented next to GRANT (reference and language pages) and in the manage-security skill, including that drop + create across runs loses a flow's roles. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FgupMwfjsUoszFq2kSn2p2
… capital `sort by CreatedDate` and `sort by M.E.CreatedDate` — the spelling describe prints for an AutoCreatedDate declaration — passed check and failed mxbuild 11.14 with CE1613 "The selected attribute 'M.E.CreatedDate' no longer exists"; `sort by createdDate` and `sort by M.E.createdDate` build clean. check --references now reports a sort column whose last segment matches createdDate / changedDate case-insensitively but not exactly, where the entity provably has no attribute of that exact name, and names the right spelling (keeping the author's qualification). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FgupMwfjsUoszFq2kSn2p2
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FgupMwfjsUoszFq2kSn2p2
…iting-brahmagupta-q0qhgl
…ting-brahmagupta-q0qhgl
…brahmagupta-q0qhgl
… (CE6592) `grant read *, write *` on a System.FileDocument or System.Image specialization wrote HasContents (and PublicThumbnailPath) ReadWrite, and mxbuild 11.14 at security level Production reported CE6592 "Attribute 'HasContents' cannot have write rights, because it is a system attribute". Measured with write * on both specializations: HasContents and PublicThumbnailPath are refused; Name, DeleteAfterDownload, Contents, Size and EnableCaching are not. They are flagged WriteForbidden on meta.SystemAttrDef, and types.WriteRightsForbidden gains the third cause beside calculated and autonumber. The GRANT downgrades them to ReadOnly as it writes, and the reconcile's preserve branch — which cannot load System but can ask meta — downgrades a stored entry, so `update security` repairs rules written before. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FgupMwfjsUoszFq2kSn2p2
…ed under `revoke write (HasContents) on entity M.Doc from M.Role` on a FileDocument specialization answered "No access rules found matching M.Role on M.Doc" and changed nothing while the rule held ReadWrite. The partial revoke qualified each member with the statement's entity (M.Doc.HasContents), but a member access is stored against the DECLARING entity (System.FileDocument.HasContents) — which GRANT already resolves through EntityMembers. REVOKE now resolves member names the same way, falling back to the old qualification for a name the walk does not know. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FgupMwfjsUoszFq2kSn2p2
Two branches each took MDL-WIDGET39 for a new rule; the widget/attribute-type rule keeps 39 and the bare-expression rule becomes 42. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FgupMwfjsUoszFq2kSn2p2
…tion example The example access rule used `[owner = '[%CurrentUser%]']`, which mxbuild 11.14 rejects with CE0161: owner is an association to System.User and is addressed as System.owner in XPath. check --references now reports the bare spelling, so the example would have failed its own validation. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FgupMwfjsUoszFq2kSn2p2
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FgupMwfjsUoszFq2kSn2p2
…pta-q0qhgl # Conflicts: # mdl/executor/validate.go
Two branches each added a map from a script-declared association to its two entities; the retrieve-constraint walk now reads the one the widget walk uses. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FgupMwfjsUoszFq2kSn2p2
…agupta-q0qhgl # Conflicts: # .claude/skills/mendix/create-page/reference/widgets.md # CHANGELOG.md # cmd/mxcli/testrunner/check_source.go # docs-site/src/appendixes/error-messages.md
…tate Once describe printed a pluggable widget's stored Editable, the baseline the mendixlabs#1247 merge compares against carried it, and a replacement that did not mention Editable was read as stating the default. Visibility and editability the statement leaves out are now left out of the baseline as well. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FgupMwfjsUoszFq2kSn2p2
|
Cause: Studio Pro binds these widgets directly to the snippet parameter through
mxcli does not model that binding yet. Before this PR, describe dropped both properties, so the round trip lost them silently. This PR prints them, and Fix in progress: describe will print the binding as Generated by Claude Code |
MDL-WIDGET41 refused visibility on a widget whose package declares no Visibility system property, but Studio Pro stores conditional visibility on a Datagrid that declares none (TestApp WorkflowCommons.UserTask_Assign), and mxbuild accepts it, so describe -> exec of that page failed. Only Editable stays gated. The roundtrip allowlist shrinks for that page and two Rules pages that round-trip now. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FgupMwfjsUoszFq2kSn2p2
The image branch appended the conditional settings and then appendAppearanceProps, which appends them again, so Visible: (attribute value or expression) and Editable: printed twice (#721 C, TestApp WorkflowCommons.Snip_UserTask_NameColumnWithIcon). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FgupMwfjsUoszFq2kSn2p2
Forms$ConditionalEditabilitySettings.Conditions used the default list marker [3]; Studio Pro writes [2], as for visibility (both editability settings in TestApp, WorkflowCommons combo boxes with Editable: <expr>). A describe -> exec of either rewrote the snippet. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FgupMwfjsUoszFq2kSn2p2
A widget placed directly in a snippet or page, outside every data
container, binds an attribute of a parameter: Studio Pro stores the
AttributeRef (a combo box's IndirectEntityRef; a visibility setting's
Attribute) beside SourceVariable Forms$PageVariable {SnippetParameter |
PageParameter: name, Widget: ""}. Measured on TestApp WorkflowCommons:
Snip_TaskDashboard_Header / Snip_WorkflowDashboard_Header combo boxes,
Snip_UserTask_NameColumnWithIcon image visibility, and the built-in inputs
of Snip_WorkflowUserTaskView_Details and others.
describe printed the attribute bare, so exec refused it (no enclosing
object) now that describe prints the combo box's attribute and pluggable
Visible is stored. MDL now spells the source, extending the #826 form:
Attribute: $Param.Attr (inputs and combo box)
Attribute: $Param.Module.Assoc (combo box association)
Visible: $Param.Attr in (…)
describe prints it from the stored SourceVariable; the builders resolve
$Param against the declared parameter's entity and write the PageVariable
in the slot parameterSlotKind picks (inputs, the pluggable engine via
WidgetObjectBuilder.SetSourceVariable, ConditionalVisibilitySettings).
A name that is neither a parameter nor (on an input) an enclosing data
view is still refused, at exec and at check (MDL-WIDGET34). A bare
attribute at the root stays refused.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FgupMwfjsUoszFq2kSn2p2
…ram-binding Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FgupMwfjsUoszFq2kSn2p2
Binding a widget to a snippet parameter lets six snippets round-trip outright and six more execute; those now break getput instead of exec, and three lose putget. Entries only lose laws, apart from exec giving way to the getput it had masked. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FgupMwfjsUoszFq2kSn2p2
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FgupMwfjsUoszFq2kSn2p2
DESCRIBE SETTINGS now writes
AfterStartupMicroflow: 'Mod.Flow',. The testrunner split only on
=, kept the whole line as the value, failed to chain theafter-startup microflow, and restored the setting to
'AfterStartupMicroflow: ''Mod.Flow', leaving the project onMxTest.RegisterEndpoint.
Co-Authored-By: Claude Opus 5.5 noreply@anthropic.com
Claude-Session: https://claude.ai/code/session_01FgupMwfjsUoszFq2kSn2p2