Skip to content

Commit 9eeb6e0

Browse files
authored
Merge pull request #1649 from ako/main
Sync ako/mxcli: Java action template and enum parameters, access rules after drop entity, splice refusals, STRING_AGG, mxcli test false greens
2 parents 15b5b60 + a2ff0e7 commit 9eeb6e0

116 files changed

Lines changed: 5749 additions & 251 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
{"area":"cmd/mxcli","date":"2026-10-25","symptom":"`mxcli test`: an @expect written over two lines (` * @expect $r = 'a;b'` then ` * and length($r) = 3`) kept only its first line with no message; --list, check and the run all asserted `$r = 'a;b'`, so with `and` the test passed on less than it said and with `or` it failed although the whole assertion held. Only a first line that did not parse on its own was reported, as a confusing \"expected a value at column 16\". Separately, `check -p` on a lone test file reported \"microflow not found\" for a target a setup script creates, with no sign that the script-set form (`check setup.mdl x.test.mdl -p`) resolves it","cause":"parseAnnotations read the doc comment one line at a time and expectPattern/verifyPattern captured the rest of the tag's own line; a following ` * …` line carries no tag, so it matched nothing and fell through every branch. The patterns also required text on the tag line (`\\s+(.+)`), so a bare `@expect` with the expression below it was dropped whole","file":"cmd/mxcli/testrunner/parser.go (parseAnnotations, openAssertion, continuationHint), cmd/mxcli/cmd_check.go (testFileMissingTargetHint, check --help)","fix":"@expect and @verify open an annotation whose text runs, as a javadoc block tag's does, to the next `@tag` line or the end of the comment; the joined text (single spaces) is what ParseExpect/ParseVerify compile. Text after the tag that is not part of the expression therefore makes the assertion an ERROR whose message says it spans N lines and that a description goes before the first tag. @test/@setup/@cleanup/@throws stay one line, so prose under a test's name is still description. check prints a hint naming the script-set form under a test file's \"not found\" reference errors, and check/test --help say a test file can be one of the set","insight":"The same silent-absence shape as the original @expect fix (#46), one level up: that fix made a line the parser could not compile an error, but a line the parser never LOOKED at was still dropped. `--list` printing the truncated text back is what made it look wired. Javadoc's rule (a block tag's text continues to the next tag) is the fail-closed choice; stopping at a blank line was considered and rejected because it re-opens the silent drop for an expression with a blank line in it, at the cost of turning prose written after the tags (seen once, in upgrade_source_test's fixture) into an ERROR with a message naming the fix. Runtime A/B on 11.13.0 (`mxcli test --local --db-type hsqldb`): baseline PASSED `$r = 'a;b'` + `and length($r) = 4` and FAILED the true `or` assertion with \"expected $r = 'zzz', actual: a;b\"; fixed inverts both. The paren-on-line-1 fixture is what makes `make check-mdl` fail if the continuation is lost again. Sibling not fixed: an untagged line after @throws is still ignored, so `@throws` + a message on the next line becomes \"any exception\"","test":"cmd/mxcli/testrunner/parser_multiline_annotation_test.go (8 tests; fail on revert with Raw = \"$r = 'zzz'\" etc.), cmd/mxcli/check_test_file_setup_hint_test.go TestCheck_TestFileMissingTargetPointsAtTheScriptSet (with the set-form and no-hint controls); mdl-examples/bug-tests/test-runner-1514-*.mdl","refs":["ako/mxcli#1514","mendixlabs/mxcli#1604"]}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
{"area":"cmd/mxcli","date":"2026-10-25","symptom":"`mxcli run --local --page-check` and `mxcli playwright check` reported a page whose list view had not yet received its data as `text=37` with no `rows=` field, and the check exited 0 (\"OK 4 page(s)\"); the same URL checked again later read `rows=2 text=49`. A data source slower than ~1s, or than the 4s cap, was never waited for and nothing flagged the verdict as incomplete","cause":"pageProbeJS settled a page when its body text held still for three 250ms polls with no `.mx-progress/.mx-underlay-loading/[aria-busy]` element, capped at 4s, and broke out of the cap silently. Measured on 11.13.0: while a list view's microflow data source runs, `.mx-listview` sits EMPTY with no progress element, no aria-busy and no `.mx-listview-empty` placeholder, and the text holds still for the whole 2s/8s — the only sign is the open POST /xas/ runtimeOperation. Data grid 2 does render `widget-datagrid-spinner` with aria-busy=true, which is why grids were measured correctly and list views were not","file":"cmd/mxcli/docker/pagecheck.go (pageProbeJS settle loop, PageSignals.Unsettled/PendingRequests/WaitedMs/DataWidgets, formatPageVerdict, DefaultPageWaitMs), cmd/mxcli/docker/pagecheck_assert.go (EvaluatePage), cmd/mxcli/docker/runlocal.go (reportPageChecks), cmd/mxcli/cmd_playwright.go (--wait default + help)","fix":"the probe tracks same-origin requests (request/requestfinished/requestfailed, redirects collapsed) and counts a page settled only when none has been open for 500ms AND the text is unchanged AND nothing is busy; a page not settled by the cap is reported `STILL LOADING` with a `WAIT` line naming the open requests, and EvaluatePage fails it. The cap is now 15s (a cap, not a sleep: a fast page is still measured in ~1s). A page with a list view/data grid/gallery always prints `rows=`, rows=0 included","insight":"Decide what 'rendered' means by sampling the DOM and the network every 50ms during a deliberately slow data source (a Java action calling Thread.sleep behind a microflow data source), not by reasoning about Mendix: the list view gives NO DOM signal at all, so no selector-based wait could have been right, and the grid's spinner made the bug look intermittent. Also from the timeline: on a fast page the data request starts ~3ms AFTER `.mx-page` mounts, so 'no request open' must hold for a window (500ms), not an instant. Repro of the reported shape: the ledger's cold-boot first check is the same thing with the JIT as the slow part. A fake page in an httptest server reproduces it in a unit test; its /xas/ handler must read the request body before blocking, or r.Context() never sees the browser hang up and srv.Close() waits out the full simulated delay. Sibling not fixed: `run --local --screenshot` (screenshot.go) still shoots after a fixed 4s wait, so a PNG can show the same empty list","test":"cmd/mxcli/docker/pagecheck_settle_test.go: TestProbeWaitsForTheDataSourceToRender / TestProbeReportsAPageThatOutlastsTheWait drive the real probe against a fake page (skip without Node/Playwright; run with NODE_PATH set) and fail with the old probe JS (rows = 0, want 2; not reported as still loading); verdict/EvaluatePage tests fail with the Go side reverted. Runtime A/B on 11.13.0 (mdl-examples/bug-tests/pagecheck-845-slow-datasource.mdl): baseline `run --page-check` slow2/slow8 `text=37` no rows, `playwright check` OK exit 0; fixed rows=2 on all, and `--wait 3s` on the 8s page gives STILL LOADING, exit 1","refs":["ako/mxcli#845","ako/mxcli#714"]}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
{"area": "mdl/executor", "date": "2026-10-25", "symptom": "(1) `alter microflow` with `insert after $X begin declare $Late Integer = 0; end;` and then `replace loop … with begin loop … set $Late = $Late + 1; … end;` passed check with a 'not predicted' note and failed at exec: 'the fragment has errors: variable 'Late' is not declared'. (2) A producer moved upstream — `create or modify` restating `$Code = call …` above the declare it followed, or the same as an alter insert+drop in either order — was refused 'the fragment declares $Code, which the microflow already has' (and, once past that, '$Code is still used by: log …; return $Code')", "cause": "Two variable verdicts of an alter block (and of the patch create or modify splices with, which is one) were still taken against the flow as stored: the fragment builder was seeded only with the stored flow's variables (storedVariables + the full build's entity types), so a later fragment could read but not `set` or member-access what an earlier operation declares; and checkFragmentScope refused a fragment declaring any variable a stored activity declares, at the moment it ran, before a later operation dropped that activity — while checkOutputUnused counted readers the inserted producer now feeds", "file": "mdl/executor/cmd_alter_flow.go (applyTo, fragmentBuilder, buildFragmentVars, noteFragment, checkOutputUnused, checkDeclaredUnclashed); mdl/executor/validate_flow_verdicts.go (alterFlowViolations)", "fix": "Each applied fragment's declared variables (with the types its builder resolved) seed later fragments' builders, in check's pre-build as in exec; the clash with a stored producer is judged once every operation is applied and skips one the block takes away (or whose loop it replaces); a stored or fragment reader of a removed output is no 'user' when another operation's fragment declares the variable upstream of it (declaredOnPath)", "insight": "2f42df658 moved checkOutputUnused to the end of the block but left its two siblings per-operation; any check that answers 'does this variable exist' has to be judged against the flow the block writes, or create or modify's own diff (which turns a reorder into insert+drop) trips it. A useful corollary: a create-or-modify body that builds declares each variable once, so a 'declares $X, which the microflow already has' refusal from the splice is always false — the 1361 test pinned one as expected and now asserts the splice. Known order rule kept: a declaration by a LATER operation is not known to an earlier fragment (check reports it unpredicted, exec refuses it)", "refs": ["ako/mxcli#1399", "mendixlabs/mxcli#1517", "mendixlabs/mxcli#1361"], "test": "TestAlterMicroflow_FragmentBuiltWithAnEarlierOperationsDeclaration, TestCreateOrModify_ProducerMovedEarlierKeepsItsReaders, TestAlterMicroflow_ProducerMovedEarlier (mdl/executor/cmd_alter_flow_block_vars_1399_pedapp_test.go); TestCheckFlowVerdicts_Issue1361_VariableClash"}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
{"area": "mdl/executor", "date": "2026-10-25", "symptom": "ako/mxcli#819 (follow-ups of #802), measured on a blank 11.13.0 app with security on. (1) `create or modify association Crm.Lead_Order from Crm.Lead to Hr.Employee owner both` — moving a cross-module OWNER Both association's TO end from Shop.Order — printed \"Modified association\" and mx check reported CE0066 \"Entity access is out of date\" at the domain models of both Shop (old TO) and Hr (new TO); moving it to another entity of the same module, or only changing the owner through create or modify, left CE0066 at the TO module. `update security` repaired Hr but not Shop. (2) `create association … from <non-persistent> to <persistent> owner both` passed check and exec and failed CE0001 \"An association between a persistable entity and a non-persistable entity must start in the non-persistable entity and have owner 'Default'.\"; so did a persistent FROM end under owner Default, and `alter association … set owner both`. (3) GRANT naming the TO-side member of a cross-module OWNER Both association (`read (Number, Lead_Order)`) already worked on main — fixed by 74bc820b7, verified 0 errors.", "cause": "(1) The cross-module OR MODIFY branch of execCreateAssociation overwrote ChildRef and owner and reconciled nothing itself; the end-of-program reconcile covered only the declaring module. And ReconcileMemberAccesses kept the old TO entity's entry even when its module was reconciled: a reference to another module's association was preserved on faith (the #758 rule for associations inherited across modules), and this one still exists, just not as a member. (2) No check or exec guard modelled CE0001.", "file": "mdl/executor/cmd_associations.go (OR MODIFY cross branch reconciles declaring, old TO and new TO modules via reconcileModulesAccess; refusals on create and alter set owner both); mdl/executor/validate_association_persistence.go (associationPersistenceViolation, projectEntityPersistence, storedAssociationEnds, ValidateAssociationPersistence — wired into ValidateProgram); mdl/backend/modelsdk/domainmodel_security_write.go (staleForeignRef: an existing foreign association on an entity whose generalization chain never leaves its module is stale unless collected as an incoming OWNER Both)", "insight": "The issue named one half of each defect. The move left a stale entry on the OLD TO module and a missing one on the NEW TO module — read the old ChildRef before overwriting it — and the CE0001 rule is two-sided: mxbuild's message (and a 4-cell measurement: NP->P default ok, NP->P both, P->NP default, P->NP both all CE0001) says FROM must be the non-persistable end AND owner Default, so refusing only `owner both` would have left the persistent-FROM case writing a broken model. Measure the whole matrix before scoping a refusal to the reported cell. For the reconcile, 'cannot be checked' was only true when the entity inherits from another module: with a module-local generalization chain the full member set is known (own + incoming OWNER Both), so anything else foreign is stale; the cross-module-inheritance control stays green. Persistability for the refusal comes from the backend (specializations resolved from their root, mendixlabs/mxcli#1600) in exec and from the script's own declarations first in check, through one predicate. A single bug-test script under-reports the move on the old binary: grants track Hr, so the end-of-program reconcile repairs the new TO module and only the old one shows.", "refs": ["ako/mxcli#819", "ako/mxcli#802", "ako/mxcli#1449"], "ce": ["CE0066", "CE0001"], "rules": ["MDL-ASSOC01"], "test": "mdl/executor/association_persistence_819_test.go (TestAssociationPersistenceViolation_MatchesMxbuild, TestValidateProgram_RefusesOwnerBothAcrossPersistenceFromTheScript, TestExecAssociation_RefusesOwnerBothAcrossPersistence, TestValidateAssociationPersistence_ResolvesTheProject, TestCreateOrModifyAssociation_MovingTheToEndReconcilesBothToModules); mdl/backend/modelsdk/reconcile_foreign_stale_assoc_test.go (TestReconcile_DropsAForeignAssociationWhoseToEndMoved, control TestReconcile_KeepsAForeignAssociationInheritedAcrossModules); mdl-examples/bug-tests/access-819-association-owner-both-member.mdl"}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
{"area": "mdl/executor", "date": "2026-10-25", "symptom": "`call java action BusinessEvents.PublishBusinessEvent_V2(Subject = 'Order shipped', BusinessEvent = $Event)` and `call java action AgentEditorCommons.Agent_CallDocument_WithoutHistory(Agent = 'M.MyAgent', …)` pass `check --references` and exec; mx check reports CE0115 \"The arguments that are passed to Java action '…' do not match the expected parameters and need to be refreshed\" at each call. `describe java action` showed the agent parameter as `Agent: String not null`; `describe microflow` printed a template value as its text only", "cause": "addCallJavaActionAction (mdl/executor/cmd_microflows_builder_calls.go) gave only entity-type and Microflow parameters their own value; a text-template parameter (CodeActions$StringTemplateParameterType) and a custom-document parameter (CodeActions$CustomBlobDocumentParameterType) fell to the default Microflows$BasicCodeActionParameterValue with the argument's source text. The document parameter TYPE also read back as a String (codeActionParamTypeFromGen had no case for it), and MDL had no way to write a template's {n} arguments at all", "file": "`mdl/executor/java_action_call_values.go` (javaActionTemplateValue, templateArgumentType + flowExprTypes, javaActionDocumentValue, javaActionCallValueErrors, MDL-JA02), called from `addCallJavaActionAction` and `validate.go` (validateFlowBodyReferences); `mdl/backend/modelsdk/microflow_write.go` / `microflow_read_actions.go` (PrimitiveTypedTemplateArgument, CustomBlobDocumentCodeActionParameterValue), `java_read.go` / `java_write.go` (CustomBlobDocumentType), `cmd_microflows_format_action.go` (describeTypedTemplate); grammar `callArgument … templateParams?` + visitor; tests `mdl/executor/cmd_microflows_builder_java_template_1451_test.go`, `mdl/backend/modelsdk/java_action_call_values_1451_test.go`, `mdl/visitor/visitor_java_action_template_1451_test.go`; fixtures `mdl-examples/bug-tests/java-action-1451-template-parameter.mdl`, `java-action-1451-expression-with-params.fail.mdl`", "insight": "**No Studio Pro reference exists here** (searched every .mpr/.mxunit/.mpk on the machine: 0 StringTemplateParameterValue, 0 CustomBlobDocumentParameterType outside module definitions; the Studio Pro MCP tunnel was down), so the shapes come from generated/metamodel and were *measured* against mxbuild 11.13 by hand-patching the unit (a throwaway Go probe over the v1 Unit table). What the probes settled, none of it guessable: a TypedTemplate argument is Microflows$PrimitiveTypedTemplateArgument{Expression, Type}; **Type is checked against the expression** (CE0117 for String-typed Integer, Integer-typed String, and for any Type on an enumeration expression — an enum is not one of the five); an absent Type reads as String; {1} with no argument is CE0720; Long attributes take Integer; `[%CurrentDateTime%]` takes DateTime — and exprcheck's inferKind calls every token a String, so use its argKind (now InferArgumentKind), which the built-in-function check already special-cased. The runtime hands Type to Java (ITemplateParameter.getParameterType), so the type is semantics, not decoration. For the document value mxbuild reports a wrong-type document as CE0115 and a missing one as CE1613 — check --references resolves agent-editor documents for that reason. The REST-body rule (#1444) carries over: a non-literal is '{1}' with the expression; but **describe must print `$X`, not `'{1}' with ({1} = $X)`**, because the declared-match pass (flow_declared_match) cannot know which CallArgument is a template, so only the short form re-execs as 'Unchanged' — except when the one argument is itself a string literal, which would read back as the text. A pre-fix build is no control for a script-created action: `create java action … StringTemplate(Text)` wrote a String parameter (separate fix), so the control is fixed-build action + pre-fix microflow (CE0115 x2), and the BusinessEvents module (CE0115 per call -> 0 errors)", "refs": ["ako/mxcli#1451", "mendixlabs/mxcli#1554", "ako/mxcli#1444", "mendixlabs/mxcli#1210"], "ce": ["CE0115", "CE0117", "CE0720", "CE1613"], "rules": ["MDL-JA02"], "test": "TestCallJavaAction_TemplateParameterWithArguments, TestCallJavaAction_TemplateParameterPlainString, TestCallJavaAction_TemplateParameterExpression, TestCallJavaAction_DocumentParameter, TestCallJavaAction_TemplateAndDocumentRefusals, TestCallJavaAction_TemplateAndDocumentDescribeRoundTrip, TestValidate_JavaActionTemplateAndDocumentArguments, TestJavaActionCallToGen_TemplateAndDocumentValues, TestJavaActionCallFromGen_TemplateAndDocumentValuesRoundTrip, TestJavaActionParamType_CustomBlobDocumentRoundTrips, TestCallJavaActionTemplateArgument, TestTemplateArgumentRefusedOutsideCallJavaAction"}

0 commit comments

Comments
 (0)