Skip to content

fix(catalog): a widget that names two microflows keeps both references (mendixlabs/mxcli#1513) - #1431

Merged
ako merged 1 commit into
mainfrom
fix/1513-catalog-widget-multiple-refs
Oct 16, 2026
Merged

ako merged 1 commit into
mainfrom
fix/1513-catalog-widget-multiple-refs

Conversation

@ako

@ako ako commented Oct 16, 2026

Copy link
Copy Markdown
Owner

Fixes the mechanism identified for mendixlabs#1513 (#1395): QUAL004 reported "not called from anywhere" for a microflow a page does use.

Cause

scanWidgetOwnRefs (mdl/catalog/builder_pages.go) collected every Microflow, Nanoflow, Entity and Form value in a widget's own BSON, then returned only the alphabetically smallest of each kind. The refs table was filled from that single column, so a widget naming two microflows lost one.

  • Example: a Data grid 2 with datasource: microflow Tmp.Rows plus onClick: call microflow Tmp.Open(...). A ListView or Gallery with a datasource and a click action had the same problem.
  • The code comment was wrong: it said page-level dedup made the choice immaterial, but the other refs were simply discarded.

Fix

  • All names kept: scanWidgetAllRefs returns every name (sorted, deduped). buildReferences inserts the names beyond the single column with WHERE NOT EXISTS, so a page still gets one edge per target.
  • Single columns unchanged: they still hold one name (the smallest) for show widgets and Starlark microflow_ref. The write-lint-rules catalog-tables reference now says so and points to refs_to().
  • Kind label unchanged: datasource microflows stay RefKind action. Relabelling them datasource would drop them from show callers, whose callerRefKinds doesn't include it.

Tests

widget_multi_refs_test.go:

  • A unit test on a Data grid 2 map with two Microflow keys.
  • An end-to-end test through buildPages and buildReferences: each microflow gets exactly one PAGE→MICROFLOW edge.

Revert check: with the emission off, one microflow loses its edge. Without NOT EXISTS, one gets two edges.

Measured by hand on an expr-checker copy, with the bug-test script and the QUAL004 rule:

  • Before: QUAL004 flagged Tmp1513.Rows and Tmp1513.GalleryRows, and show callers found nothing.
  • After refresh catalog full force: neither is flagged, and show callers of Tmp1513.Rows lists the page.

An automated linter test from the catalog package would be an import cycle.

Also: a finding file (same class as #773 and #940) and mdl-examples/bug-tests/1513-widget-datasource-and-click-refs.mdl. make build, make test, make lint and make check-findings pass.

Notes

  • Not reproduced as reported: the reporter's exact case, a datasource only with no onClick, is not explained by this mechanism. Their field case may involve something else.
  • Catalog cache doesn't track the mxcli version. isCacheValid (cmd_catalog.go) checks only the MPR path, its mtime and the build mode. After an upgrade, lint keeps reusing a catalog built by the old binary until the project changes, so this fix won't show until then. That deserves its own issue.
  • QUAL004 doubles the module in its location (Tmp1513.Tmp1513.Rows). Not fixed here.

🤖 Generated with Claude Code

https://claude.ai/code/session_01W3x8Fyvir8kLyPgJs1BDmF


Generated by Claude Code

…of each kind (mendixlabs#1513)

scanWidgetOwnRefs collected every Microflow/Nanoflow/Entity/Form value in
a widget's own BSON but returned only the lexicographically smallest of
each, and the refs table is projected from that single widgets_data
column. A Data grid 2 (or gallery, list view) with `datasource:
microflow Tmp.Rows` and `onClick: call microflow Tmp.Open(…)` therefore
recorded only Tmp.Open: Tmp.Rows had no inbound reference, `show callers`
found none, and QUAL004 said "not called from anywhere. Remove if unused"
about a live datasource. The comment claiming page-level dedup made the
choice immaterial was wrong.

scanWidgetAllRefs now returns every name (sorted, deduped);
rawWidgetInfo.AllRefs carries them, buildPages/buildSnippets collect the
names beyond the single column onto the Builder, and buildReferences
inserts them after the widget projections with WHERE NOT EXISTS so a
page still gets one edge per target. The single columns keep their
choice (the smallest) for `show widgets` and Starlark's microflow_ref;
the lint-rules skill now says the column is one name and to ask
refs_to() instead. Datasource microflows keep RefKind `action`:
`datasource` is not in show callers' callerRefKinds.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01W3x8Fyvir8kLyPgJs1BDmF
@ako
ako merged commit a9fe433 into main Oct 16, 2026
31 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants