Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
47 commits
Select commit Hold shift + click to select a range
f2d674e
pages: alter page insert into a layout grid appends rows
claude Nov 10, 2026
e1721b0
feat(catalog): sequence flows and activity positions for lint rules (…
ako Nov 10, 2026
765aeb4
Merge origin/main
claude Nov 10, 2026
6053a59
fix(catalog): agent tools match ToolCount; consumed MCP endpoint in t…
ako Nov 10, 2026
cec8c93
fix(check): flag create or modify agents and MCP services left withou…
ako Nov 10, 2026
52de232
Merge origin/main
claude Nov 10, 2026
a73125a
docs(skills): genai-runtime, the runtime side of Agents Kit 2 (#2068)
ako Nov 10, 2026
87d382d
Merge pull request #2087 from ako/claude/issue-triage-prioritization-…
ako Nov 10, 2026
d3d5527
microflows: splice a change inside a multi-branch case
claude Nov 10, 2026
8fa3a1c
Merge remote-tracking branch 'origin/main' into fix/2090-agent-catalog
ako Nov 10, 2026
86b91b3
Merge pull request #2088 from ako/feature/1299-flows
ako Nov 10, 2026
7a55ea5
Merge pull request #2092 from ako/claude/issue-triage-prioritization-…
ako Nov 10, 2026
d91abfd
feat(test): run a .test.mdl test in the live app without building it …
claude Nov 10, 2026
cec6c42
Merge remote-tracking branch 'origin/main' into fix/2090-agent-catalog
ako Nov 10, 2026
ce065fa
Merge pull request #2091 from ako/fix/2090-agent-catalog
ako Nov 10, 2026
08de943
Merge remote-tracking branch 'origin/main' into fix/2089-agent-check-…
ako Nov 10, 2026
60e1a65
Merge remote-tracking branch 'origin/main' into docs/2068-genai-runti…
ako Nov 10, 2026
659108f
Merge remote-tracking branch 'origin/main' into docs/2068-genai-runti…
ako Nov 10, 2026
a0e6dc2
docs(spikes): measure Core.evaluateExpression as a test executor's ex…
claude Nov 10, 2026
1bba4a8
Merge pull request #2101 from ako/feat/test-interpreter
ako Nov 10, 2026
5d4a0b1
Merge remote-tracking branch 'origin/main' into docs/2068-genai-runti…
ako Nov 10, 2026
bc7764b
Merge branch 'main' into docs/2068-genai-runtime-skill
ako Nov 10, 2026
f9198ab
Merge pull request #2103 from ako/fix/2089-agent-check-create-or-modify
ako Nov 10, 2026
5cc0be2
Merge remote-tracking branch 'origin/main' into docs/2068-genai-runti…
ako Nov 10, 2026
6e8c0ad
Merge remote-tracking branch 'origin/docs/2068-genai-runtime-skill' i…
ako Nov 10, 2026
811b768
feat(test): route each test on its own, and run aggregates and associ…
claude Nov 10, 2026
412ce05
Merge pull request #2102 from ako/docs/2068-genai-runtime-skill
ako Nov 10, 2026
49ec06a
fix(run): no phantom "change pending" under --test-endpoint, and a le…
claude Nov 10, 2026
fb229a9
Merge remote-tracking branch 'origin/main' into feat/test-interpreter…
claude Nov 10, 2026
f82468f
fix(examples): write the differential test's aggregates in statement …
claude Nov 10, 2026
ee128c3
fix(mpr): keep the v1 Unit table scan and unit names between listings…
ako Nov 10, 2026
af4c68d
Merge pull request #2105 from ako/fix/test-endpoint-settings-swap
ako Nov 10, 2026
374f601
Merge remote-tracking branch 'origin/feat/test-interpreter-routing' i…
claude Nov 10, 2026
bfa3b08
fix(test): a test run holds the dev loop's watcher instead of making …
claude Nov 10, 2026
b88dc5b
Merge pull request #2104 from ako/feat/test-interpreter-routing
ako Nov 10, 2026
5f562c5
Merge remote-tracking branch 'origin/main' into fix/attach-holds-dev-…
claude Nov 10, 2026
d8baff7
perf(executor): resolve a flow's entity types per module, find a micr…
ako Nov 10, 2026
526fdce
Merge remote-tracking branch 'origin/main' into fix/2094-v1-listing-c…
ako Nov 10, 2026
3f3d960
Merge pull request #2117 from ako/fix/attach-holds-dev-loop
ako Nov 10, 2026
0b2d923
Merge remote-tracking branch 'origin/main' into perf/2094-domain-mode…
ako Nov 10, 2026
2a4d009
Merge remote-tracking branch 'origin/main' into fix/2094-v1-listing-c…
ako Nov 10, 2026
a7f7603
fix(test): test --local beside a --test-endpoint dev loop chains to t…
claude Nov 10, 2026
e78f568
docs(changelog): place the #2094 executor entry apart from the v1 one
ako Nov 10, 2026
e7a6121
Merge pull request #2120 from ako/fix/2119-test-local-under-hosted-en…
ako Nov 10, 2026
33f2103
Merge branch 'main' into fix/2094-v1-listing-cache
ako Nov 10, 2026
4ad184c
Merge pull request #2115 from ako/fix/2094-v1-listing-cache
ako Nov 10, 2026
f90a64b
Merge pull request #2118 from ako/perf/2094-domain-model-memo
ako Nov 10, 2026
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/commands/mendix/lint.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,8 @@ mxcli lint -p app.mpr --baseline lint-baseline.json
| MPR007 | security | PageNavigationSecurity - Navigation pages need allowed roles (CE0557); every user role must be able to open its home page (at Production security, else CE2729 per widget) |
| MPR012 | correctness | ReactUnsupportedWidget - static/dynamic image, drop-down, legacy data grid and reference selector are unsupported by the React client (CE0582; info on the classic client) |
| MPR013 | correctness | SnippetCallAppearance - Class/Style/DynamicClasses/design properties on a snippet call never reach the page (it renders no element) |
| MPR014 | correctness | AgentWithoutSelectedModel - an agent with no selected model, which Agent Editor 2.2+ refuses at startup, stopping the import of every agent (not reported for AgentEditorCommons before 2.2) |
| MPR015 | correctness | ConsumedMCPServiceWithoutEndpoint - a consumed MCP service with no endpoint constant, which Agent Editor skips at startup |
| SEC001 | security | NoEntityAccessRules - Persistent entities need access rules |
| SEC002 | security | WeakPasswordPolicy - Password minimum length should be 8+ |
| SEC003 | security | DemoUsersActive - Demo users should be off at Production security |
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"area": "cmd/mxcli", "date": "2026-11-10", "symptom": "Field test on ako/mxcli#2101: with `run --local --watch --test-endpoint`, `run status` said \"change pending\" for the whole session (5+ minutes) and nothing was ever built; `git status` showed the project settings modified, and a container recycle left AfterStartupMicroflow = MxTest.RegisterEndpoint in the project, where it could be committed.", "cause": "Two. The boot build scaffolds themesource/mxtest/ for the endpoint's module; the watcher excludes a build's own scaffold (sourceMTimeExcept, ako/mxcli#2033) but `run status` and `run wait` compared docker.SourceMTime, the unfiltered signal, with the boot's Last.Source, so they saw a change the watcher rightly never built (and `run wait` waited into its timeout). And only `run stop` and the next --test-endpoint run called RestoreAbandonedEndpoint, so a run killed with its container left the after-startup swap in place with nothing looking at it.", "file": "cmd/mxcli/docker/runstate.go (RunState.Scaffold, RunStateRecorder.Scaffold, SourceMTimeOf), cmd/mxcli/docker/runlocal.go (record the scaffold in the state), cmd/mxcli/cmd_run_lifecycle.go (status and wait use SourceMTimeOf), cmd/mxcli/testrunner/host_restore.go (LeftoverEndpointWarning), cmd/mxcli/cmd_run.go and cmd_check.go (restore and warn)", "insight": "A fix to a change signal has to reach every reader of that signal, not only the loop that reported the bug: #2033 fixed the watcher and left `run status`/`run wait`, which read the same question from the state file, on the old answer. Persist the filter with the state, so a reader cannot compute a different signal. The 'settings modified in git' symptom was a red herring for the pending change; the measurement that settled it was listing the files newer than Last.Source (all themesource/mxtest/). The test fails with SourceMTimeOf reduced to the unfiltered signal; on the spike app run status showed no pending change, run wait returned at once, an edit to the scaffolded folder still built, and a SIGKILLed hosted run was warned about by check -p and removed by the next plain run.", "refs": ["ako/mxcli#2101", "ako/mxcli#2033", "ako/mxcli#1917"], "test": "cmd/mxcli/docker/runlocal_scaffold_test.go, cmd/mxcli/testrunner/host_restore_test.go"}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"area": "cmd/mxcli", "date": "2026-11-10", "symptom": "ako/mxcli#2119: `mxcli test --local` while `run --local --watch --test-endpoint` ran: the scratch runtime's after-startup recursed (MicroflowException: java.lang.StackOverflowError), no test ran, and the dev loop's next build failed with CE1613 'MxTest.RegisterEndpoint no longer exists'. After the fix, a following --attach was refused: 'this change needs a runtime restart'.", "cause": "captureProjectState took the current after-startup microflow, already the dev loop's MxTest.RegisterEndpoint, as the project's own; the test run's endpoint (same name) chained to it, i.e. to itself. With MxTest pre-existing, the endpoint cleanup still dropped RegisterEndpoint, the Java action and its source, then set the setting back to the captured RegisterEndpoint. Separately, a test run that builds rewrites deployment/, which it shares with the dev loop by necessity (mxbuild cannot move it); a hold released over an unchanged model left that deployment as the test run's, and the next attach's build wanted a restart.", "file": "cmd/mxcli/testrunner/runner.go (projectState.hosted/chain, classifyHostedEndpoint, RestoreAbandonedEndpoint before capture), cmd/mxcli/testrunner/runner_endpoint.go (cleanup leaves a hosted endpoint), cmd/mxcli/docker/watchhold.go (Deployment identity, holdResync), cmd/mxcli/docker/runlocal.go", "insight": "'The current after-startup microflow' is not 'the project's after-startup microflow' once anything swaps it; the swapper's restore record is the only place the original lives, so every reader of the setting must consult it. And a cleanup keyed on 'did I create the module' is wrong when someone else owns a document inside it. The follow-on deployment drift was found only by running the next command in the workflow (attach) after the fix, not by the reported reproduction: verify the workflow, not just the repro. Measured: restoring deployment/model/ by copy is not a fix (it trips the stale-model check), so the dev loop resyncs with one build, which after --local was a restart.", "refs": ["ako/mxcli#2119", "ako/mxcli#2117", "ako/mxcli#2105"], "test": "cmd/mxcli/testrunner/hosted_endpoint_test.go, cmd/mxcli/docker/watchhold_test.go"}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"area": "cmd/mxcli", "date": "2026-11-10", "symptom": "Field test on ako/mxcli#2104 (and ako/mxcli#2063): a `test --attach` run with a test that had to be built, or a `test --local` run, made the project's `run --local --watch` dev loop rebuild (the first build failing) and apply by restarting the runtime, dropping every browser session including a --hub preview; on the spike app the restart itself failed with EOFException. The attach's own message said the app's serve process was left alone.", "cause": "Both write the test microflows into the same .mpr the dev loop watches. The attach used a serve process of its own for its build, but the watcher saw the model change, and then the cleanup, as edits. --attach also never restored the .mpr byte for byte (only --local did), so even after cleanup the model differed.", "file": "cmd/mxcli/docker/watchhold.go (HoldWatch/ReleaseWatch, ModelDigest, watchHoldGate), cmd/mxcli/docker/runlocal.go (the watcher consults the gate), cmd/mxcli/docker/runstate.go (HeldBy, Covered), cmd/mxcli/testrunner/runner_attach.go and runner.go (hold + snapshot restore), cmd/mxcli/cmd_run_lifecycle.go (status shows the hold)", "insight": "Two processes writing one project need a protocol, not a better watcher heuristic: the writer announces itself and the watcher decides on release by content, not mtimes. The release check needs the project put back byte for byte, which test --local already did (projectSnapshot) and --attach did not. Moving the watcher's baseline is not enough on its own: the run state's Last.Source must move with it, or `run status` and `run wait` see a pending change no build will cover. The control: with the hold disabled three gate tests fail; on the spike app main's binary rebuilt twice for one test --local run (restart failed) and once for a mixed attach run, the fixed binary built nothing and an edit afterwards still built.", "refs": ["ako/mxcli#2104", "ako/mxcli#2063", "ako/mxcli#2105"], "test": "cmd/mxcli/docker/watchhold_test.go"}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"area": "mdl/executor", "date": "2026-11-10", "refs": ["ako/mxcli#2089", "ako/mxcli#2031", "ako/mxcli#2060"], "ce": [], "symptom": "MDL-AGENT02 (agent without SelectedModel) and MDL-AGENT03 (consumed MCP service without Endpoint) never fired for `create or modify`, the form project scripts and describe use to create a document, so a new agent or service written that way passed check, check --references and exec silently; and lint flagged no stored document without either.", "file": "mdl/executor/validate_agent_left_unset.go (warnAgentDocumentsLeftUnset, agentEditorReadsSelectedModel), wired in validate.go validateProgramWithWarnings (check --references and exec preflight); mdl/linter/rules/agent_documents.go (MPR014 AgentWithoutSelectedModel, MPR015 ConsumedMCPServiceWithoutEndpoint), mdl/linter/context_catalog_tables.go (Agents, ConsumedMCPServices, MarketplaceModuleVersion), registered in cmd/mxcli/cmd_lint.go and mdl/executor/cmd_lint.go. Tests: validate_agent_left_unset_test.go, rules/agent_documents_test.go", "cause": "The static rules skipped every create or modify because a key it leaves out keeps the stored value; a static rule cannot see whether a value is stored, so the skip also covered a new document, which has nothing to keep.", "insight": "**A rule that exempts `create or modify` because it 'keeps the stored value' needs a project-aware half, or it exempts the most common way to create.** The static pass cannot distinguish new from existing; the reference pass (validateProgramWithWarnings, shared by check --references and exec) can, replaying drops, creates and alters in statement order over the stored documents. The selected-model half is version-gated: AgentEditorCommons 1.1.0 does not read selectedModel, and FactoryManagement's agents (1.1.0) have none, so an ungated rule would flag every agent of a working app. MPR014 on a scratch ICXGraphPilot copy flagged an agent that the original has a selected model for: that copy had been through a pre-#2031 describe/exec, which drops it; a fresh copy lints clean. Controls: stubbing the pass, removing either version gate, and an empty ConsumedMCPServices accessor each fail their test; a build without the change passes the issue's script with `Check passed!` and no warning."}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"area": "mdl/executor", "date": "2026-11-10", "symptom": "ako/mxcli#2071 (mendixlabs/mxcli#1791): `alter page M.P { insert into lgMain { row { column { … } } } }` failed with 'row needs a name'; with `row r2` it failed with 'it is not a simple container'. Only INSERT AFTER a widget inside a column worked.", "cause": "An INSERT's nodes were built one by one as widgets, and a layout grid row is not a widget: the generic builder demands a name it never stores, and InsertWidget appends to a Widgets list a layout grid does not have. Tab pages had the same shape and got their own path in #1215.", "file": "mdl/executor/cmd_alter_page.go (allLayoutGridRows), mdl/backend/pagemutator/layoutgridrows.go (InsertLayoutGridRows)", "insight": "Every non-widget child kind (tab page, list view template, navigation list item, now layout grid row) needs its own insert path; the tell is a list key other than Widgets. A row has no stored Name, so INTO the grid is its only position: refuse BEFORE/AFTER with the INTO form named rather than invent an address. Verified on TestApp: both reported forms append, describe → create or modify reports Unchanged, mx check 0 errors.", "refs": ["ako/mxcli#2071", "mendixlabs/mxcli#1791", "mendixlabs/mxcli#1215"], "test": "mdl/executor/cmd_alter_page_layoutgrid_row_1791_test.go, mdl/backend/pagemutator/layoutgridrows_test.go"}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"area": "mdl/executor", "date": "2026-11-10", "symptom": "ako/mxcli#2035 (mendixlabs/mxcli#1778): `create or modify microflow` was refused with 'replace case $Source: cannot replace the ExclusiveSplit … it has 9 paths, not two' (or 'drop case …') when anything differed inside a branch of a multi-branch case, including a one-activity change, and also when that case sat in one changed run with activities replaced before it.", "cause": "diffFlowBody had shell matches for if (sameIfShell, branches spliced) and loops (sameLoopShell / loopPair), but none for an enumeration case: a case that differed anywhere inside was replaced as a whole — or, in a run with other changes, replaced-first-and-dropped — and the splice only takes out a two-path decision.", "file": "mdl/executor/cmd_flow_modify.go (sameCaseShell, casePair)", "insight": "Each compound statement needs both halves: a shell match for a lone pair (diff its bodies) and a pair finder that splits a run around it, or a change next to it drags it into a replace. Pair branches by position, as describe prints them; a reordered branch is a real split change. Reproduced on TestApp (unfixed binary refuses, fixed splices '1 edited in place', re-run Unchanged, mx check 0 errors). The reporter's other observation — check passing what exec refuses because check reads the catalog's source text (mendixlabs/mxcli#1779) — is a separate cause.", "refs": ["ako/mxcli#2035", "mendixlabs/mxcli#1778", "mendixlabs/mxcli#1779", "ako/mxcli#1694", "ako/mxcli#942"], "test": "mdl/executor/cmd_flow_modify_case_shell_1778_test.go"}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"area": "mdl/executor", "date": "2026-11-10", "symptom": "ako/mxcli#2094 (mendixlabs/mxcli#1795), follow-up to #2115: after the v1 listing fix, a one-activity `create or modify microflow` still cost 0.35 s per statement on a 3,400-unit project (and 0.06 s on a 1,054-unit v2 one); the profile was ListDomainModels (32%) and ListMicroflows (22%), on v1 and v2 alike.", "cause": "buildMicroflowFromStmt's entityResolver decoded every domain model of the project on EACH entity lookup (6 full decodes for a flow with two entity parameters and an entity return); the nanoflow and rule builders decoded them all once per build; findMicroflowByQualifiedName decoded every microflow to pick one by qualified name, and `create or modify microflow` calls it per statement.", "file": "mdl/executor/entity_resolver.go (flowEntityResolver: lazy, GetDomainModel per named module, full listing only as fallback), cmd_microflows_build.go + cmd_rules_create.go (use it; a backend failure is reported, not read as 'entity not found'), calculated_attributes.go (findMicroflowByQualifiedName via microflowsNamed)", "insight": "Peek the profile's CALLERS of the expensive listing before designing a cache: 73% of ListDomainModels came from one closure and 100% of ListMicroflows from one helper, so two local fixes did what a cross-statement backend memo would, without its invalidation risk (64 and 51 call sites, and the verdict pass applies simulated flow writes, so a pass-wide microflow memo would serve stale flows). Keep the lookup LAZY: listing the modules up front made every build read every module unit on v2 even for flows with no entity type, which TestCheckPasses_ReadEachUnitFileOnce caught (20 reads vs 12). Count backend calls with a wrapper that embeds MockBackend and adds the by-name/by-module methods the mock lacks; the mock's GetDomainModel returns (nil, nil), so the resolver must fall back to the full listing for the other 80-odd mock tests. Measured, OneHarness v1: create or modify 0.86 -> 0.58 s alone, 0.115 s with #2115; check of 150 statements 76 -> 50 s alone, 10 s with #2115; v2 TestApp 0.061 -> 0.020 s.", "refs": ["ako/mxcli#2094", "mendixlabs/mxcli#1795", "ako/mxcli#2115", "mendixlabs/mxcli#1272"], "test": "mdl/executor/flow_build_decodes_2094_test.go, mdl-examples/bug-tests/2094-flow-build-lookups.mdl"}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"area": "mdl/catalog", "date": "2026-11-10", "refs": ["ako/mxcli#2090", "ako/mxcli#2075", "ako/mxcli#2060"], "ce": [], "symptom": "CATALOG.AGENTS.ToolCount did not match count(*) over CATALOG.AGENT_TOOLS (FactoryManagement: 5 against 6), because AGENT_TOOLS also held the agent's consumed MCP services, which ToolCount and the documented 'tools that run without approval' query exclude. CATALOG.CONSUMED_MCP_SERVICES had no Endpoint or AuthenticationMicroflow column, and `list callers` of a service's credentials microflow answered '(no callers found)'.", "file": "mdl/catalog/tables.go (agent_tools view filters ToolType <> 'MCP', agent_mcp_services reads agent_tools_data; consumed_mcp_services Endpoint, AuthenticationMicroflow; CatalogSchemaVersion 44), builder_agenteditor.go agentEditorDetail, builder_agent_refs.go agentEditorRefs (ConsumedMCPService: endpoint -> CONSTANT, call -> MICROFLOW), builder_references.go RefKindEndpoint. Tests: builder_agent_details_test.go TestAgentDetails_Issue2075, TestConsumedMCPServiceRefs_Issue2090", "cause": "#2080 stored every tools-list entry in agent_tools_data and exposed it whole as AGENT_TOOLS, with AGENT_MCP_SERVICES a filtered view over it, while the agent row counted the two apart. The consumed MCP service columns and refs predate #2086, which made endpoint and authenticationMicroflow part of the model.", "insight": "**A count column and the table it summarises must be tested against each other, not each against a fixture.** The #2075 test asserted ToolCount and the AGENT_TOOLS rows separately, both 'right', and the mismatch only showed in a query joining them; the new assertion compares ToolCount with count(*) per agent. When a document gains a modelled reference (#2086), its catalog row and refs edge are part of the same change: the reference was writable for a day while `list callers` still called its microflow dead. Control: a main build on the same FactoryManagement copy reports 5 vs 6 and no callers."}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"area": "mdl/catalog", "date": "2026-11-10", "symptom": "A Starlark rule could not see a microflow's sequence flows (which log belongs to which custom error handler, which activity an annotation is attached to, which split branch is true) nor where an object sits on the canvas: flows_for() was undefined and activities had no x/y.", "cause": "The reader decodes ObjectCollection.Flows and AnnotationFlows and each object's RelativeMiddlePoint and Size (describe prints them as on error, @annotation and @position), but the catalog builder walked Objects only.", "file": "mdl/catalog/builder_microflows.go; mdl/linter/context.go; mdl/linter/starlark.go", "insight": "Measure where the flows live before walking: on 11.12 every flow, loop bodies' included, is on the document's top-level Flows (bson dump --format ndsl), but an object's RelativeMiddlePoint inside a loop is relative to the loop's box, so x/y compare only within one parent_loop_id. A boolean split's branch reads back as an EnumerationCase with Value 'true'/'false', not a BooleanCase. On the Studio Pro-authored TestApp all 1476 flow ends resolve to an activities Id.", "issue": "ako/mxcli#1299"}
Loading
Loading