Skip to content

Fix compound (nested) design properties silently dropped on write - #21

Merged
ako merged 1 commit into
mainfrom
fix/nested-designproperties
Jul 24, 2026
Merged

Fix compound (nested) design properties silently dropped on write#21
ako merged 1 commit into
mainfrom
fix/nested-designproperties

Conversation

@ako

@ako ako commented Jul 24, 2026

Copy link
Copy Markdown
Owner

Problem

An inline compound design property on a widget — e.g. the Atlas Spacing group:

container card1 (designproperties: ['Card style': on, 'Spacing': ['margin-bottom': 'Large', 'margin-top': 'Medium']])

— was silently dropped by the legacy builder write path. mxcli check, exec, and even mx check all passed, but the nested styling vanished from the .mpr; only the flat toggle/option props survived. This also affects any block copied in via use building block, since those carry compound Atlas design properties.

Root cause

serializeDesignProperties (sdk/mpr/writer_widgets.go) switched on p.ValueType with cases for toggle/option/custom and a default: continue that dropped the compound kind entirely. A compound value nests its sub-properties in a Forms$CompoundDesignPropertyValue whose Properties list has the same marker-prefixed Forms$DesignPropertyValue shape as the outer array — but nothing serialized it, so the whole entry (key included) never reached BSON.

The AST (DesignPropertyEntryV3.Nested), visitor (buildDesignPropertyEntryV3), and builder (astDesignPropToValue → ValueType "compound") already produced the nested model correctly. Only the serializer's terminal switch dropped it. This is the CREATE write counterpart to the describe read-back fix in mendixlabs#668.

Fix

Add a case "compound" that emits Forms$CompoundDesignPropertyValue and recurses via serializeDesignProperties(p.Compound).

Validation

  • mxcli docker check = 0 errors on a real Mendix 11.12.1 project (authoritative Studio Pro validation)
  • write → describe round-trip preserves Spacing on both the legacy and modelsdk engines
  • unit test TestSerializeDesignProperties_Compound (sdk/mpr/writer_widgets_test.go)
  • bug-test mdl-examples/bug-tests/compound-designproperties.mdl
  • symptom-table row added to .claude/skills/fix-issue.md

🤖 Generated with Claude Code


Generated by Claude Code

An inline compound design property on a widget — e.g. the Atlas Spacing
group `designproperties: ['Spacing': ['margin-bottom': 'Large', ...]]`,
and any block copied in via `use building block` — was silently dropped
by the legacy builder write path. `check`, `exec`, and even `mx check`
all passed, but the nested styling vanished from the .mpr; only flat
toggle/option props survived.

Root cause: serializeDesignProperties switched on p.ValueType with cases
for toggle/option/custom and a `default: continue` that dropped the
`compound` kind entirely. A compound value nests its sub-properties in a
Forms$CompoundDesignPropertyValue whose Properties list has the same
marker-prefixed Forms$DesignPropertyValue shape as the outer array.

Add a `case "compound"` that emits Forms$CompoundDesignPropertyValue and
recurses via serializeDesignProperties(p.Compound). The AST, visitor, and
builder already produced the nested model — only the serializer's
terminal switch dropped it. This is the CREATE write counterpart to the
describe read-back fix in mendixlabs#668.

Verified: mxcli docker check = 0 errors on 11.12.1 (authoritative), plus
a write->describe round-trip preserves Spacing on both engines.

Test TestSerializeDesignProperties_Compound; bug-test
mdl-examples/bug-tests/compound-designproperties.mdl; symptom table row
added to fix-issue.md.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JXnEgoc2NQP1Y2TWMCMXC4
@ako
ako merged commit 26697aa into main Jul 24, 2026
3 checks passed
ako pushed a commit that referenced this pull request Jul 28, 2026
…otchas

Three check-time hints for constructs that pass `mxcli check` but surprise at
build or render time (ledger findings #21/#25/#27):

- MDL046: dateTime()/dateTimeUTC() with a non-literal argument (CE0117 — these
  build from hardcoded constants only). Hint: step off a literal anchor with
  addDays()/addMonths(), which take variables.
- MDL047: an association compared to `empty` in a retrieve constraint
  (`[Module.Assoc = empty]` → CE0161 — `= empty` tests attributes, not
  associations). Hint: `[not(Assoc/Target)]`. A bare attribute and an
  attribute-over-association test are correctly not flagged.
- MDL-WIDGET15 (info): two or more adjacent dynamictext siblings, which Mendix
  renders inline (concatenated, no separator) regardless of RenderMode. Info
  severity — advisory only, never fails the build.

Finding #20 (division operand typing) is covered by the existing MDL041
(div→Decimal) and the new MDL045 (`/`-as-division) rather than a separate rule
that would contradict CLAUDE.md's documented "integer div yields Decimal".

Tests: TestValidateMicroflow_DateTimeLiterals, _XPathAssociationEmpty,
TestValidateConsecutiveDynamicText. Repros in mdl-examples/bug-tests/. Skills +
symptom table updated.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JXnEgoc2NQP1Y2TWMCMXC4
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