Skip to content

fix(pages): write a null TitleOverride, unblanking every popup caption (#812) - #68

Merged
ako merged 2 commits into
mainfrom
claude/showpage-titleoverride-812
Aug 1, 2026
Merged

fix(pages): write a null TitleOverride, unblanking every popup caption (#812)#68
ako merged 2 commits into
mainfrom
claude/showpage-titleoverride-812

Conversation

@ako

@ako ako commented Aug 1, 2026

Copy link
Copy Markdown
Owner

Fixes mendixlabs#812.

The bug

Every Forms$FormSettings / Forms$PageSettings mxcli wrote carried an empty Microflows$TextTemplate as its TitleOverride, where Studio Pro writes null.

An empty template is not the absence of an override — it is an override, to the empty string. So every popup opened by an mxcli-authored button or Show Page action rendered with a blank caption and just the ×. The reporter found 10 broken popups (all mxcli-authored) against 58 correct ones from Studio Pro / marketplace modules in the same project.

Reproduced and verified in a browser

A scratch Mendix 11.12.2 project, a popup page titled Order Details Popup, and an mxcli-authored button that opens it. Same app, same page, booted via mxcli run --local; the only variable is which build wrote the page. Driven with Playwright, reading the caption out of the modal header:

Build TitleOverride in BSON headerText caption
this PR null "×\nOrder Details Popup" "Order Details Popup"
pre-fix empty Microflows$TextTemplate "×" "" — blank

That is the reported symptom reproduced exactly, and closed.

The build-level claim behind the original workaround is also disproved. The old comments said Studio Pro rejects a null embedded object on load. With the null in place, on 11.12.2:

mxcli docker check -p app.mpr   →  "The app contains: 0 errors."
mxcli docker build -p app.mpr   →  "Build complete."

A second defect the first one hid

The same unconditional write discarded an override the author did ask for. show page M.P with title = 'X' parses, the visitor sets ast.ShowPageStmt.Title, the builder sets ShowPageAction.OverridePageTitle — and no writer ever read that field. Before this change, grep -rn OverridePageTitle matched exactly two lines: the struct field and its assignment.

Both cases produced identical BSON, which is precisely why nobody noticed the second half. Both now round-trip.

Two traps, both now in the symptom table

1. A comment citing the wrong issue. The must be non-nil comments justified themselves with issue mendixlabs#295, which was about Forms$PageVariable on a PageParameterMapping — a different field. The conclusion was generalised to TitleOverride without being tested, and the runs above show it is false. This repo's own .claude/skills/debug-bson.md already documented {Key: "TitleOverride", Value: nil} as the correct shape.

Two existing tests asserted the incorrect behaviour. They are inverted here, with the reasoning recorded inline so the reversal is not silently re-reverted by someone reading the old comment.

2. codec.RegisterTypeDefaults overwrites rather than merges. Forms$FormSettings was registered twice — in microflow_write.go and widget_write.go. Registrations resolve by init order, so the NullFields entry I added beside the microflow writer was clobbered and the null never reached the output. Caught only by dumping the BSON rather than trusting the passing test. Consolidated to a single registration, with a comment at the surviving site.

Scope

The microflow Show Page and widget-action paths, where the evidence is. Navigation writes the same empty template at three sites and is deliberately left alone — the reported evidence is about popups, and changing navigation on an analogy is exactly the mistake that caused this bug in the first place.

Unit-level coverage

show page P;                        → TitleOverride: null
show page P with title = 'X';       → Microflows$TextTemplate containing 'X'
actionbutton (action: show_page P)  → TitleOverride: null

Reverting the fix makes the new tests fail with exactly the BSON shape quoted in the issue. Repro script added at mdl-examples/bug-tests/812-showpage-title-override.mdl. Full Go suite green.

🤖 Generated with Claude Code

https://claude.ai/code/session_012XR649rKk68z6gBpngu6MA

mendixlabs#812)

Every Forms$FormSettings / Forms$PageSettings mxcli wrote carried an empty
Microflows$TextTemplate as its TitleOverride, where Studio Pro writes null. An
empty template is not the absence of an override — it IS an override, to the
empty string — so every popup opened by an mxcli-authored button or Show Page
action rendered with a blank caption and just the close button. The reporter
found 10 broken popups (all mxcli-authored) against 58 correct ones in the same
project.

The same unconditional write hid a second defect: an override the author did ask
for, `show page M.P with title = 'X'`, was discarded. The syntax parses, the
visitor sets ast.ShowPageStmt.Title and the builder sets
ShowPageAction.OverridePageTitle — but no writer ever read that field. Before
this change `grep -rn OverridePageTitle` matched exactly two lines: the struct
field and its assignment. Both halves now round-trip.

Two traps made this hard to see, and both are recorded in the symptom table.

The "must be non-nil" comments cited issue mendixlabs#295 as justification. mendixlabs#295 was about
Forms$PageVariable on a PageParameterMapping — a different field — and the
conclusion was generalised to TitleOverride without ever being tested. It is
false: with the null in place, against Mendix 11.6.6,

    mxcli docker check  ->  "The app contains: 0 errors."
    mxcli docker build  ->  "Build complete."

so the loader accepts it, which is precisely what the old comments said it would
not. This repo's own .claude/skills/debug-bson.md already documented
`{Key: "TitleOverride", Value: nil}` as the correct Forms$FormSettings shape.
Two tests asserted the incorrect behaviour and have been inverted, with the
reasoning recorded so the reversal is not silently re-reverted.

codec.RegisterTypeDefaults OVERWRITES rather than merges, and Forms$FormSettings
was registered twice — in microflow_write.go and widget_write.go. Registrations
resolve by init order, so the NullFields entry added beside the microflow writer
was clobbered and the null never reached the output. Consolidated to a single
registration, with a comment at the surviving site.

Scope: the microflow Show Page and widget-action paths, where the evidence is.
Navigation writes the same empty template but the reported evidence is about
popups, so it is left alone rather than changed on a guess.

Verified end-to-end against a real project, not just in unit tests:

  show page P;                        -> TitleOverride: null
  show page P with title = 'X';       -> Microflows$TextTemplate containing 'X'
  actionbutton (action: show_page P)  -> TitleOverride: null

plus the mx check and MxBuild runs above. Reverting the fix makes the new tests
fail with exactly the BSON shape quoted in the issue. Repro script in
mdl-examples/bug-tests/.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012XR649rKk68z6gBpngu6MA
@ako
ako force-pushed the claude/showpage-titleoverride-812 branch from a26130b to 361c53e Compare August 1, 2026 03:13
Resolves the recurring conflict in .claude/skills/fix-issue.md: every fix appends
a symptom row at the same anchor, so any two branches that both add one collide.
Both sides kept — this branch's mendixlabs#812 row plus the two loop rows that arrived from
#63.

Main has since gained #64 (runUpdateWidgets) and #66 (mandatory microflow
semicolons). Parser regenerated against the new grammar; full suite green, and
the example corpus is unchanged at 235/39 including this branch's repro script.
ako pushed a commit that referenced this pull request Aug 1, 2026
Same recurring conflict as #68: .claude/skills/fix-issue.md, where every fix
appends a symptom row at the identical anchor. Both sides kept.

check.go auto-merged cleanly — #64's runUpdateWidgets and this branch's
localMxForVersion touch different functions. Parser regenerated against #66's
grammar; full suite green.
@ako
ako merged commit 73301a8 into main Aug 1, 2026
3 checks passed
ako pushed a commit that referenced this pull request Aug 1, 2026
Every bug fix touches .claude/skills/fix-issue.md, and new rows went in directly
under the table header. Two branches fixing unrelated bugs therefore inserted at
the same line, which git cannot merge — a conflict by construction rather than by
bad luck. It cost five separate resolution rounds in one week (#59-#62 three
times, then #68 and #69), and each round risks silently dropping a row: one
earlier round was "resolved" by deleting a PR's rows entirely and restoring them
in a follow-up.

Appending puts each branch's insert at a different offset, which git merges
without help. The table is unordered — it is looked up by matching a symptom, not
read top to bottom — so position carries no meaning and appending costs nothing.

Recorded in both places a contributor might look: the skill's How to Use, with
the reasoning so it is not "fixed" back, and the PR checklist item in CLAUDE.md.

Existing rows are deliberately left where they are. Reordering them would
conflict with every open branch at once, which is the problem this change exists
to avoid.

Folded into this branch rather than opened separately: it edits the same How to
Use block, so a sixth branch would have manufactured exactly the conflict it is
meant to prevent.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012XR649rKk68z6gBpngu6MA
ako pushed a commit that referenced this pull request Aug 1, 2026
#68 landed, putting its symptom row at the top of the table — the same line this
branch inserts at, so the conflict recurred immediately after the previous
resolution. That is the pattern, not bad luck.

Resolved by keeping main's rows and moving this branch's row to the END of the
table, which is the convention #70 introduces. Applying it here rather than
waiting: this branch is the exact case it exists for.
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.

Show Page actions get an empty TitleOverride instead of null, blanking every popup's caption

2 participants