Fix #834 (ALTER PAGE into a customContent column) + two defects found with it - #118
Merged
Conversation
DESCRIBE printed a bare `else` for a `split type` that never had one, and a
describe→exec roundtrip accumulated another each pass.
An object-type decision always carries an `(empty)` outgoing flow — the
null-object case — which the builder emits whether or not an `else` was
written. DESCRIBE rendered that flow as an `else`. The artifact was invisible
until the InheritanceCase writer landed: before that every branch flow degraded
to a bare NoCase, so nothing distinguished the `(empty)` flow from a real case.
Fixed in the describer: drop the `else` line when its body renders empty, using
the same elseLineIdx/truncate pattern the if/else emitters already use. Exec
re-creates the flow, so the omission is lossless — verified that describe→exec
→describe is byte-stable for an empty-branch split, an all-branches-return
split, and an authored else.
The obvious fix — not emitting the branch in the builder when no `else` is
written — was implemented first and is wrong. Without that flow the build fails
CE0089 "The '(empty)' value should be configured for an outgoing flow."
so it is load-bearing, and MDL's `else` on an inheritance split IS the `(empty)`
case. That also explains a result from the previous commit: an `else` cannot
substitute for the base entity's own case (CE0090) because `(empty)` and the
base type cover different things.
Worth recording how the wrong fix was caught: every shape was re-run through
mxbuild, not just the test suite. The unit tests passed against it — the type
splits it broke only failed at build time.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013uQvFDd5R4eNqqita59jM8
`alter page … set <prop> on <widget>` reported "widget not found" when the widget lived inside a datagrid column rendered as customContent, so the only way to touch it was CREATE OR REPLACE PAGE — a full page rewrite. findInWidgetChildren's pluggable branch searched the grid's own Object.Properties[].Value.Widgets and matched columns by their derived name, but never descended into a COLUMN's own content. Columns live at Object.Properties[columns].Value.Objects[]; a column's widgets are one level deeper, at Properties[content].Value.Widgets[]. Addressing is by the nested widget's OWN name. A `grid.column.widget` path was considered and rejected: DataGrid2 columns carry no stored name in the MPR (the existing findBsonColumn documents this), so the column segment could only ever be a derived name — the bound attribute, or the caption — which changes the moment someone edits the caption, leaving such a path silently stale. The nested widget's name is real and stable, and the grammar needs no change. A second test pins that a column still resolves by its derived name, since the new descent runs in the same loop and could otherwise shadow it. Fixes mendixlabs#834 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013uQvFDd5R4eNqqita59jM8
`alter page … set Caption = '…' on <actionbutton>` failed with "widget has no Caption property" for every action button — nested or top-level. An ActionButton has no `Caption` document. Its caption is a Forms$ClientTemplate stored under `CaptionTemplate` (Template → Items[] → Translation.Text), which is the same structure setWidgetContentMut already walked for `Content`. setWidgetCaptionMut only looked for `Caption`. Both setters now share setClientTemplateText. Found while fixing mendixlabs#834 and worth separating: the reporter hit it inside a customContent column, but it reproduces on a plain top-level button too, so it is an independent defect — and the mendixlabs#834 finder fix alone would not have made their command work. Verified end-to-end that both the nested and the top-level button now take a new caption, and that the resulting projects build with 0 errors on mxbuild 11.6.6. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013uQvFDd5R4eNqqita59jM8
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Follow-on to #115, which merged before these three commits existed. Fixes upstream #834 plus two independent defects found while verifying it.
Verified against mxbuild 11.6.6 throughout.
mendixlabs#834 — ALTER PAGE could not reach a widget in a customContent column
alter page … set <prop> on <widget>reportedwidget "X" not foundwhen the widget sat inside a datagrid column rendered ascustomContent. The only remedy wasCREATE OR REPLACE PAGE— a full page rewrite.findInWidgetChildren's pluggable branch searched the grid's ownObject.Properties[].Value.Widgetsand matched columns by derived name, but never descended into a column's own content. Columns live atObject.Properties[columns].Value.Objects[]; a column's widgets are one level deeper, atProperties[content].Value.Widgets[].Addressing is by the nested widget's own name — not the
grid.column.widgetpath the issue asks for. DataGrid2 columns carry no stored name in the MPR (the existingfindBsonColumndocuments this, and the BSON confirms it: the grid is a pluggable widget, so columns areWidgetObjectentries with noName). A column segment could therefore only ever be a derived name — the bound attribute, or the caption — which goes stale the moment someone edits the caption. The nested widget's name is real and stable, soon btnEditworks directly and the grammar needs no change.A second test pins that a column still resolves by its derived name: the new descent runs in the same loop and could otherwise shadow it.
Two defects found while verifying
set Captionfailed on every action button. With the widget found, the set still failed —widget has no Caption property. Checked whether that was specific to nesting: it wasn't. It reproduces on a plain top-level button too. AnActionButtonhas noCaptiondocument; its caption is aForms$ClientTemplateunderCaptionTemplate(Template → Items[] → Translation.Text), which is the same structuresetWidgetContentMutalready walked forContent. Both setters now share a helper.Committed separately, since it's independent of mendixlabs#834 — and the finder fix alone would not have made the reporter's command work.
describeinvented anelseon a type split. Left as a known artifact in #115; now fixed. An object-type decision always carries an(empty)outgoing flow — the null-object case — which the builder emits whether or not anelsewas written. DESCRIBE rendered it as a bareelse, so describing a split written without one produced MDL with anelse, and each describe→exec pass accumulated another.Fixed in the describer (drop the
elseline when its body renders empty, the same pattern the if/else emitters already use), not the builder. The obvious builder fix — don't emit the branch when noelseis written — was implemented first and is wrong: without that flow the build failsCE0089 "The '(empty)' value should be configured for an outgoing flow."It's load-bearing, and MDL'selseon an inheritance split is that(empty)case. That also retro-explains a result from #115:elsecan't substitute for the base entity's case (CE0090) because(empty)and the base type cover different things.Worth recording how the wrong fix was caught — every shape was re-run through mxbuild, not just the test suite. The unit tests passed against it; the type splits it broke only failed at build time.
Verification
on btnEdit(nested in customContent)Caption: 'Changed'on btnTop(top-level button)Caption: 'Changed too'describeof a split with noelseelsego test ./...green;make check-mdlandcheck-skill-mdlpass.else.main(which moved twice while this was in flight).Note for review
I deliberately did not add the 3-segment
grid.column.widgetgrammar the issue requests — reasoning above. If ambiguity ever bites (two columns with same-named children), the better answer is an ambiguity error like the onecolumnMatchCountalready produces, rather than a path built on a name that isn't stable. Worth saying so on the issue when it's closed.🤖 Generated with Claude Code
https://claude.ai/code/session_013uQvFDd5R4eNqqita59jM8
Generated by Claude Code