Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
41 commits
Select commit Hold shift + click to select a range
c78be20
fix(executor): gate the workflow microflow actions older Mendix does …
claude Oct 30, 2026
7ff1883
fix(executor): gate what alter microflow inserts as create microflow is
claude Oct 30, 2026
ba05e88
test(bug-tests): measure the 1787 workflow action gates on 9.24 and 1…
claude Oct 30, 2026
885d75a
docs(wiki): add the version-floor variant to duplicate-resolver-drift
claude Oct 30, 2026
df514e2
fix(debug): always send condition on add_breakpoint
claude Oct 30, 2026
be4327d
fix(debug): continue the paused flow by its debug_id
claude Oct 30, 2026
6434c99
fix(debug): fail a debugger request the runtime refuses
claude Oct 30, 2026
b5be179
test(debug): run `debug continue` end to end against a runtime stub
claude Oct 30, 2026
b26e538
fix(debug): inspect prints a primitive variable's value
claude Oct 30, 2026
cc075ca
fix(debug): print a refused request's reason without the usage text
claude Oct 30, 2026
72237cf
docs(findings): record ako/mxcli#1782, debugger refusals read as success
claude Oct 30, 2026
a0aa744
Merge origin/main into claude/loving-clarke-qun7w5
claude Oct 30, 2026
2b4359a
fix(catalog): base an entity's source reuse on its own subtree (#1803)
ako Oct 30, 2026
ef49cdf
fix(mpr): serve unchanged v2 units from one store file (#1804)
ako Oct 30, 2026
dda5080
Merge pull request #1808 from ako/claude/loving-clarke-qun7w5
ako Oct 30, 2026
f299fd1
Merge remote-tracking branch 'origin/main' into fix/1803-entity-level…
ako Oct 30, 2026
56ab354
Merge pull request #1807 from ako/fix/1803-entity-level-source-reuse
ako Oct 30, 2026
de38191
Merge remote-tracking branch 'origin/main' into fix/1804-incremental-…
ako Oct 30, 2026
d2621fc
Merge branch 'mendixlabs:main' into main
ako Oct 30, 2026
cfaf575
fix(debug): drop the "later slice" text now that continue and steppin…
claude Oct 30, 2026
a4361e5
Merge pull request #1814 from ako/fix/1804-incremental-catalog
ako Oct 30, 2026
6b8109d
fix(playwright): reach an app on this machine directly behind a proxy
claude Oct 30, 2026
48cba0f
docs(playwright): an app's own host name behind an environment proxy
claude Oct 30, 2026
3bbe6b0
fix(odata): write every entity-set mode and an action's empty paramet…
claude Oct 30, 2026
e3d6ff8
fix(canon): fill the domain-model and OData names an older mxcli left…
claude Oct 30, 2026
2890c8b
test(bug-tests): run every domain-model and OData write path for #1805
claude Oct 30, 2026
06c47e7
docs(findings): record ako/mxcli#1805, a writer branch and stored ele…
claude Oct 30, 2026
f7d7b1a
fix(executor): follow a self-reference retrieve from its FROM end in …
claude Oct 30, 2026
27d7e22
fix(check): refuse a list use of the one object an association retrie…
claude Oct 30, 2026
1397ac3
test(bug-tests): measure the 1793 self-reference retrieve on 11.13
claude Oct 30, 2026
8ee3530
docs(findings): record ako/mxcli#1793, a self-reference retrieve type…
claude Oct 30, 2026
61bc4ee
fix(executor): gate create workflow at Workflows$Workflow's 9.0.2 floor
claude Oct 30, 2026
395234f
fix(executor): gate the microflow actions the write refuses below the…
claude Oct 30, 2026
9b9fff7
fix(executor): gate loops, queued calls, show page arguments and data…
claude Oct 30, 2026
a93de10
fix(executor): gate the workflow parts the write refuses below their …
claude Oct 30, 2026
9aa1df3
fix(executor): gate the document shapes the write refuses below their…
claude Oct 30, 2026
b16c69e
test(executor): fail the build when a write path stores an ungated ne…
claude Oct 30, 2026
d714de2
test(bug-tests): measure the 1811 write-floor gates on 9.24 and 10.24
claude Oct 30, 2026
4a0dab5
docs(wiki): add the property-floor and coverage-test lessons to dupli…
claude Oct 30, 2026
b125b93
Merge origin/main into claude/loving-clarke-qun7w5
claude Oct 30, 2026
99c6c09
Merge pull request #1815 from ako/claude/loving-clarke-qun7w5
ako Oct 30, 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
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"area": "cmd/mxcli/docker", "date": "2026-10-30", "symptom": "ako/mxcli#1782 (mendixlabs/mxcli#1676): \"`mxcli debug break` prints \\\"Breakpoint set\\\" but the runtime never registers the breakpoint\" — the microflow runs to the end; a logging proxy showed add_breakpoint sent without `condition` and answered {\"message\":\"Missing property: condition\",\"status\":2}. Same session: `debug inspect <var>` printed {} for a String variable, `debug continue` after a step printed \"Continued.\" while the flow stayed paused until `continue --all`, and some debug calls seemed to hang while a flow was paused", "cause": "The /debugger/ endpoint answers every request with HTTP 200 and reports the outcome in the envelope: {\"result\":…,\"status\":0} on success, {\"result\":{},\"message\":…,\"status\":2} on a refusal (every reply measured on 11.13 and 11.15). DebuggerClient.post checked only the HTTP status, so every refusal came back as success with an empty result — and three malformed requests stayed invisible behind it: add_breakpoint omitted `condition` when empty (required: \"Missing property: condition\"), `continue` sent {} (required debug_id: \"Missing property: debug_id\"), and `inspect` sent a primitive to get_object, which takes objects only (\"The variable 'Greeting' is not a Mendix object.\"). The 'hang' is poll_events, a long poll that returns after ~25 s when no event is pending, called by every paused/step/inspect/continue that auto-resolves the flow", "file": "`cmd/mxcli/docker/debugger.go` (`post`, `DebuggerError`, `AddBreakpoint`, `Continue`/`ContinueAll`); `cmd/mxcli/cmd_debug.go` (`continue --flow`, inspect of a primitive, SilenceUsage in RunE); `cmd/mxcli/debug_resolve.go` (`pausedVariable`)", "insight": "An API that puts the outcome in the body turns a status-code-only client into one where every refusal is a success, and then each malformed request it sends is invisible: one missing envelope check hid three protocol bugs. Surface the envelope first; the other bugs then fail on their own (the existing continue test went red the moment the stub answered like the runtime). The wrong turn is the proposal's protocol table (`condition?`, `continue {}`): it was never read against the envelope. Probe the runtime directly — a 30-line client posting each action through a logging proxy — and keep the stub answering the runtime's way (status, plus a 'Missing property' refusal per required param); the old stub replied {\"result\":{}} to everything, so the tests passed against a protocol that does not exist. Not fixed, both measured: poll_events consumes events, and an event delivered to an abandoned poll (client timeout, a `timeout` wrapper) is lost — a short poll timeout is NOT a safe fix for the 25 s wait (control: with no poll in flight the same pause's event arrives); and queued paused_microflow events outlive the pause (one for a finished flow came back minutes later). Controls: stubbing the status guard fails 17 tests with 'the client reported success'; the pre-fix binary prints 'Breakpoint set' and exits 0 for a condition the runtime refuses", "refs": ["ako/mxcli#1782", "mendixlabs/mxcli#1676"]}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"area": "cmd/mxcli", "date": "2026-10-30", "symptom": "`mxcli playwright check http://frontend.local:8180/ -p app.mpr --role R` (frontend.local an /etc/hosts name for 127.0.0.1 and the app's configured root URL, app started with `run --local --detach`) printed a rendered page (right title, text=2021) with `console-errors=2`, two `ERR WebSocket connection to 'ws://frontend.local:8180/mxdevtools/' failed: Error during WebSocket handshake: Unexpected response code: 502` and `FAIL 2 console error(s)`, exit 1; the same check on http://127.0.0.1:8180/ passed with 0 console errors", "cause": "Not the runtime and not run --local: the runtime answers the /mxdevtools/ handshake 101 on the configured host name (curl --noproxy '*' with Host and Origin vm:18180, 11.15.0), and run --local puts nothing between browser and runtime. The 502 is the environment's HTTPS proxy. Chromium on Linux takes its proxy from the environment and, for a WebSocket, prefers the HTTPS proxy even for ws:// (RFC 6455 4.1.3). With only https_proxy set, every http:// load of the app went direct, while ws://frontend.local:8180/mxdevtools/ went to the proxy because no_proxy listed localhost and 127.0.0.1 but not the app's own name; the Claude Code agent proxy cannot reach this machine's loopback and answered `502 upstream request failed` (curl -p -x $HTTPS_PROXY: 200 Connection Established, then 502)", "file": "cmd/mxcli/docker/browser_proxy.go (directRouteEnv), called where mxcli starts a browser at the app: cmd/mxcli/docker/pagecheck.go (ProbePages), screenshot_login.go (LoginAndSaveStorage, SignOut), screenshot.go (CaptureScreenshot)", "fix": "the browser mxcli starts gets every host among its URLs that resolves only to this machine (loopback, or an address of one of its interfaces) appended to no_proxy and NO_PROXY, when the environment configures a proxy; the proxy settings themselves are untouched, so a remote --hub URL still uses it. No console error is ignored: the channel connects, and a genuine console error still fails the page", "insight": "The report called /mxdevtools/ mxcli's own channel and offered two fixes (ignore it in the verdict, or make run --local serve it on the configured host); both assumed the server answered 502 on that host. One curl straight at the runtime with the configured Host (101) and one through the proxy (502 'upstream request failed') settled who answers, and three standalone Playwright runs of the same page settled the route: env as is -> 502; the name appended to no_proxy -> 0 errors, 2 frames received; https_proxy unset -> 0 errors. The tell was the shape of the failure: the page loads, only the WebSocket fails, and only on a name not in no_proxy. Ignoring the channel in the classifier would have passed the page while the channel stayed broken in that browser, and an app's own ws:// to its host would still have failed the check. Repro without /etc/hosts edits: a Claude Code container maps `vm` and `runsc` to 127.0.0.1 and neither is in no_proxy, so `ApplicationRootUrl http://vm:<port>/` reproduces the report byte for byte. A unit test needs no host name either: an httptest page on the machine's own non-loopback address (Chromium bypasses only loopback by itself) plus a fake CONNECT proxy that answers the tunnelled request 502 reproduces the message verbatim. Scope: env proxies are what Chromium reads on Linux; macOS and Windows Chromium read the OS proxy settings, which this does not touch", "test": "cmd/mxcli/docker/browser_proxy_test.go (TestDirectRouteEnv_*: loopback name and interface address added; remote, split, unresolvable, already-listed, no-proxy and unparsable left alone; both no_proxy spellings; IPv6 bracketed); cmd/mxcli/docker/pagecheck_proxy_test.go (TestProbeReachesALocalAppsDevChannelDirectly: the real probe against a fake dev-mode page behind a refusing proxy, with an in-test control that reproduces the 502; skips without Node/Playwright). Control: directRouteEnv stubbed to return env fails all of them, the probe test with `Unexpected response code: 502`. Runtime A/B on 11.15.0 (mdl-examples/bug-tests/pagecheck-1800-host-name-behind-proxy.mdl, root URL http://vm:18180/): control binary / FAIL 2 console errors (2x 502), /p/brokenprobe 3; fixed / OK console-errors=0, /p/brokenprobe FAIL on its genuine error only", "refs": ["ako/mxcli#1800", "ako/mxcli#1216", "mdl-examples/bug-tests/pagecheck-1800-host-name-behind-proxy.mdl"]}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"area":"mdl/executor","date":"2026-10-30","symptom":"ako/mxcli#1793: for a self-referencing Reference M.Order_Parent (M.Order to M.Order), `retrieve $P from $Order/M.Order_Parent; $n = count $P;` passed `check -p --references` and exec (\"Created microflow\"), then mx check failed CE0013 \"Input variable 'P' must be of type 'List'.\" The full build typed $P `List of M.Order` unless an object use followed; the splice path (#1777) already typed it M.Order.","cause":"Three answers to 'which end does this association retrieve start from': addRetrieveAction asked only 'is the start the child (TO) entity', which a self-reference always is, so it read as a reverse traversal; flowBuilder.associationRetrieveType (#1777) special-cased the self-reference as the FROM end; check's associationRetrieveShape returned known=false for from == to and took the nearest generalization link otherwise; validateFlowBody's forwardReferenceTarget kept a self-association a list. And the check judged only a list used as an object, never one object used as a list, so even a forward Reference `count` (CE0013) was silent.","file":"mdl/executor/validate_retrieve_association_list.go (associationRetrieveEnd, associationRetrieveShape, listActivityInput, the MDL-RETRIEVE01 half of validateAssociationRetrieveListUse), mdl/executor/cmd_microflows_builder_actions.go (associationRetrieveEndFrom, addRetrieveAction, associationRetrieveType), mdl/executor/cmd_microflows_builder_validate.go (forwardReferenceTarget), mdl/executor/validate_flow_member_refs.go (scriptGeneralizationChain)","fix":"One rule, associationRetrieveEnd(isFrom, isTo, self): both ends + self-reference = FROM, both ends of two different entities = ambiguous (the build keeps its old TO reading, the splice and the check stay silent), else the end it is. The build, the splice, check --references and validateFlowBody all ask it. check --references now reports MDL-RETRIEVE01 for the one object a Reference followed from its FROM end binds (self-reference or not) taken by an aggregate (CE0013), a list operation but range (CE0097), a loop (CE0100) or a list parameter (CE0117); exec's pre-flight refuses the same. A one-to-one from its TO end is not judged (the build rewrites a list use into a database retrieve). The start's chain is read through the script's declarations first, so a specialization the script creates counts.","test":"cmd_microflows_builder_retrieve_self_reference_test.go (TestBuildFlowGraph_SelfReferenceIsFollowedFromItsFromEnd, TestBuildFlowGraph_AssociationDirectionControls, TestAssociationRetrieveType_AgreesWithTheFullBuild); validate_retrieve_self_reference_test.go (TestAssociationRetrieveObjectUsedAsList: 11 refused shapes, 10 controls; _ScriptDeclaredSpecialization; SelfReferenceSetUsedAsObject; TestCheckSetOnSelfReferenceObject). mdl-examples/bug-tests/1793-self-reference-retrieve-is-the-object.mdl. Control: the fix's source files reverted to main -> $P typed List of Sample.Node, the owner-Both self-reference expanded to a DatabaseRetrieveSource, the check returns no error for every list use. Knock-on: bug-tests 1323 (item 2, set over a self-reference Reference) and 1100 (SUB_LoopAssoc looped over a FROM-end Reference: exec as written is CE0100 on 11.13.0) held shapes the rule now refuses; both rewritten to the measured-valid shape.","insight":"Measure every activity kind before refusing, not one: over the same object on 11.13.0 every aggregate is CE0013, every list operation CE0097 EXCEPT range (Microflows$ListRange, 0 errors), a loop CE0100, a list parameter CE0117, and add/remove/clear/set (Change list) 0 errors — mx check does not type a Change list's variable or a range's. That last gap is why #1323's bug-test claimed 'Mendix types a self-association retrieve as a list': its `set` was written as Change list Replace and passed; mx check's CE0100 text names the variable's type outright (\"'P' is of type M.Order\"), which is the measurement to trust. When a fix adds a helper that answers a question another site answers too, grep for every site (here four) and route them through one function with a test that the two builder paths agree.","ce":["CE0013","CE0097","CE0100","CE0117","CE7247"],"rules":["MDL-RETRIEVE01","MDL-RETRIEVE03","MDL-SET01"],"refs":["ako/mxcli#1793","ako/mxcli#1777","ako/mxcli#1323","mendixlabs/mxcli#1516"]}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"area": "mdl/executor", "date": "2026-10-30", "symptom": "`mxcli check -p` passed a script with workflow microflow actions the project's Mendix version does not have, and exec then refused it at write time, part-way: on 9.24.30 `this Mendix 9.24.30 project cannot store Microflows$NotifyWorkflowAction (Mendix 10.0.0+)` after five statements (rolled back), and with --continue-on-error the script ran on without MF_Notify, MF_GetWorkflows, MF_ActivityRecords and an `alter microflow` that inserted `get workflows`", "cause": "#1774 added the write-time type floor (canon.UndeclaredTypeError) for every type, but check-time MDL-VERSION01 gates only for the workflow activity and event types. No gate named a workflow microflow action, and statementVersionGates had no case for `alter microflow|nanoflow` at all, so nothing a splice inserted was ever version-gated (send email and a named lock included)", "file": "`mdl/executor/version_gates.go` (typeGate, workflowActionGates, microflowBodyGates, nanoflowBodyGates, the AlterFlowStmt case); `mdl/executor/cmd_alter_flow.go` (execAlterFlow calls checkVersionGates); `sdk/versions/mendix-9|10|11.yaml` (workflows.workflow_microflow_actions 9.0.2, workflow_operations 9.2.0, workflow_operation_pause_continue 9.3.0, get_workflow_data 9.10.0, workflow_operation_retry_unpause 9.12.0, notify_workflow 10.0.0, get_workflows 10.4.0, get_workflow_activity_records 10.4.0)", "insight": "**A write-time floor with no check-time gate is a check/exec disagreement, and the sweep is mechanical**: list the $Types the statement's converter writes (mdl/backend/modelsdk/microflow_workflow_write.go: workflowMicroflowActionToGen, workflowOperationToGen), look each up in modelsdk/canon/mendixmodelsdk_type_versions.json, and gate every floor above the lowest supported version. That is 9.0.0 (sdk/versions supported_range), and it is real: the CDN lists mxbuild 9.0.0 and 9.0.1 builds (`curl 'https://cdn.mendix.com/?list-type=2&prefix=runtime/mxbuild-9.0.'`), so the 9.0.2 actions (call workflow, set task outcome, open user task, open workflow) are gated too. A workflow operation writes two types, the action (9.2) and the operation (abort/restart 9.2, pause/continue 9.3, retry/unpause 9.12): the floor is the later. **Keep the floor in sdk/versions and pin it, rather than reading canon at check time**: every gate reads the registry, `show features` lists it, and the message's version comes from it; a gate that read canon would be a second mechanism, and the yaml row could still drift from it. TestTypeGates_FloorIsTheTypeIntroduction pins each type gate's row (the #1774 ones too) to canon.TypeIntroduced of the types it names, at the exact boundary (available at the floor, not just below it). The migration reference does not read the yaml, so `check-migration-reference` is unaffected. **Lock and unlock (9.21) need no gate of their own**: every lock or unlock that check and exec accept names its workflow, and that selection is the 10.0 workflowDefinitionSelectionGate. **`alter microflow` had no version gate at all**: a body gate added for `create microflow` must reach the splice, so share one function (microflowBodyGates) and apply it to every operation's fragment; execAlterFlow calls it too, for exec without the pre-flight. `create or modify` needs nothing new: it is a CreateMicroflowStmt, gated on its whole declared body, and its splices do not go through execAlterFlow. **The mxbuild class-name oracle agrees**: `strings -a Mendix*.dll | tr -c 'A-Za-z0-9_\\n' '\\n' | sort -u` on 9.24.30 lacks NotifyWorkflowAction, GetWorkflowsAction and GetWorkflowActivityRecordsAction and has every other action and operation (ResumeOperation too, kept to load older models); 10.24.24 has all. Measured with the bug-test: on 9.24.30 before, check -p passed the script without its 10.0 workflow and exec refused it at write time; after, check -p and exec's pre-flight report MF_Notify (10.0), MF_GetWorkflows, MF_ActivityRecords and the alter (10.4), App.mpr is byte-identical, mx check 0 errors; the 9.0.2-9.12 actions, written with --no-check, load on 9.24 with 0 errors. 10.24.24 before and after: check -p clean, exec writes everything, mx check 0 errors. Control: without the table's gates in microflowBodyGates, the 70 create and 140 alter below-floor cases report nothing; a row moved off its type's introduction fails the pin test; without execAlterFlow's call, exec reports `microflow not found` instead of the refusal. Tests: version_gates_workflow_actions_1787_test.go; mdl-examples/bug-tests/1787-workflow-actions-below-their-version.mdl", "refs": ["#1787", "#1774", "#1771", "#1434"]}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"area": "mdl/executor", "date": "2026-10-30", "symptom": "On a Mendix 9.0.0 or 9.0.1 project `mxcli check -p` passed `create workflow` (and `create or modify workflow`), and exec then refused it at write time (\"cannot store … Workflows$Workflow (Mendix 9.0.2+)\", or, for a workflow with a parameter, the 9.6–9.18 Workflows$Parameter properties) after the statements before it; `alter workflow`'s hint said \"upgrade your project to Mendix 9.12+\"", "cause": "sdk/versions workflows.basic was typed as 9.0.0 when Workflows$Workflow is 9.0.2 in mendixmodelsdk 4.117.0, and the CreateWorkflowStmt case of statementVersionGates applied only the sub-feature gates; only AlterWorkflowStmt applied basic, with a comment and hint naming 9.12, a third version", "file": "`mdl/executor/version_gates.go` (workflowBasicGate, the CreateWorkflowStmt and AlterWorkflowStmt cases); `sdk/versions/mendix-9|10|11.yaml` (workflows.basic 9.0.2); `mdl/executor/version_gates_workflow_actions_1787_test.go` (pinned in TestTypeGates_FloorIsTheTypeIntroduction)", "insight": "**A `basic` row is a floor like any other, so pin it to the type the statement writes.** It looked exempt because 9.0.0 is the bottom of supported_range and the gate could never fire, which is also why nobody noticed that `create workflow` had no basic gate at all: a gate that cannot fire and a missing gate behave the same until the floor moves. **A hint that names a version is a second copy of the floor**, and this one disagreed with both the row (9.0.0) and the type (9.0.2); checkFeature already puts the registry's version in the message, so a hint should say what to do, not when. Measured on a 9.24.30 app whose _MetaData version was set to 9.0.1.0 (no 9.0.x mxbuild is cached; the gate and the write read only that field): before, check -p passed and exec created the microflow and refused the workflow at write time; after, check -p and the pre-flight report MDL-VERSION01 for create, create or modify and alter workflow, and App.mpr is byte-identical. Control: the row moved back to 9.0.0 fails the pin test (\"available at 9.0.1, where the write refuses [Workflows$Workflow]\") and the gate test; the create case without workflowBasicGate fails the gate test for both create forms. The same stamped run shows how much more is refused below 9.12 that no gate names, all by property floors: `create module` (Projects$ModuleSettings.ProtectedModuleType 9.12), `create entity` (ExportLevel 9.3) and a workflow parameter (Workflows$Parameter.Name 9.18)", "refs": ["#1809", "#1787", "#1774"]}
Loading
Loading