Skip to content

Starlark rule API: documented literals drift from what the linter emits — microflow_type has three spellings, two of which never occur #1178

Description

@ako

Summary

#1164 / #1165 fixed two rules that compared entity_type to "PERSISTENT", a value the linter never returns. The instances are fixed; the class is not. The same drift is live right now on microflow_type, where the repository documents three different spellings and two of them never occur.

Found while reviewing #1165, on main at cb7cfa86.

The failure mode is silence

A Starlark rule that compares a field to a value the API never emits does not error. It skips every item, reports nothing, and is still listed by --list-rules. Its output is byte-identical to a clean project, which is why ARCH002 and ARCH003 went unnoticed indefinitely — and why "I ran the rule and it found nothing" is not evidence that a rule works.

Live instance: microflow_type

LintContext.Microflows() selects mf.MicroflowType raw (mdl/linter/context.go:552) — no CASE normalization — so Starlark receives what the catalog stores: "MICROFLOW", "NANOFLOW", "RULE" (context.go:512).

What the repo tells a rule author:

source documented values correct?
.claude/skills/mendix/write-lint-rules/SKILL.md:176 "microflow", "nanoflow" no — neither occurs
.claude/lint-rules/example_microflow.star:17 "Microflow", "Nanoflow" no — neither occurs
.claude/lint-rules/mccabe_complexity.star:21 "MICROFLOW", "NANOFLOW" yes

No shipped rule is currently dead from this — the only rule that actually compares the field (missing_documentation.star:186) uses "MICROFLOW". But the two wrong sources are the two a new rule gets written from, and example_microflow.star exists to be copied. It is synced into cmd/mxcli/lint-rules and embedded (Makefile:74), so it ships into every project mxcli init touches.

Both wrong sources also omit "RULE" entirely. All three flavours share one catalog table, so a rule filtering microflow_type without knowing that silently treats rules as microflows or drops them — the same root cause as the DocumentNoun() comment at context.go:520.

Second instance: the entity field table is incomplete

entityToStarlark exposes 17 keys; the skill's entity table documents 13. Missing: has_created_date, has_changed_date, has_owner, has_changed_by — added in d6c4059 precisely so a rule could ask "which persistent entities have no audit trail", and undocumented since. Not a dead comparison, but the same drift: the API moved and its only documentation did not.

Why it recurs

59db6e7b (Sep 18) corrected "six of its example rows named values the API has never returned" in that same skill file, and never touched the entity_type row — which stayed wrong until #1165 five days later. Two passes, same file, same class, and nothing in CI can tell that a documented literal is unreachable.

There is also an inconsistency worth deciding on directly: Entities() normalizes its enum (CASE e.EntityType WHEN 'PERSISTENT' THEN 'Persistent', context.go:185) and Microflows() does not. Two adjacent iterators in one file with opposite conventions is the trap that produced both bugs.

Suggested fix

Either direction closes it; the test is the part that matters.

  1. Pin the literals in a test. Assert that every value documented in write-lint-rules/SKILL.md and in the example_*.star comment headers is one the corresponding API can actually emit. That is the check neither fix(lint): ARCH002 and ARCH003 compare entity_type to a value the linter never returns #1165 nor 59db6e7b added, and it is what makes a third recurrence impossible rather than unlikely.
  2. Remove the asymmetry, by normalizing MicroflowType in Microflows() the way EntityType is normalized in Entities() — then one convention holds across the API and the docs have one spelling to state. Note this would be a behaviour change for any user rule already comparing "MICROFLOW" correctly, so it needs a CHANGELOG entry and probably a deprecation note; option 1 does not.

Fixing the two wrong sources without (1) leaves the next drift to be found by a user whose rule reports nothing.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions