Repository navigation
Sync ako/mxcli: domain model SVG, SEC010 unreadable data, test ports, mxbuild watchdog - #1394
Conversation
describe --format svg domainmodel <Module> writes a finished SVG: entities at the positions the model stores (where Studio Pro shows them), coloured by kind, with attributes, associations and their multiplicities, and generalizations. No layout engine runs, so a client with no browser (the Claude Code companion) can show it as is. Associations between the same two entities are drawn side by side; one to another module's entity, which has no position here, is left out. domainmodel is now a describe type with no MDL form, like systemoverview, so --format elk domainmodel <Module> works too. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Selecting a module's Domain Model node in the desktop app or VS Code shows the SVG mxcli draws, with hover tooltips; the terminal lists the entities as before. Diagrams are cached per model generation like MDL. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ll-width view A surface scales an Svg to its slot, so a 700-2200 px domain model shown whole in the source column was too small to read. Diagrams now open at actual size, cropped to the column through the root viewBox, with ◀ ▶ to pan across; Fit shows the whole drawing; Hide tree gives the diagram the pane's full width. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
An interactive Svg draws in a sandboxed frame, which does not size itself to its content the way an image does: with no width and height the domain model showed as a ~90x110 px thumbnail in the desktop app whatever the zoom. The element now gets its size in CSS pixels: the column's width at Fit, the window's size at 100%. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
#1103 added SubmitBehaviour and grid Width lines to the create-page skill without regenerating its line-numbered index, so make check-skill-index fails on main. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012PkPkaYM12u8yqMKNmzBgT
Boxes were sized for a monospace font and ran into the entities Studio Pro places beside them. They are now sized for a proportional font, and rather than clip names the way Studio Pro's fixed-width boxes do (a common complaint), the stored layout is pulled apart along x, then y, just enough for every box to fit its longest line, capped at 2.5x. Past the cap a box is clamped to the gap and its text clipped, the tooltip keeping all of it. On Evora's Teamcenter model: no clipping, the canvas 1.4x wider. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A test body whose CALL threw reported only "FAIL:exception during execution": rewriteBodyForVerdict set a fixed verdict and discarded the error. It now appends $latestError/Message, the expression throwsHandler already uses (measured on mxbuild 11.6.0 and 11.13.0). The test-microflows skill now states that tests run in a system context with no current user: [%CurrentUser%] throws, $currentUser is empty, and user-scoped logic is tested by passing the user as a parameter. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012PkPkaYM12u8yqMKNmzBgT
`mxcli test --local` hard-coded 8081/8091/6544, so a machine with 8081 taken (an editor helper) could not run tests. The three flags `run --local` has now exist on `test`, carried through RunOptions into the boot options and the endpoint client; defaults are unchanged. Each port is probed before the project is touched, and a taken one fails with the port, its role, the flag that moves it and (on Linux) the holding process. The flags are refused with --attach and on the Docker path, and out-of-range or colliding values are refused up front. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012PkPkaYM12u8yqMKNmzBgT
… template A column with no caption, no attribute and no name had nothing to fall back to, so applyColumnHeaderFallback left the header empty and it was serialized as TextTemplate: null. mx update-widgets writes an empty Forms$ClientTemplate there, so mx check reported CE0463 until the next fix widgets, and every re-exec brought it back. Flag the header EmptyTemplate instead, scoped to items with a header slot. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012PkPkaYM12u8yqMKNmzBgT
mxcli init named the project after the folder it ran in, so a cloud session (which clones into a folder named after the repository) got a CLAUDE.md titled "Mendix Project: seed" for an app called ChipCoV8, and a devcontainer with the same name. Use the .mpr file name, falling back to the folder when there is none, in init, add-tool and the --sync-skills doc refresh (which would otherwise retitle it on the next session). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012PkPkaYM12u8yqMKNmzBgT
… release .claude/bootstrap-mxcli.sh fetched the nightly whenever MXCLI_TAG was unset, so a project initialised by a pinned release got a different mxcli back after every idle reap. Write the generating binary's version as the default when it is a plain vX.Y.Z release; dev and nightly builds keep nightly, since they have no fixed download. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012PkPkaYM12u8yqMKNmzBgT
The skill said the newest Mendix version "at the time of writing" was 11.14.0, a number that ages the moment it is written. Keep only the procedure: probe the CDN for the newest version with both tarballs and step back a minor on a 404. Regenerating the embed also refreshes create-page's stale skill index. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012PkPkaYM12u8yqMKNmzBgT
… stub
resolveMacOSJavaHome hard-coded `java_home -v 21`, so for an 11.14+
(Java 25) project the answer always failed the major check and
resolution fell through to LookPath("java"). On macOS /usr/bin/java is a
launcher stub, not a symlink: the derived home was /usr, and isJDK
passed because the stub forwards -version to the default JDK. mxbuild
then ran with --java-home=/usr and Gradle spun indefinitely during
`mxcli new` (ChipCoV8 #3).
java_home is now asked for the required major, and on darwin a home of
/usr is rejected from every source. The resolver takes an injectable
environment so the macOS path is tested from Linux; the not-found error
names the major, the java_home query and the Temurin install command.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012PkPkaYM12u8yqMKNmzBgT
…script The SessionStart hook runs .claude/bootstrap-mxcli.sh under '|| true' so it never blocks a session, and the script ended in 'exec ./mxcli run --local --setup --ensure-db' — so a failure such as "no local PostgreSQL superuser available" passed unnoticed. Run it without exec, tee the output, and on failure print an ERROR banner to stderr naming the reason, the retry command and --db-type hsqldb, then exit with the failure's status. The hook keeps '|| true'. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012PkPkaYM12u8yqMKNmzBgT
…-index docs(skills): regenerate the create-page section index
The `mxcli syntax` reference showed spellings `check` reports as MDL-DEPRnnn, so an agent copying it got warnings (and refusals under `mdl 1;` for `Date`): - NOT NULL / UNIQUE [ERROR 'msg'] -> ERROR MESSAGE 'msg' (MDL-DEPR021), and say that an AutoNumber needs a DEFAULT seed (MDL023 / CE7247) - named data grid and layout grid columns/rows -> COLUMN ( … ) (MDL-DEPR005) - CREATE [OR REPLACE] for microflow, nanoflow, layout, navigation, and the create-modifiers topic -> OR MODIFY (MDL-DEPR001); OR REPLACE TRANSLATIONS keeps its own meaning and stays - "MODEL accepts these keys" -> RUNTIME (MDL-DEPR555) - MOVE doctype list: TASK QUEUE, CONSUMED/PUBLISHED REST/ODATA SERVICE, AI MODEL (MDL-DEPR550..553, 131) - Date dropped from the attribute type list (MDL-DEPR160) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012PkPkaYM12u8yqMKNmzBgT
TestExamplesParse now runs every Example block through executor.ValidateProgram with the deprecation policy at error, in every context the block parses in, and adds data grid and data view hosts so container-scoped rules (a named grid column, MDL-DEPR005) fire on a fragment documented on its own. TestSyntaxShowsNoDeprecatedSpelling covers the metasyntax field. Its patterns come from the registry: each entry's Example/CanonicalExample diff, widened until it holds a keyword, does not end on a name, and matches nothing in the known-clean corpus (canonical examples and the validated syntax Examples). `--` comments are not matched. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012PkPkaYM12u8yqMKNmzBgT
Documents the microflow statements that start and manage workflows — call workflow ($Wf = call workflow M.WF ($Obj);), get workflow data, get workflows for, get workflow activity records, workflow operation, open workflow, open user task, lock/unlock workflow — with examples that pass `check --deprecations=error`. Cross-linked from microflow.call and workflow.call-workflow. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012PkPkaYM12u8yqMKNmzBgT
…chdog SettleGeneratedSources (the final step of `mxcli new`) and docker build's mxbuildCmd called cmd.Run() with no deadline, so a build that stopped making progress — Gradle spinning behind a bad --java-home on macOS (ChipCoV8 #6) — hung forever. runWithWatchdog starts mxbuild in its own process group, kills the whole tree when the build exceeds MXCLI_MXBUILD_TIMEOUT (default 15m) or prints nothing for MXCLI_MXBUILD_STALL_TIMEOUT (default 5m), and returns an error with the last output lines, the java home used and the variable that raises the limit. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012PkPkaYM12u8yqMKNmzBgT
A false kill of a slow but healthy first build costs more than a late kill of a hung one, and Gradle steps can print nothing for minutes. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012PkPkaYM12u8yqMKNmzBgT
A column with no attribute, no content widgets and no ShowContentAs is still an attribute column (showContentAs defaults to attribute), but it was classified by whether an attribute was bound and fell to the default kind, so its tooltip was written null where mx update-widgets writes an empty Forms$ClientTemplate — CE0463. Classify by the effective showContentAs (authored, else the widget default). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012PkPkaYM12u8yqMKNmzBgT
…te path
`column (Attribute: FieldService.ServiceRequest_LithoSystem/SerialNumber)`
failed check with MDL-WIDGET34 + MDL-WIDGET42 while the bare
`ServiceRequest_LithoSystem/SerialNumber` passed. attributePathV3 took only
plain identifiers between slashes, so the qualified form fell through to the
generic `keyword: expression` alternative and parsed as a division. Each hop
before the final segment may now be `Module.Assoc`; the final segment stays
bare. The builder already resolved qualified hops; describe still prints the
short form.
The MDL-WIDGET34 refusal for a value that is not an attribute path now
quotes the source text (not `&{...}`), names an unnamed widget by its
caption, suggests `Assoc/Attr or Module.Assoc/Attr` for a value with a `/`,
and stops advising a data view when the widget already sits in one.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012PkPkaYM12u8yqMKNmzBgT
The System module's access rules are not in the .mpr, so nothing in mxcli could say whether a role may read a System entity. Measure them from the System domain model mxbuild writes into deployment/model/model.mdp (Mendix 11.15.0, Production security), translating each rule's stored user roles to System.User / System.Administrator. Notable: System.User is [id = '[%CurrentUser%]'] for every role, Administrator included; an Administrator's user list comes from a specialization such as Administration.Account. System.FileDocument and System.Image have no rule at all. TestSystemReadAccessMatchesMDP re-checks the table against a build given with -mdp, and fails when a row is changed (control run on System.Language). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012PkPkaYM12u8yqMKNmzBgT
A combo box over System.User filtered by role rendered empty for a non-admin
role, and check, mxbuild and the runtime log were all clean (ChipCoV9).
SEC010 takes every list-like data widget on a page (list view, data grid,
template grid, reference selectors, pluggable data grid / gallery / combo
box) and asks, per user role that may open the page, whether any of its
module roles has a read rule on the entity, a generalization or a
specialization. No rule -> warning ("renders empty for them"); only
XPath-constrained rules -> info; System.User row-scoped -> warning that it
lists only the current user. System entities use the measured
meta.SystemReadAccess table. Only at Production: Prototype is
CheckFormsAndMicroflows, which does not enforce entity access.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012PkPkaYM12u8yqMKNmzBgT
`check -p` and exec's preflight skipped any `create or modify` of a stored flow whose declared body did not build against the stored project. A body calling a flow an earlier statement of the same script creates is exactly that case, so a change exec refuses (MDL-V1-REBUILD, "$X is still used by") checked clean, and exec wrote the earlier statements before refusing it. The verdict build now resolves calls to flows the script itself declares (an overlay of the script's create [or modify] microflow|nanoflow signatures, generalising the self-call resolution), so check and the preflight refuse it before anything is written. A flow change that is still not predictable is reported with what it depends on instead of the unconditional "no flow change exec would refuse". Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012PkPkaYM12u8yqMKNmzBgT
…ports feat(test): --app-port, --admin-port and --serve-port for test --local
…bootstrap fix(init): app-named docs, release-pinned bootstrap, visible DB-provisioning failure
…e-0emnau-test-errors # Conflicts: # .claude/skills/mendix/test-microflows/SKILL.md
…x-help fix(syntax): stop teaching deprecated spellings, and test it
…ualified-path fix(pages): accept Module.Assoc/Attr as a widget attribute path
…verdict fix(check): predict flow rewrites that call a flow the same script creates
…unreadable feat(lint): SEC010 — a page lists an entity a role that opens it cannot read
…-java fix(docker): macOS JDK for the project's major, never /usr; bound one-shot mxbuild runs
…eader fix(pages): Data grid 2 columns with no caption write the empty templates update-widgets writes
…errors fix(test): carry the raised error message into the FAIL verdict
feat: domain model diagrams as SVG, shown in the companion
… it grants A list over System.User for a non-admin role gets every row through Administration.Account's rule, which the real 11.15 Administration module grants Administration.User unconstrained — but that rule grants only Account's own members (FullName, Email), so System.User.Name comes out blank on every row but the user's own. SEC010 counted any entity-level read on a specialization as reading the generalization and stayed silent on the case it was written for (ChipCoV9 FINDINGS, re-checked on main). A specialization's rule now raises the level only when its MEMBER_READ rows include a member declared by the listed entity or one of its generalizations; a rule with no member rows is taken at its entity-level word as before. System.User's own rule also opens the users whose roles a user role may manage, so Manage All Roles reads every name. Measured on a scratch copy of ChipCoV9 (11.15, run --local, playwright): ServiceManager ["", "", "demo_manager", ""]; with ManageableRoles (FieldEngineer) demo_engineer's name appears too; Administrator sees all. Lint on that copy now flags ServiceManager only. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012PkPkaYM12u8yqMKNmzBgT
…0-members fix(lint): SEC010 counts a specialization's rule only for the members it grants
AI Code ReviewReview SummaryThis PR synchronizes 25 commits from
Critical IssuesNone found. Moderate IssuesNone found. Minor Issues
What Looks Good
RecommendationApprove. The PR introduces valuable features with proper testing and follows project conventions. The minor issues noted are trivial and don't block merging. The synchronization approach is acceptable given the fork's maintenance workflow. Note: The VS Code extension companion feature mentioned in the PR body (hover tooltips for SVG) appears to be in the Automated review via OpenRouter (Nemotron Super 120B) — workflow source |
Syncs
ako/mxcli:mainintomendixlabs/mxcli:main: 25 commits since v0.26.0, landed in the fork through PRs ako#1115–ako#1126.Domain model diagrams
describe --format svg domainmodel <Module>writes a finished SVG: entities at their stored positions, coloured by kind, with attributes, associations with their multiplicities, and generalizations. No layout engine runs, so a client without a browser can show it as is. Boxes are sized for a proportional font, and the stored layout is pulled apart just enough to fit full names (capped at 2.5×) instead of clipping them.domainmodelis also available as--format elk.Lint: data the user cannot read
System.User, which rendered blank names for non-admins while check, mxbuild and the runtime log were clean.model.mdpmxbuild writes (Mendix 11.15.0, Production security), since they are not in the.mpr.Check
Module.Assoc/Attr) parses, instead of falling through to a division.Pages
mx update-widgets(CE0463 before).Testing
mxcli test --localtakes--app-port/--admin-port/--serve-port, probes each before touching the project, and names the holding process when a port is taken.Robustness: init, bootstrap, mxbuild
mxcli new,docker build) have a timeout and a stall watchdog (default 30 m / 10 m stall, configurable) that kills the whole process tree and reports the last output.java_home, and/usris never accepted as a Java home. Before, an 11.14+ project got the launcher stub and Gradle spun indefinitely duringmxcli new.mxcli inittitles the generated docs after the.mpr, not the folder. The bootstrap script pinsMXCLI_TAGto the generating release, and a failed database provisioning is reported loudly instead of passing silently.Syntax reference
mxcli syntaxno longer teaches spellings check flags as deprecated. A test holds every Example and Syntax block to the deprecation registry at error level.microflow.workflow-actions.