You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
#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.
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.MicroflowTyperaw (mdl/linter/context.go:552) — no CASE normalization — so Starlark receives what the catalog stores: "MICROFLOW", "NANOFLOW", "RULE" (context.go:512).
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.
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.
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.
Summary
#1164 / #1165 fixed two rules that compared
entity_typeto"PERSISTENT", a value the linter never returns. The instances are fixed; the class is not. The same drift is live right now onmicroflow_type, where the repository documents three different spellings and two of them never occur.Found while reviewing #1165, on
mainatcb7cfa86.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_typeLintContext.Microflows()selectsmf.MicroflowTyperaw (mdl/linter/context.go:552) — noCASEnormalization — so Starlark receives what the catalog stores:"MICROFLOW","NANOFLOW","RULE"(context.go:512).What the repo tells a rule author:
.claude/skills/mendix/write-lint-rules/SKILL.md:176"microflow","nanoflow".claude/lint-rules/example_microflow.star:17"Microflow","Nanoflow".claude/lint-rules/mccabe_complexity.star:21"MICROFLOW","NANOFLOW"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, andexample_microflow.starexists to be copied. It is synced intocmd/mxcli/lint-rulesand embedded (Makefile:74), so it ships into every projectmxcli inittouches.Both wrong sources also omit
"RULE"entirely. All three flavours share one catalog table, so a rule filteringmicroflow_typewithout knowing that silently treats rules as microflows or drops them — the same root cause as theDocumentNoun()comment atcontext.go:520.Second instance: the entity field table is incomplete
entityToStarlarkexposes 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 theentity_typerow — 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) andMicroflows()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.
write-lint-rules/SKILL.mdand in theexample_*.starcomment 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 nor59db6e7badded, and it is what makes a third recurrence impossible rather than unlikely.MicroflowTypeinMicroflows()the wayEntityTypeis normalized inEntities()— 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.