Skip to content

fix: TimeRegistration findings #51–#55 (ALTER-PAGE assoc binding, microflow check false-positives, workflow-action describe) - #55

Merged
ako merged 4 commits into
mainfrom
claude/mxbuild-diagnostics-spike-emta6h
Jul 30, 2026
Merged

fix: TimeRegistration findings #51–#55 (ALTER-PAGE assoc binding, microflow check false-positives, workflow-action describe)#55
ako merged 4 commits into
mainfrom
claude/mxbuild-diagnostics-spike-emta6h

Conversation

@ako

@ako ako commented Jul 30, 2026

Copy link
Copy Markdown
Owner

Fixes five findings from the ako/mxcli-timeregistration retest. Every fix was verified against real mxbuild 11.12.1 (not just mxcli check), and each has a regression test + a mdl-examples/bug-tests/ repro + a fix-issue.md symptom row.

Findings addressed

#55ALTER PAGE INSERT/REPLACE dropped a widget's attribute binding in a non-database-sourced list (c663f7af, a91e7329)
Inserting/replacing a data-bound widget into a list whose datasource was from association or datasource: microflow/nanoflow bound the new widget's Attribute to the wrong entity, or to nothing — mxcli check passed but the Mendix build failed CE1613 / CE0402, and DESCRIBE masked it by printing only the short attribute name. Root cause: the page mutator read the enclosing entity from DataSource.EntityRef.Entity, which is only populated for a direct ref (database).

  • Association (c663f7af): the destination lives on the last EntityRefStep of an IndirectEntityRef. extractEntityFromDataSource now reads it.
  • Microflow/nanoflow (a91e7329): a flow source stores no entity in its BSON — its entity is the flow's return type (in the flow document). The mutator's new EnclosingDataSourceFlow returns the governing flow's qualified name (nearest-enclosing for sibling INSERT/REPLACE, or own for INSERT INTO; a nearer non-flow source shadows an outer flow), and the executor resolves the return entity via getMicroflowReturnEntityName/getNanoflowReturnEntityName.

Verified: nested dataview→assoc-listview, and microflow-sourced listview, both INSERT and REPLACE → mx check 0 errors (CE1613/CE0402 before). This also closes the remaining half of #49.

#53 — MDL048 false-positive on [id = '[%CurrentUser%]'] (25b02acd)
The standard signed-in-user idiom (mx-check clean) was rejected. checkXPathIdConstraint now skips a '[%…%]' server-token operand; real stored-id values ([id = $Id]) are still flagged.

#52 — MDL045 false-positive on division by an association-attribute path (25b02acd)
round($a div $obj/Attr * 100) was rejected. The grammar parses div/*// at one precedence level, so $a div $obj/Attr mis-nests as ($a div $obj) / Attr; Mendix has no /-division operator and re-parses the raw $obj/Attr as a path (serialized output preserves the /, so mx check → 0 errors). exprHasSlashDivision now ignores a / whose right operand is a bare IdentifierExpr.

#54describe microflow rendered set task outcome as -- Empty action (25b02acd)
Under the default (modelsdk) engine, set task outcome / open user task / notify workflow read back as -- Empty action, so a describe→drop→exec round-trip silently dropped them. Added the three read cases to actionFromGen, mirroring the legacy parsers.

#51create association not idempotent (25b02acd)
The corrupting cascade was already fixed (mendixlabs#90). create association erroring on re-run is correct SQL-shaped semantics; the idempotent form create or modify association was undiscoverable. Improved the "already exists" error to name it (and drop association …).

Not code changes: #50 (daysBetween sign) is Mendix runtime behavior with no mxcli surface; #52's dateTime()-literal restriction is genuine platform behavior already surfaced by MDL046.

Testing

🤖 Generated with Claude Code

claude added 4 commits July 30, 2026 03:18
… bindings (FINDINGS #55)

ALTER PAGE INSERT/REPLACE into a ListView bound `from association` produced a
widget whose Attribute binding pointed at the wrong entity — the outer data
view's entity instead of the association's destination — or no binding at all.
`mxcli check` passed; the Mendix build then failed with CE1613 "The selected
attribute 'Module.OuterEntity.Attr' no longer exists", and DESCRIBE masked it by
printing only the short attribute name.

Root cause: the page mutator read the enclosing entity from
DataSource.EntityRef.Entity, which is only populated for a DIRECT entity ref
(database source). An AssociationSource stores its destination on the last
DomainModels$EntityRefStep of an IndirectEntityRef, so the mutator saw no entity
for the list and left the context at the outer data view's entity; the inserted
bare attribute then resolved against that outer entity.

Fix: extractEntityFromDataSource now also reads the IndirectEntityRef's last
EntityRefStep.DestinationEntity (new lastStepDestinationEntity helper), so a
list bound `from association` reports its correct child entity to INSERT/REPLACE.

Verified on real mxbuild 11.12.1: the nested dataview→association-listview→insert
and →replace cases now `mx check` with 0 errors (CE1613 before). Unit guard
TestEnclosingEntity_AssociationSource; repro
mdl-examples/bug-tests/55-alter-page-insert-assoc-binding.mdl.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JXnEgoc2NQP1Y2TWMCMXC4
…ore workflow-action describe (FINDINGS #51–54)

Four findings from the TimeRegistration retest, all verified against real
mxbuild 11.12.1:

#53 — MDL048 rejected `retrieve … where [id = '[%CurrentUser%]']`, the standard
signed-in-user idiom (mx check → 0 errors). checkXPathIdConstraint now skips a
`'[%…%]'` server-token operand; a real stored-id value is still flagged.

#52 — MDL045 rejected division whose divisor is an association-attribute path,
e.g. `round($a div $obj/Attr * 100)`. The grammar parses div/*/`/` at one
precedence level, so `$a div $obj/Attr` mis-nests as `($a div $obj) / Attr` with
`Attr` a bare identifier; MDL045 saw the `/` as division. Mendix has no `/`
division operator and re-parses the raw `$obj/Attr` as a path — the serialized
output preserves the `/` and mx check passes. exprHasSlashDivision now ignores a
`/` whose right operand is a bare IdentifierExpr (member navigation).

#54 — `describe microflow` printed `-- Empty action` for `set task outcome`
(and open user task / notify workflow) under the default modelsdk engine, so a
describe→drop→exec round-trip silently dropped it. The write path and describe
formatter already handled these; only the modelsdk read case (actionFromGen) was
missing. Added SetTaskOutcome/OpenUserTask/NotifyWorkflow read cases.

#51 — `create association` erroring on re-run is correct SQL-shaped semantics
(not idempotent); the idempotent form `create or modify association` was
undiscoverable. Improved the "already exists" error to name it (and
`drop association …`).

(#50 daysBetween sign and #52's dateTime-literal restriction are genuine Mendix
platform behavior — the latter is already surfaced by MDL046 — so no code change.)

Tests: TestValidateMicroflow_XPathIdConstraint (CurrentUser token),
TestValidateMicroflow_SlashDivision (div-by-assoc), TestActionFromGen_WorkflowActions.
Repros: mdl-examples/bug-tests/{52-53-microflow-check-false-positives,54-describe-set-task-outcome,51-create-or-modify-association}.mdl.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JXnEgoc2NQP1Y2TWMCMXC4
…ERT/REPLACE bindings (FINDINGS #55)

Retest of #55 showed the association + database datasource cases are fixed, but
a microflow/nanoflow datasource still bound nothing: inserting/replacing a
data-bound widget in a list bound `datasource: microflow …` produced an unbound
attribute (mx check CE0402 "No value specified" / CE1613).

Unlike a database (direct EntityRef) or association (IndirectEntityRef) source,
a MicroflowSource/NanoflowSource stores no entity in its own BSON — the entity
is the flow's RETURN type, which lives in the flow document. So the mutator's
BSON walk yielded "" and the bare inserted attribute resolved against nothing.

Fix: the page mutator's new EnclosingDataSourceFlow returns the qualified name
of the microflow/nanoflow governing the target's context — the nearest ENCLOSING
datasource for sibling INSERT/REPLACE, or the widget's OWN datasource for INSERT
INTO — via findNearestDataSourceDoc, which returns the nearest datasource *doc*
so a nearer non-flow source (database/association) correctly shadows an outer
flow. The executor (resolveDataSourceFlowEntity) then resolves that qualified
name to the flow's return entity via the existing
getMicroflowReturnEntityName / getNanoflowReturnEntityName, and uses it as the
widget's entity context when the BSON walk found none.

Verified on real mxbuild 11.12.1: INSERT and REPLACE into a microflow-sourced
ListView now mx check with 0 errors (CE0402 before). New EnclosingDataSourceFlow
interface method + mock/mcp impls (mcp is a no-op — its model resolves entities
directly). Unit guard TestEnclosingDataSourceFlow (incl. nearer-source shadowing);
repro extended in mdl-examples/bug-tests/55-alter-page-insert-assoc-binding.mdl.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JXnEgoc2NQP1Y2TWMCMXC4
…ct arg (write is correct)

Investigation of FINDINGS #56 ("describe page omits a show_page action's
arguments"), verified against mxbuild 11.12.1:

A widget button's show_page stores FormSettings.ParameterMappings as an empty
list [2] and Mendix infers the current-row object for each unmapped page
parameter. Storing an explicit `Argument: "$currentObject"` mapping makes
mxbuild report CE0115 "arguments do not match" — the original issue mendixlabs#296,
re-confirmed here. So the empty-mapping write is REQUIRED for a building app.

Consequence: `show_page X` and `show_page X($p = $currentObject)` serialize to
identical BSON, so describe→drop→exec re-produces a byte-identical valid page —
the round-trip is functionally lossless; only the redundant $currentObject
annotation is not echoed. No writer change (a fix there reintroduces CE0115);
clarified the serializer comment and added a bug-test documenting the verified
behavior and the boundary (a non-$currentObject widget page arg needs a
Studio-Pro WidgetValue reference to encode).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JXnEgoc2NQP1Y2TWMCMXC4
@ako
ako merged commit 672c3e3 into main Jul 30, 2026
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants