Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
9bbf194
fix(widget): resolve concatenated hide lists in editorConfig
claude Sep 9, 2026
d614807
fix(check): report a container-typed widget property written as a value
claude Sep 13, 2026
4862fe5
Merge remote-tracking branch 'origin/main' into claude/banking-app-fe…
claude Sep 13, 2026
f0488ce
test(widget): derive fixture widget definitions instead of reading gi…
claude Sep 13, 2026
1a18bfe
Merge pull request #459 from ako/claude/banking-app-feedback-do04dy
ako Sep 13, 2026
8368b2f
docs: settle four open questions on the microflow-description proposal
claude Sep 13, 2026
3195a45
fix(describe): warn when a merge the description omits will be deleted
claude Sep 13, 2026
575c732
feat(describe): Mode 3 — DESCRIBE MICROFLOW ... NORMALIZED
claude Sep 13, 2026
7e7bfd7
fix(catalog): tell an un-run graph pass apart from a graph with nothi…
claude Sep 13, 2026
2d35684
feat(catalog): answer the module-cycle question, and make the edge sc…
claude Sep 13, 2026
bb486fd
Merge pull request #460 from ako/claude/mxbuild-diagnostics-spike-emta6h
ako Sep 14, 2026
8667e2a
Merge branch 'mendixlabs:main' into main
ako Sep 14, 2026
d3e7273
Merge remote-tracking branch 'origin/main' into claude/mxcli-unit-tes…
claude Sep 14, 2026
41f5b70
Merge pull request #461 from ako/claude/mxcli-unit-test-perf-n7ggx8
ako Sep 14, 2026
2f330b5
fix(describe): tell a button's three icon elements apart (#1059)
ako Sep 13, 2026
2da92c2
feat(pages): author all three of a widget's icon elements (#1059)
ako Sep 13, 2026
692c342
Merge pull request #462 from ako/claude/mxcli-findings-nnl181
ako Sep 14, 2026
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
8 changes: 8 additions & 0 deletions .claude/skills/fix-issue/findings/mdl-executor.jsonl

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions .claude/skills/fix-issue/findings/mdl-other.jsonl
Original file line number Diff line number Diff line change
Expand Up @@ -61,3 +61,4 @@
{"area": "mdl/linter", "date": "2026-09-07", "symptom": "`mxcli lint` CONV010 reported \"ACT_ microflow 'X' contains 'ExclusiveMerge' activity\" on every ACT_ microflow containing an `if` — 122 times on one project, and on a minimal microflow whose ONLY violation was the merge (ako/CapTrackV4 R11).", "cause": "ALLOWED_ACTIVITY_TYPES in conv010_act_microflow_content.star listed ExclusiveSplit and not ExclusiveMerge. An `if` emits both, so the rule permitted the branch and flagged the join it necessarily creates. Fixed by adding ExclusiveMerge, with a test that asks the catalog's own labeller (getMicroflowObjectType) for the two names rather than hardcoding them.", "file": "`.claude/lint-rules/conv010_act_microflow_content.star`; tests `mdl/catalog/lint_rule_vocabulary_test.go`", "insight": "The codebase had already answered this in the other direction and the rule disagreed with it: countMicroflowActivities in mdl/catalog excludes ExclusiveMerge as structural, with a comment saying so. When a lint rule's vocabulary looks wrong, check whether another part of the same package has already classified the same thing. The control matters as much as the fix — LoopedActivity and InheritanceSplit must stay flagged, or a 'widen the list' fix passes the new test and guts the rule. Note also that `mxcli lint` loads Starlark rules ONLY from the project's own `.claude/lint-rules/`, never the embed, so a fixed built-in reaches an existing project only when it re-runs `mxcli init` — which is why a project can keep reporting a rule bug that is already fixed upstream."}
{"area": "mdl/catalog", "date": "2026-09-07", "symptom": "A microflow wired as the project's AfterStartupMicroflow reported \"no callers found\" and \"no references found\", and `mxcli lint` QUAL004 said \"is not called from anywhere. Remove if unused.\" Dropping it left a dangling name that `mx check` also missed; only the runtime refused to start (ako/CapTrackV4 049, R13).", "cause": "The runtime calls these, so nothing in the model does, and CATALOG.REFS had no edge kind for a project setting. Added RefKindSettings and extractProjectSettingsRefs, emitting one `settings` edge per setting that names a microflow (AfterStartupMicroflow, BeforeShutdownMicroflow, HealthCheckMicroflow), with the SETTING as the edge's source; added \"settings\" to QUAL004's MICROFLOW_ENTRY_KINDS.", "file": "`mdl/catalog/builder_references.go` (RefKindSettings, extractProjectSettingsRefs, projectSettingsMicroflowRefs); `.claude/lint-rules/orphaned_elements.star`; tests `mdl/catalog/lint_rule_vocabulary_test.go`", "insight": "Same class as the scheduled-event edge, and the rule's own comment had already named the failure mode — \"a kind missing here turns a live document into a false 'not called from anywhere' finding\" — which makes the list of entry kinds worth auditing whenever a new way to invoke a microflow is added. The dangerous half is not the missing reference but the lint rule built on it: QUAL004 does not merely fail to notice, it actively advises deleting the microflow whose deletion breaks the build. The settings list is a literal rather than reflection over ModelSettings, because most of that struct is strings that are not microflow names and a wrong entry would invent an edge rather than miss one."}
{"area": "mdl/catalog", "date": "2026-09-09", "symptom": "A new CATALOG view is queryable by name but absent from `show catalog tables`, so nobody can discover it — caught by TestTables_CoversAllViews, not by any query", "cause": "Adding a view to tables.go creates it in SQLite, but Catalog.Tables() is a separate hand-maintained list and is what SHOW CATALOG TABLES prints. The two are not derived from each other", "file": "`mdl/catalog/catalog.go` (Tables), `mdl/catalog/tables.go`", "insight": "Two hand-maintained lists of the same thing, with a guard test comparing them — the guard is the only reason this is a five-second fix instead of a view nobody finds for months. When adding a catalog view, expect to touch both. The general shape recurs in this codebase (stmtCreateInfo vs projectNameSets.setFor under-reported conflicts for the same reason), so the question to ask of any list is what compares it to its twin", "refs": ["ako/mxcli#413"]}
{"area":"mdl/catalog","date":"2026-09-13","symptom":"A user asks 'are there circular dependencies between my modules?', reads CATALOG.GRAPH_CYCLES, gets 0 rows, and concludes there are none — while CATALOG.GRAPH_MODULE_COUPLING lists the same module pair in both directions (mendixlabs/mxcli#1060)","cause":"GRAPH_CYCLES is SCCs of the ASSET graph. Modules A and B reference each other through DIFFERENT documents, which form no cycle among themselves, so the asset table is correctly empty for a genuinely circular module pair. Compounded by scope: the asset graph admits only graphRefKinds, while coupling counts every kind — 110 of 316 edges on a stock 11.14 app, and Administration -> Atlas_Core there is `layout`-only","file":"`mdl/catalog/builder_graph.go` (`buildModuleCycles`, `loadModuleEdges`, `graphRefKindsSQL`), `mdl/catalog/tables.go` (`graph_module_cycles_data`, `graph_analysis_scope`), `mdl/linter/starlark_graph.go` (`module_cycles()`)","insight":"**When a report says two tables disagree, check whether they answer different questions before checking whether one is broken — then make the difference queryable.** Tarjan was fine; the granularity and the edge filter were both undocumented and invisible from SQL. Two traps in the implementation: (1) the new module pass sat after `if len(edges) == 0 { return }` on the STRUCTURAL edge set, so a project whose only cross-module refs are navigational — the reported case exactly — got nothing; a test with only `layout` edges caught it. (2) A module-cycle table built on the structural subset would have shipped green and still answered 'none' for the reported pair, so the edge set has to match graph_module_coupling, the table it is read beside. Generate the scope view's IN list from graphRefKinds rather than restating it, or the documentation of the filter drifts from the filter","fix":"Add graph_module_cycles (module SCCs, all kinds, with a RefKinds column naming the edges inside the cycle) and graph_analysis_scope (per-kind edge counts + InAssetGraph), plus a module_cycles() Starlark builtin"}
35 changes: 35 additions & 0 deletions .claude/skills/mendix/create-page/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -380,6 +380,41 @@ image imgRemote (ImageType: imageUrl, ImageUrl: 'https://example.com/logo.svg')
image imgIcon (ImageType: icon)
```

### A button's icon is one of three elements

Mendix stores **three different icon elements**, and the keyword picks which:

```sql
actionbutton btnEdit (Caption: 'Edit', Action: nothing, Icon: 'Atlas_Core.Atlas_Filled.pencil')
actionbutton btnLogo (Caption: 'Logo', Action: nothing, Icon: image MyFirstModule.Images.logo)
actionbutton btnHome (Caption: 'Home', Action: nothing, Icon: glyph 57377)
```

| form | element | holds |
|------|---------|-------|
| bare name | `Forms$IconCollectionIcon` | a name in an **icon** collection |
| `image <name>` | `Forms$ImageIcon` | a name in an **image** collection |
| `glyph <code>` | `Forms$GlyphIcon` | a font character code, no name |

The first two are spelled identically and point into **different documents**, so
the `image` keyword is the only thing separating them. Write an image reference
without it and mxcli stores a custom-icon reference, which fails the build with
*CE1613 "The selected custom icon … no longer exists."* `mxcli check -p app.mpr
--references` resolves each kind against its own collection and names the remedy
when the kind is wrong, which is cheaper than a build.

Any icon collection works, third-party ones included — `show icon collections`
lists them and `describe icon collection Atlas_Core.Atlas_Filled` lists the names
(they are non-obvious: it is `add`, not `plus`).

A glyph has a code and no name. The codes are **sparse**, and an undefined one
fails only at `mxbuild --target=deploy` — with *"An exception occurred while
exporting page '<name>'"*, naming the page and never the icon — so `mxcli check`
reports it as **MDL078** first. Browse them with `show glyphs`.

`describe page` emits all three forms, so describe → exec round-trips a button's
icon whichever kind it is.

### Binding across modules and to audit members

An attribute path may cross module boundaries, including into the platform's
Expand Down
23 changes: 23 additions & 0 deletions .claude/skills/mendix/custom-widgets/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,29 @@ vanished from storage, while the multi-key shape died as `missing ')' at ','`.
The error now names the container keyword and rewrites your entry into the form
that works.

The same rule covers the two spellings that carry no entry to key on
(`mendixlabs/mxcli#1056`):

```sql
selectionhelper sh (renderStyle: 'custom', customAllSelected: []) -- MDL-WIDGET27
selectionhelper sh (renderStyle: 'custom', customAllSelected: 'something') -- MDL-WIDGET27
```

A **widgets**-typed property such as `customAllSelected` holds child widgets, so
it is written as a block with widgets in it rather than entries:

```sql
selectionhelper sh (renderStyle: 'custom') {
customallselected s1 { dynamictext d1 (Content: 'All') }
}
```

The empty form is reported from its shape, with no project needed. The scalar
form is reported only when the widget resolves, because without a definition
`p: 'x'` is the ordinary property form and flagging it would be a guess. Both
matter because a required slot left empty is not a silent no-op at build time —
it is `CE0642 "Property '…' is required."`, one per slot.

`describe widget <name> -p <project.mpr>` lists a widget's container keywords
under **Body containers**, and — for an object list — the widgets-typed **slots
inside one item**, with the widget types that route into each:
Expand Down
44 changes: 43 additions & 1 deletion .claude/skills/mendix/graph-analysis/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,15 @@ Two separate commands are needed. The full catalog must exist before community d
| `REFRESH CATALOG FULL` | `GRAPH_GOD_NODES`, `GRAPH_MODULE_COUPLING`, `GRAPH_MODULE_COHESION`, `GRAPH_DEAD_ASSETS`, `GRAPH_ENTITY_HOTSPOTS`, `GRAPH_MODULE_DEPENDENCIES`, `GRAPH_REFKIND_DISTRIBUTION` |
| `REFRESH CATALOG COMMUNITIES` | `COMMUNITIES`, `COMMUNITY_SUMMARY`, `GRAPH_CYCLES`, `GRAPH_LAYERS`, `GRAPH_CENTRALITY`, `GRAPH_INTEGRATION_SURFACE` |

**An empty table from the second row is ambiguous unless you check.** Step 2 is
not a build mode — it augments whatever the catalog is, so `Build mode: full`
says nothing about whether it ran, and before mendixlabs/mxcli#1060 an un-run
pass and a genuinely cycle-free project produced the same `0 rows`. A query now
warns `requires refresh catalog communities (not run for this catalog)` when the
pass is missing, and `SHOW CATALOG STATUS` has a `Graph analysis:` line. **No
warning plus no rows is a real answer**; treat anything else as "not computed
yet", not as "clean".

`REFRESH CATALOG COMMUNITIES` with a resolution modifier:
- `REFRESH CATALOG COMMUNITIES` — default resolution (balanced granularity)
- `REFRESH CATALOG COMMUNITIES resolution 0.6` — coarser clusters (fewer, larger communities; good for monolith-to-multi-app planning)
Expand Down Expand Up @@ -233,6 +242,37 @@ ORDER BY CycleSize DESC, CycleId
**Size 2**: mutual reference between two elements — e.g. workflow A calls workflow B which triggers workflow A. Investigate before assuming it's intentional; these can cause infinite loops at runtime.
**Size 3+**: a dependency ring — must be broken before the module can be split or properly layered.

### Module cycles are a different query, not a rollup

`GRAPH_CYCLES` is **document**-level. Two modules referencing each other almost
never means two documents do — Administration calls something in Atlas_Core from
one page while Atlas_Core reaches back from another — so `GRAPH_CYCLES` is
correctly empty for a genuinely circular pair of modules. Reading it as "no
circular dependencies" was mendixlabs/mxcli#1060. Ask the module question of the
module table:

```sql
SELECT ModuleName, CycleSize, RefKinds
FROM CATALOG.GRAPH_MODULE_CYCLES
ORDER BY CycleSize DESC, ModuleName
```

`RefKinds` names the kinds on that module's edges **into the rest of the cycle** —
the references to go and break, as opposed to everything else the module points at.

The two also read different edges. `GRAPH_MODULE_CYCLES` uses every reference
kind, matching `GRAPH_MODULE_COUPLING`; the computed asset pass
(`GRAPH_CYCLES`/`COMMUNITIES`/`GRAPH_LAYERS`/`GRAPH_CENTRALITY`) is restricted to
the structural kinds so navigational edges do not blur the clustering. On a stock
app that is about a third of the edges. `GRAPH_ANALYSIS_SCOPE` reports the split
per kind, which is the query to run when coupling shows an edge and the computed
tables act as though it were not there:

```sql
SELECT RefKind, Edges, InAssetGraph FROM CATALOG.GRAPH_ANALYSIS_SCOPE
ORDER BY InAssetGraph, Edges DESC
```

---

## Use Case 8: Monolith-to-Multi-App Planning
Expand Down Expand Up @@ -294,7 +334,9 @@ Elements at the same layer can be parallelised safely. A microflow calling somet
| Which modules are tightly coupled? | `GRAPH_MODULE_COUPLING` |
| Is module M self-contained? | `GRAPH_MODULE_COHESION` |
| What is unused / dead? | `GRAPH_DEAD_ASSETS` |
| Are there circular dependencies? | `GRAPH_CYCLES` |
| Are there circular dependencies between **documents**? | `GRAPH_CYCLES` |
| Are there circular dependencies between **modules**? | `GRAPH_MODULE_CYCLES` |
| Why isn't an edge I see in coupling affecting the cycles/layers? | `GRAPH_ANALYSIS_SCOPE` |
| Which entities are hotspots? | `GRAPH_ENTITY_HOTSPOTS` |
| What contracts would a split require? | `GRAPH_INTEGRATION_SURFACE` |
| What reference types cross module boundaries? | `GRAPH_MODULE_DEPENDENCIES` |
Expand Down
Loading
Loading