Skip to content

fix(lint): QUAL004 counts every catalog reference as a caller; published-service microflows get edges - #1746

Merged
ako merged 3 commits into
mainfrom
fix/1484-qual004-navigation-callers
Oct 27, 2026
Merged

ako merged 3 commits into
mainfrom
fix/1484-qual004-navigation-callers

Conversation

@ako

@ako ako commented Oct 27, 2026

Copy link
Copy Markdown
Owner

Closes #1484 (mirror of mendixlabs#1579). Closes #1639.

Lint rule QUAL004 told users to remove live entry points ("is not called from anywhere — Remove if unused"). Three commits: the location fix found on the way, then one per issue.

Lint locations repeated the module (at M.M.NAV_Home)

Cause: Location.QualifiedName() (mdl/linter/linter.go) prefixed the module to DocumentName, and ~30 bundled Starlark rules plus missing_translations pass the qualified name there. CONV018's module-level finding printed Sales.Sales.
Fix: in that one function (text, Markdown, SARIF and lint -d all go through it): a name already starting with <module>. is returned unchanged, and a module-level finding prints just the module. Fixing it there also covers stale mxcli init copies and user-written rules. Tests with controls for a document named like its module and a module name that prefixes the document name.

#1484 — a navigation-started microflow reported as unused

Cause: orphaned_elements.star kept its own MICROFLOW_ENTRY_KINDS list, missing home_page and menu_item (the page list had both).
Fix: no kind list at all: a microflow, nanoflow, rule or page counts as used when any edge in the catalog's reference graph points at it, which is what mxcli refs and GRAPH_DEAD_ASSETS already mean. Drift is pinned by tests: catalog.RefKinds lists every kind and TestEveryObjectTypeConstantIsInAPublishedList ties it to the RefKind constants; TestQUAL004CountsEveryRefKind sends every kind at a microflow, a nanoflow and a page through the shipped rule, so a reintroduced allowlist fails naming the kind.

#1639 — published-service microflows have no catalog edge

Half 1 (QUAL004 ignoring references the catalog has) reduced, after the reporter's re-measure, to the menu-item case fixed above.
Half 2, cause: buildODataServices emitted no edge for any microflow a service names.
Fix: builder_service_refs.go: one edge per distinct microflow, sourced from the service, with existing kinds so show callers, the analysis graph and QUAL004 need no list changes — published OData (publish: read/insert/update/delete, published actions, authentication), published REST (publish: authentication), business event service (event: operation handler), consumed OData (call: configuration, headers, error handling). CatalogSchemaVersion 28 → 29 so cached catalogs rebuild.

Verified

  • Revert proofs: the [up#1579] QUAL004 flags microflows started from the navigation (home microflow, menu item) as "not called from anywhere" #1484 test fails with "reports M.NAV_Home / M.NAV_Orders"; the location tests fail with at Sales.Sales.Big_MF; the edge tests get [] and GRAPH_DEAD_ASSETS lists the service microflows again. Controls: an unreferenced microflow and page are still reported; ACT_ stays exempt.
  • Real 11.13 app: NAV_Home (home microflow) and NAV_Orders (menu item) drop out of QUAL004; the controls stay. refs for OData read/authentication/action microflows, a REST authentication microflow and a business event handler go from "no references found" to one row each. On TestApp (Studio Pro-authored), the 3 published OData actions get publish edges.
  • Full go test ./..., go vet, gofmt and CI's doc checks pass with all five of this batch's branches merged onto main. Bug-tests qual004-1484-navigation-microflows.mdl, catalog-1639-service-microflow-refs.mdl; findings recorded; starlark-rules and catalog-schema docs, write-lint-rules skill updated.
  • Not verified: the ReadMode and authentication-microflow read paths on a Studio Pro-authored document (none available; keys match generated/metamodel); consumed OData edges only by unit test (authoring needs a reachable $metadata).

Noticed, not fixed

Edges still missing (different mechanism: no reader or no table): published SOAP operations and their header-authentication microflow; import/export mapping custom-handler microflows; ProjectSecurity.SignInMicroflow; a microflow's ConcurrencyErrorMicroflow; the navigation's not-found page (stored, but no edge, so a page used only as the 404 page can still be reported); document-template data sources and app services. Also: QUAL004's page message ("is not shown from any microflow") is out of date now that navigation counts.

🤖 Generated with Claude Code

ako and others added 3 commits October 27, 2026 14:54
Every QUAL004 finding printed its location as `at M.M.NAV_Home`, and so
did CONV001-010, the SEC rules and most other bundled Starlark rules:
they pass `document_name=<x>.qualified_name`, while
Location.QualifiedName() prefixes the module to DocumentName, which it
takes to be the short name. The Go rule missing_translations does the
same, and CONV018's module-level finding printed `at Sales.Sales`.

QualifiedName() is the one place a location becomes a name (text,
Markdown, SARIF, `lint -d`), so it now accepts both spellings: a
DocumentName that already starts with "<module>." is returned as is (a
Mendix document name cannot contain a dot, so the prefix is always the
module), and a module-level finding naming the module itself prints the
module once. Fixing the ~30 rule files instead would leave every
project's stale `mxcli init` copies, and every user-written rule, still
doubling.

Verified: TestLocationQualifiedNameDoesNotRepeatTheModule (with controls
for a document named like its module and a module name that prefixes
the document's) and TestShippedRuleLocationPrintsTheModuleOnce (the
shipped QUAL004 rule through the text formatter) both fail with
`at Sales.Sales.Big_MF` when the change to linter.go is reverted.
Found while reproducing #1484.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…m anywhere (#1484)

A microflow started only from the navigation — a profile's home
microflow, or a menu item's `call microflow` — was reported as
"is not called from anywhere. Remove if unused", while `mxcli refs`
listed its `home_page` / `menu_item` edge (upstream
mendixlabs#1579; the 4 NAV_ findings left in #1639).

Cause: orphaned_elements.star decided "has a caller" from its own copy
of the reference vocabulary, MICROFLOW_ENTRY_KINDS. PAGE_ENTRY_KINDS had
home_page and menu_item; the microflow list did not. It is the fourth
time that list fell behind the catalog (datasource/action/calculate,
schedule, settings/publish, event), so adding the two kinds would only
reset the clock.

Fix: the rule keeps no kind list. A microflow or page is used when any
edge in the reference graph points at it — what `refs` and
GRAPH_DEAD_ASSETS already read; no kind that targets a microflow or a
page means anything but "this runs / opens it", so no exclusion list is
needed either. Drift is guarded behaviourally: catalog.RefKinds is held
complete against the RefKind constants, and TestQUAL004CountsEveryRefKind
drives every one of them at a microflow, a nanoflow and a page through
the shipped rule (an unreferenced microflow is the per-kind control), so
a kind added later is tested the day it is declared and a reintroduced
allowlist fails by name. The two catalog tests that pinned the old lists
are replaced by it; `schedule` edges are among the kinds driven.

Verification:
- TestQUAL004CountsNavigationAsACaller (controls: an unreferenced
  microflow and page are still reported, an ACT_ microflow is still
  exempt) fails with "QUAL004 reports M.NAV_Home / M.NAV_Orders" when
  the rule change is reverted; TestQUAL004CountsEveryRefKind fails for
  every kind, and the RefKinds guard fails when a constant is left out.
- Real 11.13 app (blank app + navigation from the repro), BASELINE
  binary with only the rule swapped: NAV_Home and NAV_Orders drop out of
  QUAL004; the unreferenced control microflow M.Unused and page
  M.ItemEdit are still reported; ACT_Entry still exempt.

Repro: mdl-examples/bug-tests/qual004-1484-navigation-microflows.mdl.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ntication microflows dead (#1639)

`mxcli refs Mod.Read_Drivers` said "no references found" for a
microflow that is a published OData resource's `ReadMode: microflow`,
and likewise for the service's `Authentication: (microflow X)`; `impact`
agreed and QUAL004 advised "Remove if unused" — 35 of the reporter's
backend's 43 QUAL004 findings.

Half 1 of the issue (QUAL004 ignoring references the catalog already
has) was, after the reporter's re-measurement, the navigation menu-item
case alone; it is fixed by the #1484 commit, which makes the
rule count every edge. This commit is half 2: the edges themselves.

Cause: buildODataServices catalogued the service and emitted no edge for
any microflow it names — entity-set read/change modes, published OData
actions, the authentication microflow. Auditing the sibling services
(every `*Microflow*QualifiedName()` in modelsdk/gen against the
builders) found the same gap where the reader already has the name: a
published REST service's authentication microflow, a business event
service operation's handler (stored in business_events_data, dropped),
and a consumed OData service's configuration / headers / error-handling
microflows.

Fix: the service builders collect one edge per distinct microflow
(builder_service_refs.go) and buildReferences emits them, from the
service (new source types PUBLISHED_ODATA_SERVICE,
PUBLISHED_REST_SERVICE, BUSINESS_EVENT_SERVICE, CONSUMED_ODATA_SERVICE,
equal to their objects ObjectType, catalog id in SourceId). Kinds are
reused, not minted, so `show callers`, the analysis graph and QUAL004
need no list change: `publish` (a published service runs it), `event`
(an event handler — entity or business event), `call` (the consumed
service calls its configuration microflow, as a workflow calls its
handlers). CatalogSchemaVersion 28 -> 29 so cached catalogs rebuild.

Verification:
- builder_service_refs_test.go pins each edge, the SourceId join, the
  dedup and the no-microflow modes; with the collection hooks reverted
  every edge test gets [] and GRAPH_DEAD_ASSETS lists Read_Rows and
  AuthenticateApiClient again (unreferenced control still listed).
- Real 11.13 app, same model, baseline vs fixed binary: `refs` for the
  read, authentication and action microflows of a published OData
  service, a published REST service's authentication microflow and a
  business event handler goes from "no references found" to one
  publish/event row each; `show callers` names the service; QUAL004 and
  GRAPH_DEAD_ASSETS drop them; the unreferenced control is still
  reported. On ako/TestApp (Studio Pro reference) the three published
  OData actions gain `publish` edges. No Studio Pro document with a read
  or authentication microflow was at hand, so those two read paths are
  verified on mxcli-written documents (keys match generated/metamodel).

Not fixed, same symptom, different mechanism (no reader or no table):
published SOAP web services, import/export mapping custom-handler
microflows, ProjectSecurity.SignInMicroflow, a microflow's
ConcurrencyErrorMicroflow, the navigation not-found page.

Repro: mdl-examples/bug-tests/catalog-1639-service-microflow-refs.mdl.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@ako
ako merged commit cfd9280 into main Oct 27, 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

1 participant