fix(widgets): stop seeding placeholder rows into object lists - #1162
Conversation
…endixlabs#1161) EnsureRequiredObjectLists auto-populated any object list whose nested properties were all "simple" (nothing Attribute/Expression/TextTemplate/ Widgets/DataSource). Studio Pro leaves such a list EMPTY, so the seeded row made the stored instance disagree with the installed .mpk and mxbuild raised CE0463 "the definition of this widget has changed". mxcli check and mx check both reported zero errors on the same model. Measured across the shipped widget templates the heuristic fired on exactly two properties and helped neither: barcodescanner barcodeFormats required, nested {Enumeration} htmlelement events optional, nested {Action,Boolean,Enumeration} barcodeFormats is declared with no `required` attribute at all; the widget XML schema defaults an absent one to true (mpk.go: `Required: p.Required != "false"`), so it arrives marked required and the nested Enumeration does not match the complex-type skip. It seeded one row carrying the enum default AZTEC. Three Barcode Scanners across two pages each raised CE0463; the app would not deploy until Studio Pro's "Update widget" deleted exactly that row. The MCP write path has always had this method as a no-op, so removing the seeding also makes the two engines agree rather than leaving the model dependent on which one authored it. This does NOT touch the sibling fix for mendixlabs#891: filling an *authored* object-list item's required TextTemplate with the widget's shipped translations lives in buildObjectListItemBSON and still applies. An absent `required` attribute in widget XML still means required. Verified on a real app: same MDL script, same model, same mxbuild 11.14.0. before 3x CE0463, BUILD FAILED after 0x CE0463, BUILD SUCCEEDED, no Studio Pro intervention Re-exec is idempotent, and a DataGrid2/Combobox script builds identical error sets before and after. The new test drives Builder.EnsureRequiredObjectLists (the call site used by mdl/backend/mutation.go and mdl/executor/widget_engine.go) and covers both measured properties. It fails against the pre-fix code on both subtests. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
AI Code ReviewReview SummaryThis PR fixes a CE0463 widget deployment error by removing incorrect placeholder seeding in object lists. The changes are well-justified, thoroughly tested, and follow project conventions. Critical IssuesNone found. Moderate IssuesNone found. Minor IssuesNone found. What Looks Good
RecommendationApprove the PR. The fix is correct, minimal, and fully addresses the reported CE0463 issue without introducing regressions. All validation evidence supports the change. Automated review via OpenRouter (Nemotron Super 120B) — workflow source |
ako
left a comment
There was a problem hiding this comment.
Reviewed this by re-measuring the claims rather than reading the diff — the diagnosis holds, the fix looks right, and the reasoning for removal over narrowing is sound. Details below, including one claim I verified independently, two checklist items missing, and one link in the argument that is asserted rather than shown.
The load-bearing claim checks out
"Measured across the 31 shipped widget templates, the seeding fired on exactly two properties" is what justifies deleting the heuristic instead of narrowing it, so I measured it rather than taking it on trust: ran the old ensureRequiredObjectLists over all 31 real templates on main and counted CustomWidgets$WidgetObject nodes before and after.
28 templates unchanged (1 -> 1)
1 template unchanged (4 -> 4)
2 templates GREW:
com.mendix.widget.web.barcodescanner.BarcodeScanner barcodeFormats(required=true {barcodeFormat:Enumeration})
com.mendix.widget.web.htmlelement.HTMLElement events(required=false {eventAction:Action, eventName:Enumeration, ...})
Exactly the two named, and htmlelement.attributes is correctly skipped for its nested Expression/TextTemplate. So removal regresses nothing among the shipped set, and "zero beneficiaries, two victims" is accurate.
One caveat on my numbers: I called the internal function against raw template documents, so my row deltas are not comparable to your BSON oracle on an authored page (4 -> 3). Different starting document; the count to trust is yours. What mine establishes is which templates are reached.
The new test actually detects the bug
Ran objectlist_no_autoseed_test.go against unfixed main — it fails on both cases with the intended symptom:
--- FAIL: TestEnsureRequiredObjectLists_LeavesUnwrittenListsEmpty
object list "barcodeFormats" was auto-populated: 2 WidgetObject nodes, want 1
object list "events" was auto-populated: 2 WidgetObject nodes, want 1
Worth stating because grep finds no existing test that ever covered the old seeding — which is how it survived. Going through Builder.EnsureRequiredObjectLists rather than the helper is the right call: reinstating the seeding fails the test.
Also confirmed: mcp.(*mcpWidgetBuilder).EnsureRequiredObjectLists is already a no-op (mdl/backend/mcp/widget.go:551), so the engines really do agree afterwards; both #891 guard tests pass; make test exit 0 with zero failures; nothing orphaned by the deletion.
Missing from the PR checklist
- No finding recorded in
.claude/skills/fix-issue/findings/<area>.jsonl. This one is a textbook entry — "CE0463 on a widget mxcli authored,mxcli checkandmx checkboth clean, cause was a seeded object-list row" is exactly the symptom → fix recipe the store exists for, and the next CE0463 report is the one it would save. - No
mdl-examples/bug-tests/script, which the checklist asks for on every bug fix so the result can be confirmed in Studio Pro.
The one link that is asserted rather than shown
The unit test proves the builder no longer seeds. That an empty required object list then builds rests on the inference that Studio Pro's "Update widget" — which empties the list — is what clears CE0463. Persuasive, but there is no mxbuild run in the PR against a page authored by the fixed binary.
That is the step that would close it: author the reporter's page with this build, then mxcli docker check -p app.mpr --no-update-widgets (the plain form runs mx update-widgets first and reports 0 errors on a project that genuinely has a CE0463 — per .claude/skills/diagnose-ce0463.md). I could not run it here: it needs Barcode Scanner 2.5.0 installed plus mxbuild 11.14.0, and neither is available in this environment.
Since you have the reproducing project, that is a cheap addition and it would turn the strongest claim in the PR from an inference into a measurement.
Closes #1161
What this fixes
A page authored by mxcli containing a Barcode Scanner would not deploy:
mxbuildraised CE0463 — "The definition of this widget has changed."mxcli check --referencesandmx checkboth reported zero errors on the same model, so nothing in the mxcli workflow caught it. The only way out was to open Studio Pro and run Update widget.The same defect affects the HTML element widget's
eventsproperty.Root cause
Builder.EnsureRequiredObjectLists(mdl/backend/widgetobj/builder.go) seeded one placeholder row into any object-list property whose nested properties were all "simple" — the skip list beingAttribute / Expression / TextTemplate / Widgets / DataSource.Barcode Scanner 2.5.0 declares:
There is no
requiredattribute, and the mpk reader defaults an absent one to required (Required: p.Required != "false"). So the property arrives required, its only nested member is anEnumeration(not in the skip list), and mxcli seeded a row carryingAZTEC. Studio Pro writes the list empty — stored instance disagrees with the installed.mpk— CE0463.BSON oracle. Comparing
mxcli bson dump --objectof a stock-authored page against a patched-authored one, ignoring regenerated$ID/TypePointerblobs, gives exactly one hunk — the seeded row:4
CustomWidgets$WidgetObjectnodes before, 3 after. Nothing else in the page changes.Why removal rather than narrowing the heuristic
Measured across the 31 shipped widget templates, the seeding fired on exactly two properties, and Studio Pro leaves both of them empty:
barcodescannerbarcodeFormats{Enumeration}htmlelementevents{Action, Boolean, Enumeration}Zero beneficiaries, two victims. A narrowed predicate would be a special case with no remaining positive case to serve.
Additionally,
mdl/backend/mcp/widget.goalready implementsEnsureRequiredObjectLists()as a no-op. The CLI and MCP write paths disagreed, so the model depended on which engine authored it. Removal makes them agree.This does NOT touch #891
#891's fix — filling an authored object-list item's required
TextTemplatewith the widget's shipped translations — lives inbuildObjectListItemBSONand is untouched. The inference "absentrequiredattribute means required" is still correct and stays. Both guard tests still pass:Changes
EnsureRequiredObjectListsbecomes a documented no-op;ensureRequiredObjectListsandcreateDefaultWidgetObjectare deleted. The method is kept sobackend.WidgetBuilderkeeps its shape.[Unreleased] → Fixedentry.Testing confirmation
make build/make test/make lint— all run.Full suite: 43 packages ok / 0 FAIL.
The three affected packages re-run with
-count=1 -vso nothing is(cached):→ 3038 PASS / 0 FAIL / 1 SKIP.
gofmt -lclean,go vetclean.The new test is proven meaningful. Restoring the pre-fix
builder.gofromorigin/mainand re-running fails both subtests:Mendix validation
Validated against a real app, Mendix 11.14.0, Barcode Scanner 2.5.0, mxbuild 11.14.0 — same MDL script, same starting model, two identical lab copies:
v0.23.0 (2026-09-21)release binaryv0.23.0-46-gab8adea7(this branch)And with the minimal four-statement repro from the issue, one scanner instead of three:
Studio Pro opens the patched model without offering "Update widget" on the scanner.
Re-exec is idempotent. A DataGrid2 / Combobox script produces identical error sets before and after, so no adjacent widget regressed.
Agentic Code Testing
.mpkXML and a BSON diff of the written model, not from the error message alone.Limits of the evidence
The two-property surface is measured against the shipped widget templates. A third-party widget with a required object list whose nested properties are all simple will also stop being seeded. I believe that is correct — Studio Pro would leave it empty too, which is the whole point — but I cannot prove no downstream user depends on the old behaviour.
🤖 Generated with Claude Code