| On **Mendix 11.12**, `mx check` fails to load projects on the **legacy** engine for several doctypes (`01-domain-model`, `10-odata`, `14-project-settings`, `22-published-rest`) with `System.InvalidOperationException: Expected '$ID' as the first property of a storage object, but got 'X'` at `StreamingBsonUnitReader`. The bad first-key varies run-to-run (`ThemeModuleOrder`, `EnableRspackBundler`, `Oql`, `MarkAsUsed`, `PopupResizable`…) — the tell-tale of random map-key order. modelsdk passes; ≤ 11.11 tolerates any order | Several legacy `sdk/mpr` writers preserve round-trip fidelity by carrying parsed subtrees as Go maps (`ProjectSettings.RawParts`, ref-marking `raw`, rename `raw`, domain-model `raw`) and marshalling them back. `bson.Marshal` emits **map keys in random order**, so `$ID` only lands first by luck. 11.12 rejects any storage object whose first key isn't `$ID`. The earlier `$ID`-first fix (bson.D reorder in writer_domainmodel/security) missed every map-passthrough site | `sdk/mpr/writer_settings.go` (+ `writer_refs`, `writer_rename`, `writer_domainmodel`, `writer_odata`, `writer_rest`, `writer_customblob`, `writer_security`, `writer_modules`, `writer_microflow`, … — all unit-boundary marshals) via new `sdk/mpr/writer_order.go` | Wrap every unit-boundary `bson.Marshal(doc)` in `marshalUnitIDFirst(doc)` = `bson.Marshal(bsonutil.HoistStorageID(doc))`. **Use the non-sorting `HoistStorageID`, NOT `OrderStorageValue`**: `OrderStorageValue` *sorts* the non-`$ID` keys, which corrupts template-derived pluggable-widget/datagrid page trees (mx then aborts with `got 'LabelTemplate'`, regressing `29-datagrid`/`33-alter-page`). `HoistStorageID` only lifts `$ID`/`$Type` to the front and preserves every other key's order → no-op on already-correct writers, fixes only the map-passthrough ones. **Do NOT normalize `writer_pages.go` or the page/workflow mutators** — their output is already `$ID`-first and they embed delicate widget maps the hoist would sort-corrupt. Verify on 11.12 with the doctype gate; regression-check `29`/`33` specifically. (Left unfixed: `03-page` LabelTemplate — a separate pre-existing issue where on-disk bytes read `$ID`-first yet mx still objects) |
0 commit comments