Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
2 changes: 2 additions & 0 deletions .claude/skills/fix-issue/findings/cmd-mxcli.jsonl
Original file line number Diff line number Diff line change
Expand Up @@ -152,3 +152,5 @@
{"date": "2026-10-03", "area": "cmd/mxcli/check", "symptom": "`mxcli check -p` prints MDL067 (bare commit now WITH events) for a create-or-modify flow already stored with events, which `exec -p` no longer prints", "cause": "cmd_check ran ValidateProgram without the DropSettledCommitNotes filter exec_preflight applies; the project was only connected later, for the reference tier", "fix": "check connects to the project before the semantic report when -p is given, applies DropSettledCommitNotes and StoredTaskClaimViolations, and reuses that connection for the reference tier", "insight": "Two gates over one rule set drift whenever a post-filter lives in only one of them; grep for every caller of ValidateProgram when adding a filter. Control: a flow stored without events still notes", "issue": "ako/mxcli#943", "file": "cmd/mxcli/cmd_check.go", "test": "cmd/mxcli/check_stored_semantics_test.go"}
{"date": "2026-10-03", "area": "cmd/mxcli/test", "symptom": "every `mxcli test` run prints 2x MDL-DEPR001 and 2x MDL-V1-SLASH about a script the user never wrote", "cause": "GenerateEndpointMDL emitted a headerless mdl 0 script with `create or replace` and `/` terminators; the test-flow generators had already moved to the version-aware writeScriptHeader/createFlow/writeFlowEnd", "fix": "GenerateEndpointMDL writes mdl 1 through the same helpers (header, create or modify, `;` only); endpoint script is independent of the suite's version", "insight": "A generated script is checked like a user's one; pin it with a test that parses it and asserts ValidateProgram returns nothing. Verified end to end with `mxcli test --local` on a fresh 11.13 app", "issue": "ako/mxcli#943", "file": "cmd/mxcli/testrunner/endpoint.go", "test": "cmd/mxcli/testrunner/endpoint_clean_test.go"}
{"date": "2026-10-03", "area": "cmd/mxcli/theme", "symptom": "`theme create acme --from design.css` with `--mxt-font: \"Inter\", system-ui, sans-serif` prints nothing about Inter; the theme ships no woff2 and no @font-face for it and renders in the fallback font wherever Inter is not installed", "cause": "planFonts only decided which VENDORED families to drop; a seeded family outside the vendored set was never looked at, so the silent outcome was the default", "fix": "unvendoredSeededFamilies takes the primary (first) family of each seeded font stack, skips generic families and var() and the families the base partial loads, and CreateResult.UnvendoredFonts carries them to cmd_theme.go, which prints a note per family naming mxcli-fonts/ and the partial", "insight": "Only the first family of a stack is the design's choice; flagging the fallbacks (Helvetica, Arial) would make the note noise. The controls are a vendored family (IBM Plex Mono) and a generic stack, which must stay silent", "issue": "ako/mxcli#944", "file": "cmd/mxcli/theme/create_seeded.go (unvendoredSeededFamilies, planFonts); cmd/mxcli/cmd_theme.go", "test": "cmd/mxcli/theme/create_seeded_test.go (TestCreate_NamesSeededFontsItDoesNotVendor)"}
{"area": "cmd/mxcli/docker", "date": "2026-10-03", "symptom": "`mxcli docker check` (a check) rewrote an MPRv1 project's .mpr permanently, and on v1 and v2 alike rewrote theme-cache/web/theme.compiled.css(.map) and created deployment/sass/main.scss \u2014 with or without --no-update-widgets", "cause": "update-widgets ran on the user's project, protected only by a snapshot/restore of the v2 storage (.mpr + mprcontents/), so v1 had no protection; and `mx check` itself compiles the theme into theme-cache/ and writes deployment/sass/, which nothing guarded", "file": "`cmd/mxcli/docker/check.go` (`Check`), `cmd/mxcli/docker/check_copy.go` (`copyProjectForCheck`)", "insight": "A snapshot of the files you expect a tool to touch protects only those files; measure with a whole-tree hash+mtime diff before/after, which is what showed that plain `mx check` writes too. The fix is to run both mx steps on a temporary copy (skipping deployment/, releases/, theme-cache/, .git, node_modules) and rewrite the copy's path back in mx output. Control: a CE0117 microflow is still reported on both formats with the tree unchanged. `docker build` keeps runUpdateWidgets because it is expected to write deployment/ \u2014 but it still rewrites a v1 .mpr", "refs": ["ako/mxcli#951", "ako/mxcli#568", "ako/mxcli#646"]}
{"area": "cmd/mxcli", "date": "2026-10-03", "symptom": "A project whose CLAUDE.md, skills and lint rules were written by a newer mxcli is served by an older binary (v0.24.0 on PATH) with no warning: 'mdl 1;' is a parse error and shipped lint rules crash, all reading as project defects", "cause": "Nothing recorded which mxcli wrote the tooling; .claude/bootstrap-mxcli.sh linked whatever mxcli was on PATH; init --sync-skills refreshed only .ai-context/skills, never .claude/lint-rules or CLAUDE.md/AGENTS.md", "fix": "init and every sync write .ai-context/mxcli-tooling.json; root PersistentPreRun warns once on stderr when the binary is provably older (release by number, nightly by tag date, mixed by build date, dev never); sync refuses from an older binary; the bootstrap script carries a POSIX-sh copy of the ordering and neither links an older PATH binary nor keeps an older ./mxcli, downloading via a temp file + mv; sync also refreshes bundled lint rules by name and the CLAUDE.md/AGENTS.md section between mxcli:begin/end markers", "insight": "A binary cannot warn about a stamp it predates, so the guard for already-shipped binaries must live in the generated script, which the newer mxcli regenerates. The sh and Go comparisons share one test table so they cannot drift. curl -o ./mxcli on a symlinked ./mxcli would overwrite the PATH binary — always download to a temp name and rename", "issue": "ako/mxcli#952", "file": "cmd/mxcli/tooling_stamp.go; cmd/mxcli/init_tooling_sync.go; cmd/mxcli/init_hook.go (bootstrapScriptTemplate); cmd/mxcli/main.go; cmd/mxcli/init.go", "test": "cmd/mxcli/tooling_stamp_test.go; cmd/mxcli/init_hook_version_test.go; cmd/mxcli/init_tooling_sync_test.go"}
2 changes: 2 additions & 0 deletions .claude/skills/fix-issue/findings/mdl-executor.jsonl
Original file line number Diff line number Diff line change
Expand Up @@ -844,3 +844,5 @@
{"date": "2026-10-03", "area": "mdl/executor/drop", "symptom": "`drop microflow M.F;` in one `mxcli exec` run and `create microflow M.F …` in the next leaves M.F with no module-role grants (CE0106 on the pages calling it); the drop printed only \"Dropped microflow: M.F\"", "cause": "the grants carry only through the session cache (rememberDroppedMicroflow / consumeDroppedMicroflow), which a later process does not have; nothing told the user the carry was session-scoped. `drop page` never remembers its AllowedRoles at all", "fix": "writeDroppedGrantsNote (mdl/executor/drop_grants_note.go), called from execDropMicroflow / execDropNanoflow / execDropPage, prints the removed roles, whether a create carries them (same script or session for flows; never for a page), and the `grant` that restores them; flowRefusal's rebuild advice says drop + create in the same script", "insight": "A carry that lives in a session cache is invisible at the statement that creates it; the place to say so is the drop, which is the last moment the roles are known. Snippets have no access roles, so they need nothing", "issue": "ako/mxcli#944", "file": "mdl/executor/drop_grants_note.go; cmd_microflows_drop.go; cmd_nanoflows_drop.go; cmd_pages_builder.go; flow_verdict.go", "test": "mdl/executor/drop_grants_note_test.go; flow_verdict_test.go (TestFlowRefusalNamesTheFlowAndTheReason)"}
{"date": "2026-10-03", "area": "mdl/executor/settings", "symptom": "after `alter settings language (DefaultLanguageCode: 'de_DE')`, `docker check` fails with CE4899 \"Empty caption. [German, Germany]\" at Tab page 'tabPage2' (Administration.Account_Overview, en_US only) while `check -p --references`, `lint` and exec are silent; a page created AFTER the switch in the same script fails the same way", "cause": "nothing compared required captions with DefaultLanguageCode (QUAL005 compares languages with each other, and `mxcli lint` does not even run it); and describeDefaultLanguage cached the authoring language once per session, so the switch did not reach later creates", "fix": "translations.MissingRequiredCaptions (measured set: Forms$TabPage.Caption in pages, snippets, layouts; templates and building blocks skipped) feeds lint QUAL006, the note printed by alterSettings (defaultLanguageChanged, which also drops the cached authoring language) and check -p MDL-I18N01 (CheckDefaultLanguageCaptions simulates which documents the script writes before/after the switch)", "insight": "Measure which caption kinds the build requires before flagging: of eleven kinds written en_US-only, only the tab page caption failed; flagging the rest would have made an error rule wrong ten times out of eleven. The first lint run also flagged 22 page-template tab pages mxbuild never reported, caught only by comparing lint's count with docker check's (1 vs 1 after the fix, 3 vs 3 on the e2e script)", "issue": "ako/mxcli#944", "file": "mdl/translations/required.go; mdl/linter/rules/required_captions.go; mdl/executor/default_language_captions.go; mdl/executor/cmd_settings.go (defaultLanguageChanged)", "test": "mdl/translations/required_test.go; mdl/linter/rules/required_captions_test.go; mdl/executor/default_language_captions_test.go"}
{"area": "mdl/executor", "date": "2026-10-03", "symptom": "ako/mxcli#944: describe printed `clear $L;` and `set $L = $M;` for a Change list Clear/Set (Replace) action; `clear` did not parse, and `set` on a list variable executed to a Change variable action that passes mxcli check but mx check refuses (CE7247 \"Variable 'A' does not have a primitive type\")", "cause": "the grammar had only add/remove for Change list, and the builder routed every `set $X = …` to ChangeVariableAction regardless of the target's type", "file": "`mdl/grammar/domains/MDLMicroflow.g4` (clearListStatement), `mdl/executor/cmd_microflows_builder_graph.go` (MfSetStmt → addReplaceListAction when isListVariable)", "insight": "A describe → exec round trip that compares only MDL text passes when the action TYPE changes but prints the same — the Set revert check stayed green until the test also read the stored $Type/Type from the unit. Assert the stored shape, not just the re-described text", "refs": ["ako/mxcli#944"]}
{"area": "mdl/executor", "date": "2026-10-03", "symptom": "ako/mxcli#953 item 1: two calls to a VOID Java/JavaScript action with the same output name (Studio Pro names a JS one after the action, `$RefreshEntity`) failed `check` — MDL063 in a microflow, \"duplicate variable name … already declared in this scope (CE0111)\" in a nanoflow — while mxbuild 11.13.0 builds them clean; describe's header also called the model invalid. Alongside it, the opposite: a real duplicate output in a nanoflow's if/else branches passed check and was CE0111 in mxbuild", "cause": "every named call output counted as a declaration regardless of the action's return type; MDL063 ran for microflows only, and the nanoflow path relied on the check-time body validator (validateFlowBody), which scoped names per branch", "file": "`mdl/executor/validate_void_code_calls.go` (voidCodeActions: script declarations + lazily opened project), `validate_microflow_ce_gaps.go` (checkDuplicateVariableNames skips void calls, walks error handlers), `validate_nanoflow.go` (MDL063 for nanoflows), `cmd_microflows_builder_validate.go` (duplicatesOwnedElsewhere), `cmd_microflows_show.go` (duplicateOutputVariableWarnings)", "insight": "Measure what the name IS before deciding whether to print it: a void call's output name is inert in mxbuild (a later `declare` of the same name is clean, a use is CE0109 Undefined variable), but Studio Pro stores it with UseReturnVariable=true, and the bare `call …` form writes an empty name — so describe keeps `$X =` for the round trip and only the declaration count changes. The Java action reader returns a NIL ReturnType for Void (codeActionReturnTypeFromGen) while the JavaScript reader returns a VoidType; a mock built from the type name passed while the real project did not — run the end-to-end check on a real project. Two validators owning one rule disagreed in both directions; give the rule one owner (MDL063) and switch the other off for it.", "refs": ["ako/mxcli#953"]}
{"area": "mdl/executor", "date": "2026-10-03", "symptom": "ako/mxcli#953 item 7: `check --references` accepted `call java action M.ValidateEmail(email = …)` (the parameter is EmailAddress), which mxbuild rejects with CE1613; `call microflow` / `call nanoflow` with an argument naming no parameter of the callee passed even in isolation", "cause": "the Java/JavaScript parameter check existed, but `check` returned before the reference tier whenever a semantic rule reported an error (the repro also had a true MDL063), and validateWithContext returned a flow's body-validation errors INSTEAD of its reference errors; flow calls had no parameter-name check at all", "file": "`cmd/mxcli/cmd_check.go` (reference tier runs after semantic errors), `mdl/executor/validate.go` (flowCallRef + flowValidationError; validateCodeActionParams treats a known-empty parameter list as none)", "insight": "A check that 'accepts' an input may never have looked at it: before writing a new rule, run the existing one in isolation — here it already fired, and the bug was the early return two tiers up. mxbuild's CE1613 is itself a gate (it hides the other errors in the same run), so measure one fault per mx check.", "refs": ["ako/mxcli#953"]}
2 changes: 2 additions & 0 deletions .claude/skills/fix-issue/findings/mdl-other.jsonl
Original file line number Diff line number Diff line change
Expand Up @@ -90,3 +90,5 @@
{"area": "mdl/catalog", "date": "2026-10-03", "symptom": "`delete $Order` / `change $Order (…)` inside `loop $Order in $Orders` produce no refs row (no delete/change edge to the entity), while `delete $Orders` on the list does; same for the output of `retrieve $Cust from $Order/Mod.Assoc`. show references / impact under-report batch flows.", "cause": "buildVarEntityMap seeded only object/list parameters and create / database-retrieve outputs, from a flattened action list that had already lost the loop's IterableList; loop iterators and association-retrieve outputs never got an entity, so microflowVarActionRef could not resolve them.", "file": "`mdl/catalog/builder_references.go` (buildVarEntityMap, associationTarget, associationEnds)", "insight": "Walk the object collection, not the flattened actions: the iterator's type lives on the LoopedActivity. Objects is not flow order, so map to a fixpoint (first assignment wins, which also bounds it). An association retrieve's output is the OTHER end from the start variable's entity; when the start is neither end (a specialization) leave it unmapped rather than guess. Control in the test: the list delete that always resolved.", "refs": ["mendixlabs/mxcli#1266"]}
{"area": "mdl/linter", "date": "2026-10-03", "symptom": "lint CONV013 reports \"Java action call ... uses '' error handling instead of Custom\" on calls that have `on error { \u2026 }`, and CONV014 never fires on `on error continue` on an action", "cause": "Both rules read BaseActivity.ErrorHandlingType, which the model reader never fills: Mendix stores an action activity's error handling on the ACTION (Microflows$JavaActionCallAction.ErrorHandlingType). The '' in the message was the empty field", "file": "`mdl/linter/rules/conv_error_handling.go`; shared reader `sdk/microflows/error_handling.go` (`ObjectErrorHandlingType`)", "insight": "The unit tests had always set the activity field by hand, so they passed against a shape the reader never produces. Build test objects the way flowObjectFromGen does. Two private reflection helpers (executor DESCRIBE, MCP backend) already read the action correctly; the rules had a third, wrong copy. A '' interpolated into a diagnostic is the cheapest tell of a never-populated field", "refs": ["mendixlabs/mxcli#1202"], "rules": ["CONV013", "CONV014"]}
{"area": "mdl/catalog", "date": "2026-10-03", "symptom": "activities table / activities_for() has no rows for anything inside a loop (nested loops included) in a microflow, nanoflow or rule; a Starlark rule cannot find a retrieve, commit or delete in a loop", "cause": "buildMicroflows had three near-copy loops (microflow, nanoflow, rule) over ObjectCollection.Objects that never recursed into LoopedActivity.ObjectCollection, although the reader fills it; countDecisionPoints beside them did recurse", "file": "`mdl/catalog/builder_microflows.go` (`insertFlowActivities`, `countFlowActivities`)", "insight": "Three copies of one walk is how the gap stayed in all three flavours. One shared walker writes ParentLoopId/LoopDepth; activities_for() keeps its top-level default (filtering ParentLoopId = '') so bundled rules such as CONV010 keep their counts, and ActivityCount keeps its top-level meaning beside a new TotalActivityCount. Raw SQL over activities now sees loop-body rows", "refs": ["mendixlabs/mxcli#1266"]}
{"area": "mdl/catalog", "date": "2026-10-03", "symptom": "Parallel mxcli processes on one project (8x `lint` on a fresh copy) print `Warning: failed to save catalog cache: failed to create table catalog_meta: table catalog_meta already exists` or `database is locked (SQLITE_BUSY)`; a reader can open a half-written .mxcli/catalog.db", "cause": "buildCatalog removed the cache and SaveToFile wrote into the path in place: VACUUM INTO refuses a non-empty target another process had just created, and the manual-copy fallback then CREATE TABLEd into that same file. Opening a cache (NewFromFile) also always wrote (createTables + schema_version row), so concurrent openers contended for the write lock", "file": "`mdl/catalog/catalog.go` (`SaveToFile`, `NewFromFile`), `mdl/catalog/catalogdb_sqlite.go`, `mdl/executor/cmd_catalog.go` (buildCatalog save)", "insight": "Write to a temp file in the same directory and os.Rename it over the cache \u2014 but a rename alone moves the failure to readers: SQLite refuses a write on a file renamed out from under an open connection (SQLITE_READONLY_DBMOVED, 'attempt to write a readonly database', 1032). So opening a cache at the current schema version must not write at all; the busy_timeout in the DSN covers the remaining writes of an old-version cache. An in-process test with 8 goroutine writers + 4 reader loops reproduces all three errors deterministically, no subprocesses needed", "refs": ["ako/mxcli#951"]}
{"area": "mdl/linter", "date": "2026-10-03", "symptom": "mxcli report scores a project lower for lint rules that crash: under v0.24.0, project rules written by a newer mxcli (QUAL004, CUSTOM002 reading .document_noun_title) each produced an error-severity 'Starlark rule error: \"microflow\" struct has no .document_noun_title attribute' that counted 10 points against the project", "cause": "StarlarkRule.Check turned every evaluation error into an ordinary SeverityError violation, indistinguishable from a finding; BuildReport and Summarize counted it, and a configured rule severity was applied to it too", "fix": "ruleFailureViolation marks every failure Violation.RuleFailure; a missing struct attribute (matched on the evaluator message, since starlark flattens NoSuchAttrError via fmt.Errorf) becomes info 'rule <ID> needs a newer mxcli (<detail>)'; BuildReport splits RuleFailures out before counting and every report format lists them separately; Linter.Run skips the severity override for them; an 'undefined:' load failure gets a newer-mxcli hint", "insight": "The score measures the project, so anything about the tooling has to be partitioned out BEFORE counting, not filtered in the formatter. The control that makes the score assertion meaningful is a working rule's finding that does move the score", "issue": "ako/mxcli#952", "file": "mdl/linter/starlark.go (ruleFailureViolation); mdl/linter/report.go (BuildReport); mdl/linter/linter.go (Run); mdl/linter/report_format.go", "test": "mdl/linter/starlark_rule_failure_test.go"}
Loading
Loading