Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .claude/skills/fix-issue.md
Original file line number Diff line number Diff line change
Expand Up @@ -472,3 +472,4 @@ extracting `OffsetExpression`/`LimitExpression`.
| A datagrid column bound to an association — `column c (attribute: Order_Customer)` — reports success from `mxcli exec` and then fails the build with `[error] [CE1613] "The selected attribute 'Mod.Order.Order_Customer' no longer exists." at Columns (1/1) of data grid 2`. Separately, there is no MDL spelling for the drop-down filter's association mode: `mxcli check` says `[MDL-WIDGET01] has no property \`refEntity\`` | Two unrelated defects behind one report. (1) The reference is **not representable**: `CustomWidgets$WidgetValue.AttributeRef` is typed `AttributeRef`, not the polymorphic `MemberRef`, so the association was qualified like an attribute and written as a dangling `AttributeRef`. (2) `dropdownfilter.def.json` mapped only `attrChoice`/`attributes`/`defaultFilter`, so every `baseType: 'ref'` property was unmapped and dropped | `mdl/executor/cmd_pages_builder_input.go` (`rejectAssociationAsAttribute`, `entityInChain`) wired into `mdl/executor/widget_engine.go` (the objectlist `attribute` case and the `Attribute` source); `sdk/widgets/definitions/dropdownfilter.def.json` (association mode); `mdl/executor/cmd_pages_describe_parse.go` + `_pluggable.go` + `_output.go` (round-trip) | **Establish that a shape is unrepresentable before designing a fix for it** — hand-patch the BSON and run `mx check`. A `DomainModels$AssociationRef` in that slot makes the project **UNLOADABLE** (`ArgumentException: Object of type 'AssociationRef' cannot be converted to type 'AttributeRef'`), and the assembly defining the type (`Mendix.Modeler.WebUI.dll`) has no `AssociationRef` member at all — so the only correct outcome is a refusal carrying both working forms. **`<associationTypes>` on an ATTRIBUTE-typed widget property is permission to TRAVERSE a reference, not to bind one** — `attribute: Assoc/Attr` already worked and is what the XML is advertising; the DataGrid column is the only shipped widget where the two are easy to confuse. **A def.json `mode` is the whole feature** for an unauthorable widget mode — the engine already had the `association` operation and the `hasDataSource` condition, so the second half was a data change plus its DESCRIBE reader (without which describe→edit→exec silently reverts the filter to attribute mode). **0 errors from `mx check` does not prove the properties landed** — an unmapped property is silently dropped and the build is just as green; read them back with `mx dump-mpr`. Tests `cmd_pages_builder_assoc_as_attribute_test.go`, `widget_dropdownfilter_assoc_test.go`, example `mdl-examples/bug-tests/830-datagrid-association-filter.mdl`. upstream #830 |
| An association's line anchors — where the connector attaches to the entity boxes in the domain model editor — are absent from `DESCRIBE ASSOCIATION`, and manual adjustments made in Studio Pro do not survive an mxcli round trip | `DomainModels$Association.ParentConnection`/`ChildConnection` (the string `"x;y"`) were **hardcoded** to `"0;50"`/`"100;50"` in BOTH writers and never read by either parser. Because every association write rebuilds the whole element, this was not an omission but active destruction: a documentation-only `alter association … set comment` reset them | `sdk/domainmodel/connection.go` (new: `ParseConnectionPoint`/`FormatConnectionPoint`, `Default*Connection`), `sdk/domainmodel/domainmodel.go` (fields → `*model.Point`), `sdk/mpr/parser_domainmodel.go` + `sdk/mpr/writer_domainmodel.go`, `mdl/backend/modelsdk/domainmodel.go` + `domainmodel_write.go`, `mdl/executor/cmd_associations.go` (`describeConnectionPoints`) | **A feature request that says "X is not exposed" may be hiding "X is destroyed"** — check the write path before scoping the read path. The A/B that settled it: a blank 11.13 app's own `Administration.AccountPasswordData_Account` stores `0;54/100;54`, so a Studio-Pro-authored association is a free fixture for "did mxcli overwrite this?" — no Studio Pro needed. **Learn the value's constraints from the LOADER, not from the shape**: hand-patch and run `mx check` — `"0.5;50"` dies with `StorageLoadException` (integers required) while `"0;500"` and `"-20;50"` load with 0 errors (no range check), so out-of-range values must round-trip untouched. **A zero value is not an absent value** — `{0,0}` is a real anchor (top-left), which forces the field to be a POINTER; a plain `model.Point` cannot distinguish "unset" from "top-left" and would silently rewrite it. **Fix both engines**: they share the semantic model, and a fix in one is invisible to a user on the other. **Emit unauthorable data as a COMMENT** — DESCRIBE output must stay re-executable, and inventing syntax (`@anchor(parent: bottom-left, …)`) would bake in a vocabulary the storage does not have: the pair is CONTINUOUS, not 8 named anchors (observed x values 0 9 11 17 18 47 49 50 65 77 78 84 87 100). **The marketplace is the sample** when you need to know what Studio Pro actually writes: `mxcli marketplace download <id>` gives real Mendix-authored models, and a module .mpk holds either a raw BSON `project.mpr` or an MPR v1 SQLite one — 88 coordinate pairs from three modules turned "looks like percentages" into a measurement (all 0..100; 85 of 88 pin one coordinate to exactly 0 or 100). **Rule a unit out from the model, not the values**: pixels is impossible because `DomainModels$EntityImpl` stores only `Location` and NO size — the box is sized by the editor from the name and attribute list, so a pixel anchor would have nothing to measure against. Not applicable to `CrossAssociation`, which has no connection properties and crashes Studio Pro if given them (#50). Tests `sdk/domainmodel/connection_test.go`, `mdl/backend/modelsdk/association_connection_test.go`, `sdk/mpr/writer_domainmodel_test.go`, example `mdl-examples/bug-tests/872-association-line-anchors.mdl`. upstream #872 |
| An association's line anchors can be preserved but not AUTHORED — a scripted domain model cannot lay out its own connector lines, so `@Position(x, y)` gets you boxes and nothing gets you the lines between them | Feature gap, not a defect. `DomainModels$Association.ParentConnection`/`ChildConnection` had no MDL surface | `mdl/grammar/domains/MDLDomainModel.g4` (`SET ANCHOR`/`anchorPoint` — the ONLY grammar change), `mdl/visitor/visitor_association.go` (`anchorAnnotation`, `annotationParenPoint`, `anchorCoord`), `mdl/ast/ast_association.go` (`FromAnchor`/`ToAnchor` on both create and alter), `mdl/executor/cmd_associations.go` (`applyAnchors`, `describeConnectionPoints`) | **Look for an existing annotation before inventing one** — `@anchor(from:, to:)` already existed for microflow sequence flows, asking the same question (where does the connector attach), and `annotationParamName` already admitted FROM and TO, and `(x, y)` was already `annotationParenValue`: CREATE needed **zero** grammar. The two forms cannot be confused because the microflow one names its inner params (`(from: right, to: left)`) while a coordinate pair is positional. **Let the storage pick the value type**: the measured pair is continuous (x takes 14 distinct values across 88 samples), so named anchors were never an option — see the preservation row above for how that was established. **Silence must mean "preserve", not "default"** — naming one end sets it and omitting one keeps what is stored, which is what stops a `create or modify association` about the delete behaviour from flattening a hand-tuned line; the AST carries POINTERS so "not mentioned" and "mentioned as (0, 0)" stay distinguishable. **Reject what the LOADER rejects, at check time**: a fractional coordinate must error, not be truncated to 0 — Mendix refuses to open such a project, and a silently-wrong value in a file that still loads is the worse failure. **Prove DESCRIBE round-trips by parsing its own output** — asserting on a string literal passes against a formatter emitting something nothing can read. Tests `mdl/visitor/visitor_association_anchor_test.go`, `mdl/executor/cmd_associations_anchor_test.go`, example `mdl-examples/bug-tests/872-association-line-anchors.mdl`. upstream #872 |
| `ALTER PAGE` over `--mcp` fails against Studio Pro **11.13** with `pg_patch_page: … PROP_NOT_PRIMITIVE: Property 'widgets' is not a primitive property`. `CREATE PAGE` is fine; the page itself is left intact | 11.13 gave `pg_read_page` a **`depth` argument defaulting to 4**, replacing anything deeper with the literal string `"..."`. ALTER PAGE is read-modify-**replace-whole-page**, so the truncated read went straight back as the new page body. Measured live: `Administration.Account_Overview` read 32,594 bytes at full depth but **1,052 bytes** at the default, its entire tree reduced to `{"widgets":["...","..."]}`. Every ordinary page truncates — three of three PgTest pages did | `mdl/backend/mcp/page.go` (`pgReadPage`, `pgReadFullDepth`, `hasTruncationSentinel`), `mdl/backend/mcp/client.go` (`SupportsToolArg`) | Request the full depth, and **guard rather than trust it**: refuse a read still carrying the sentinel instead of letting a partial page reach a write (ADR-0005 guard-don't-drop). Two traps. (1) **Do not send `depth` unconditionally** — 11.11/11.12 declare `pg_read_page` `additionalProperties:false` without it, so the whole call fails; gate on a live `tools/list` probe of the tool's input schema, because `serverInfo.version` is frozen at `1.0.0` across 11.11/11.12/11.13 and cannot discriminate releases. (2) **Match the sentinel only as an array element** — a caption or title legitimately reading `"..."` is real content, and a naive substring scan rejects valid pages. The release notes announced none of this, exactly as 11.12 silently removed `pg_write_page` (#697): on any Studio Pro upgrade, re-probe `tools/list` and diff the input schemas, not just the tool names. Tests `mdl/backend/mcp/page_depth_test.go`; controls: stub the depth arg (full-depth test fails) and stub the guard (truncation test fails) |
31 changes: 21 additions & 10 deletions .claude/skills/mendix/live-edit-with-studio-pro.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,21 +40,28 @@ Run a script the same way: `mxcli --mcp http://localhost/mcp --mcp-dial localhos

## What you can change via MCP — check first

**What's authorable over MCP depends on the Studio Pro version**, because the
underlying capability surface grows per release. Before generating MDL for live
editing, ask the connected server what it supports — don't guess:
**What's authorable over MCP depends on the Studio Pro version *and on this
session*.** The capability surface changes per release, but it also depends on
your Studio Pro preferences (some tools are togglable) and on which MCP servers
you have connected to Studio Pro. So the answer is not derivable from a version
number — ask the connected server, every session, before generating MDL:

```bash
mxcli mcp capabilities -p /path/to/app.mpr --mcp http://localhost/mcp --mcp-dial localhost:7782
```

It prints, for *this* server: what's authorable (modules, entities + ALTER,
It prints, for *this session*: what's authorable (modules, entities + ALTER,
associations, enumerations, constants, microflows, pages + ALTER PAGE, workflows,
view entities, documents into folders), what's **not** (e.g. nanoflows, Java
actions, business-event services, security, navigation, MOVE/re-parent, attribute
type change — hard PED limits), and the live tool list. Treat anything reported as
not authorable as off-limits over MCP — do it in Studio Pro or against the on-disk
`.mpr` instead.
navigation, entity access rules, documents into folders), what's **not** (e.g.
nanoflows, Java actions, business-event services, view entities, security roles,
MOVE/re-parent, attribute type change), and the live tool list. Treat anything
reported as not authorable as off-limits over MCP — do it in Studio Pro or against
the on-disk `.mpr` instead.

A feature can also be reported unavailable because **this session** lacks a tool it
needs, or because the tool probe did not answer; the report says which, and mxcli
fails closed rather than assuming a tool is there. Quote the whole report in a bug
report — a Studio Pro version number alone does not identify the surface you had.

New modules and their dependents resolve within the same run, so
`create module X; create enumeration X.Status (...)` works in one script. Place a
Expand All @@ -65,7 +72,11 @@ MOVE can't re-parent over MCP.

The machine may run two MCP servers:

- **Studio Pro built-in (port 7782)** — model authoring. **Use this by default.**
- **Studio Pro built-in (port 7782 by default)** — model authoring. **Use this by
default.** From **11.13** Studio Pro **auto-selects a free port** when 7782 is
taken, so multiple instances can run side by side. The active port is shown in
Studio Pro's **status bar** (and set under Preferences > AI > MCP Server) — read
it there rather than assuming 7782, and pass it to `--mcp-dial`.
- **Concord (port 7783)** — a temporary gap-filler with operational/refactor tools
(`delete_document`, `save_all`, `run_app`, `check_model`). **Only** reach for
Concord when the built-in server lacks the capability you need.
Expand Down
Loading
Loading