Skip to content

fix(widgets): stop seeding placeholder rows into object lists - #1162

Merged
ako merged 1 commit into
mendixlabs:mainfrom
MendixMau:fix/ce0463-object-list-autoseed
Sep 23, 2026
Merged

ako merged 1 commit into
mendixlabs:mainfrom
MendixMau:fix/ce0463-object-list-autoseed

Conversation

@MendixMau

Copy link
Copy Markdown
Contributor

Closes #1161

What this fixes

A page authored by mxcli containing a Barcode Scanner would not deploy: mxbuild raised CE0463 — "The definition of this widget has changed." mxcli check --references and mx check both 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 events property.

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 being Attribute / Expression / TextTemplate / Widgets / DataSource.

Barcode Scanner 2.5.0 declares:

<property key="barcodeFormats" type="object" isList="true">
  <properties><propertyGroup caption="Object list group">
    <property key="barcodeFormat" type="enumeration" defaultValue="AZTEC">

There is no required attribute, and the mpk reader defaults an absent one to required (Required: p.Required != "false"). So the property arrives required, its only nested member is an Enumeration (not in the skip list), and mxcli seeded a row carrying AZTEC. Studio Pro writes the list empty — stored instance disagrees with the installed .mpk — CE0463.

BSON oracle. Comparing mxcli bson dump --object of a stock-authored page against a patched-authored one, ignoring regenerated $ID/TypePointer blobs, gives exactly one hunk — the seeded row:

>   { "Key": "$Type", "Value": "CustomWidgets$WidgetObject" },
>   ...
>   { "Key": "PrimitiveValue", "Value": "AZTEC" }

4 CustomWidgets$WidgetObject nodes 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:

widget property declared nested types
barcodescanner barcodeFormats required (by absent attribute) {Enumeration}
htmlelement events optional {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.go already implements EnsureRequiredObjectLists() 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 TextTemplate with the widget's shipped translations — lives in buildObjectListItemBSON and is untouched. The inference "absent required attribute means required" is still correct and stays. Both guard tests still pass:

--- PASS: TestObjectListItem_RequiredTextTemplateGetsShippedDefault (0.00s)
--- PASS: TestIsUnsetRequiredTextTemplate (0.00s)

Changes

 CHANGELOG.md                                          |   8 +
 mdl/backend/widgetobj/builder.go                      | 124 +++-------
 mdl/backend/widgetobj/objectlist_no_autoseed_test.go  | 168 ++++++++++++++
 3 files changed, 208 insertions(+), 92 deletions(-)
  • EnsureRequiredObjectLists becomes a documented no-op; ensureRequiredObjectLists and createDefaultWidgetObject are deleted. The method is kept so backend.WidgetBuilder keeps its shape.
  • New regression test covering both measured properties.
  • CHANGELOG [Unreleased] → Fixed entry.

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 -v so nothing is (cached):

ok  github.com/mendixlabs/mxcli/mdl/backend/widgetobj
ok  github.com/mendixlabs/mxcli/sdk/pages
ok  github.com/mendixlabs/mxcli/mdl/executor   85.772s

3038 PASS / 0 FAIL / 1 SKIP. gofmt -l clean, go vet clean.

The new test is proven meaningful. Restoring the pre-fix builder.go from origin/main and re-running fails both subtests:

--- FAIL: TestEnsureRequiredObjectLists_LeavesUnwrittenListsEmpty
    --- FAIL: .../barcodescanner_barcodeFormats_(required,_nested_Enumeration)
        object list "barcodeFormats" was auto-populated: 2 WidgetObject nodes, want 1
    --- FAIL: .../htmlelement_events_(optional,_nested_Action/Boolean/Enumeration)
        object list "events" was auto-populated: 2 WidgetObject nodes, want 1

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:

lab binary CE0463 mxbuild
control Studio-Pro-repaired model, no mxcli write 0 BUILD SUCCEEDED
stock v0.23.0 (2026-09-21) release binary 3 BUILD FAILED (exit 3)
patched v0.23.0-46-gab8adea7 (this branch) 0 BUILD SUCCEEDED (exit 0)

And with the minimal four-statement repro from the issue, one scanner instead of three:

stock    ERROR at ReproScan, Page 'ScanPage', Barcode Scanner 'bsCode': The
         definition of this widget has changed. ...
         BUILD FAILED                                       exit=3
patched  BUILD SUCCEEDED                                    exit=0

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

  • Change was produced with AI assistance (Claude Code) and reviewed line by line by the author.
  • Root cause established from the widget .mpk XML and a BSON diff of the written model, not from the error message alone.
  • Fix verified by a matched before/after pair on the same model with two binaries, plus an unmodified control.
  • Regression test verified to fail against pre-fix code before being accepted.
  • Adjacent behaviour (ALTER PAGE cannot address widgets inside a pluggable widget, and DataGrid2 column edits are silently destructive #891 TextTemplate rule) verified unaffected by its own existing tests.
  • Full test suite run with cache disabled on the changed packages; counts reported with denominators.
  • No customer or third-party names, paths or data in the diff, commit message or this description.

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

…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>
@github-actions

Copy link
Copy Markdown

AI Code Review

Review Summary

This 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 Issues

None found.

Moderate Issues

None found.

Minor Issues

None found.

What Looks Good

  • Root cause analysis: Excellent identification of the discrepancy between mxcli's seeding behavior and Studio Pro's empty lists, backed by BSON diffs and Mendix validation.
  • Test coverage: New regression test (objectlist_no_autoseed_test.go) precisely targets the two affected properties (barcodeFormats and events) and was validated to fail with pre-fix code.
  • Documentation: Clear CHANGELOG entry explaining the fix, including Mendix validation results showing build success after the fix.
  • Atomicity: Focused change - only removes the problematic seeding logic without touching unrelated functionality (ALTER PAGE cannot address widgets inside a pluggable widget, and DataGrid2 column edits are silently destructive #891 remains intact).
  • Backend compliance: Maintains the EnsureRequiredObjectLists method signature (no-op) to preserve interface shape while aligning CLI and MCP write paths.
  • Verification: Comprehensive testing including local build/test/lint, package-specific test runs, and real Mendix 11.14.0 validation with Barcode Scanner 2.5.0.

Recommendation

Approve 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 ako left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

  1. No finding recorded in .claude/skills/fix-issue/findings/<area>.jsonl. This one is a textbook entry — "CE0463 on a widget mxcli authored, mxcli check and mx check both 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.
  2. 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.

@ako
ako merged commit d34c680 into mendixlabs:main Sep 23, 2026
5 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.

CE0463 on every mxcli-authored Barcode Scanner: an object list the author never wrote is seeded with a placeholder row

2 participants