Skip to content

fix(docker): preserve MPRv2 storage format across docker check#764

Merged
ako merged 1 commit into
mendixlabs:mainfrom
dimenus:fix/763-preserve-mprv2-storage-format
Jul 19, 2026
Merged

fix(docker): preserve MPRv2 storage format across docker check#764
ako merged 1 commit into
mendixlabs:mainfrom
dimenus:fix/763-preserve-mprv2-storage-format

Conversation

@dimenus

@dimenus dimenus commented Jul 17, 2026

Copy link
Copy Markdown

Closes #763

What does it do?

mxcli docker check runs mx update-widgets before mx check to prevent false CE0463 ("widget definition changed") errors. On an MPRv2 project, that step rewrites the project into the self-contained MPRv1 format: it inlines every unit into the .mpr and deletes the mprcontents/ directory. A command named check should not change the on-disk storage format.

On a project under Git this is damaging. After the conversion the working tree no longer matches the mprcontents/*.mxunit files tracked in the repository. If the project is then opened in Studio Pro (which auto-converts back to MPRv2) or the state is committed, the history and the on-disk format drift apart. In the case that motivated this, Studio Pro then failed to open the project, crashing in its Git version-control provider.

The fix detects the storage format before running update-widgets. When the project is MPRv2, it snapshots the .mpr and mprcontents/ to a temp directory and restores them after the check. The check still runs against the widget-normalized model, so CE0463 false positives stay suppressed; only the on-disk format is preserved. MPRv1 projects are single-file and left untouched. If the snapshot cannot be taken, update-widgets is skipped rather than risk an unrecoverable conversion.

Testing

  • go build ./cmd/mxcli/... — passes
  • go vet ./cmd/mxcli/docker/... — passes
  • go test ./cmd/mxcli/docker/... — unit tests pass, including the new TestSnapshotStorageFormat_RestoresV2AfterConversion. Mutation-checked: the test fails when the restore is disabled.
  • Integration test TestCheck_PreservesMPRv2StorageFormat (build tag integration): scaffolds a real MPRv2 project with mx create-project, runs the real Check(), and asserts the project is still MPRv2. Verified green in a Linux container with the same mx version the CI integration job installs (11.9.0). Mutation-checked: it fails (project ends MPRv1) when the fix is disabled.
  • Root cause confirmed on both mx 11.9.0 and 11.12.1: mx update-widgets performs the v2 to v1 conversion.

Mendix validation

Verified against a real MPRv2 project (Mendix 11.12.1): before the fix, mxcli docker check left the project MPRv1 with mprcontents/ deleted; after the fix, the project stays byte-for-byte MPRv2 and git reports no model changes.

Agentic Code Testing

N/A — this changes the behavior of docker check, not an MDL command surface.

🤖 Generated with Claude Code

`mxcli docker check` runs `mx update-widgets` before `mx check` to avoid false
CE0463 ("widget definition changed") errors. On an MPRv2 project that step
rewrites the project into the self-contained MPRv1 format: it inlines every unit
into the .mpr and deletes the mprcontents/ directory. A command named `check`
should not change the on-disk storage format. On a project under Git this
desyncs the working tree from the tracked mprcontents/ files and can leave
Studio Pro unable to open the project (it crashed in its Git version-control
provider in the case that motivated this).

## Changes

- Detect the storage format before running update-widgets. When the project is
  MPRv2, snapshot the .mpr and mprcontents/ to a temp directory and restore them
  after the check. The check still runs against the widget-normalized model, so
  CE0463 false positives stay suppressed; only the on-disk format is preserved.
  MPRv1 projects are single-file and left untouched.
- If the snapshot cannot be taken, skip update-widgets rather than risk an
  unrecoverable v2 to v1 conversion.

## Tests

- Unit regression test for the snapshot/restore round-trip (deletes mprcontents/
  to mimic the conversion, asserts a byte-for-byte restore).
- Integration test (build tag `integration`) that scaffolds a real MPRv2 project
  with `mx create-project`, runs the real `Check()`, and asserts the project is
  still MPRv2 afterwards.

## Benefits

- `docker check` no longer mutates the storage format of the project it checks.
- Removes the Git working-tree desync and the Studio Pro open failure that
  followed from it.

## Statistics

- 3 files changed: 1 fix, 2 tests.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This was referenced Jul 18, 2026
@ako
ako merged commit 88077af into mendixlabs:main Jul 19, 2026
1 check passed
ako pushed a commit to ako/mxcli that referenced this pull request Jul 19, 2026
CE0463 remediation in the synced skills told agents to run `mx update-widgets`
directly. On an MPRv2 project (an `mprcontents/`-folder project — everything
`mxcli new` creates) that command silently rewrites the project to single-file
v1 and deletes `mprcontents/`, corrupting a git working tree, breaking a running
`mxcli run --local` loop, and sometimes leaving Studio Pro unable to open the
project. This actually bit a warm-loop app-generation run (CE0463 on Image
widgets → agent ran `mx update-widgets` per the skill).

With PR mendixlabs#764 (issue mendixlabs#763) merged, `mxcli docker check`/`build` snapshot and
restore the v2 storage around the widget normalization, so they are the safe
remediation. Point every CE0463/update-widgets mention at those commands and
warn that bare `mx update-widgets` is only safe on a v1 project or a throwaway
diagnostic copy.

- create-page.md, custom-widgets.md, migrate-design-prototype.md,
  download-marketplace-content.md (user-synced): steer to docker check/build,
  warn about the v2->v1 deletion.
- debug-bson.md (dev): fix the "safety net"/"quick workaround" framing; add a
  "use a COPY" caution to the reference-extraction blocks.
- fix-issue.md: add a symptom-table row for the mprcontents/ data-loss class
  (issue mendixlabs#763 / PR mendixlabs#764).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JXnEgoc2NQP1Y2TWMCMXC4
ako pushed a commit to ako/mxcli that referenced this pull request Jul 20, 2026
…lope is mxbuild-only

End-to-end reproduction of the mendixlabs#600 stack (DW 3.10.0 over bundled 3.4.0 in an
11.12.0 project, DataGrid2 authored with mxcli, isolated from the Atlas template
pages) with an order-independent, ID-masked before/after-update-widgets subtree
diff proves the parser/template/augment approach is structurally capped below
large version jumps:

- Nested-column PropertyKey sets are already identical before/after (augment adds
  every new 3.10.0 key correctly).
- Residual drift is dominated by fields ABSENT from the widget XML source:
  AllowUpload (x105), DesignProperties, LabelTemplate, and a mxbuild-COMPUTED
  Required (XML declares 3 required="true"; mxbuild emits 54).
- update-widgets on the same copy takes the grid CE0463 -> 0.

Since these fields are not derivable from the .mpk, no augment (including the
proposed full-ValueType path a) can reproduce a version-faithful WidgetType. A
speculative reconcileValueTypeSchema was implemented and reverted (setting
Required from the XML attr would mis-set the 51 computed keys to false).

Revised path forward: delegate the envelope to mxbuild's own update-widgets made
v2-safe (adopt mendixlabs PR mendixlabs#764), keep the moderate-drift augment as-is. Records
the rejected approach inline in augment.go and updates the proposal Open Question #1.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JXnEgoc2NQP1Y2TWMCMXC4
ako pushed a commit to ako/mxcli that referenced this pull request Jul 20, 2026
…jected, editor-logic boundary

Corrects the earlier (wrong) "only mxbuild can produce the envelope" conclusion:
the WidgetType IS generically reproducible and is now byte-identical to
update-widgets output (committed c7fe714). Records the instance-side investigation:

- The residual CE0463 (definition matching) is textTemplate default-template
  instantiation in the WidgetObject. Even a minimal DataGrid2 reproduces it.
- Which textTemplates get a default template is config-conditional applicability
  (aria labels always; clearSelectionButtonLabel/loadMoreButtonCaption/
  singleSelectionColumnLabel only when their feature is active) — a distinction
  mxbuild derives from the widget's editor code, NOT the declarative XML (the
  null-vs-populated property defs are structurally identical). always-populate
  closes 9->3, Required-gate 3->9; neither matches because the rule isn't declarative.
- The Object-from-definition rebuild was tried and REJECTED: it discards the
  byte-exact extracted template Object and regressed bundled DataGrid2 0->1 on
  both 11.12.0 and 10.24.

Path forward for the instance last-mile: v2-safe update-widgets (mendixlabs#764) on top of
mxcli's now-correct definition — the applicability logic lives in the widget's
compiled editor code, which mxbuild executes and mxcli cannot.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JXnEgoc2NQP1Y2TWMCMXC4
ako pushed a commit to ako/mxcli that referenced this pull request Jul 20, 2026
…nce-default mechanism

Upgrades the instance last-mile finding from inference to confirmed, with the
concrete evidence: the widget .mpk ships Datagrid.editorConfig.js whose
getProperties() calls hidePropertyIn/hidePropertiesIn conditionally on the
instance's values. The verbatim hides for the three always-null properties are
present ("Multi"!==selection hides clearSelectionButtonLabel; "loadMore"!==
pagination hides loadMoreButtonCaption; singleSelectionColumnLabel conditional),
mapping 1:1 onto the measured null-vs-populated pattern. Always-populated props
(selectRowLabel, cancelExportLabel) appear 0x in editorConfig.js. A hidden
property does not get its default template instantiated — so the applicability
rule is imperative JS keyed on instance config, not declarative XML.

Adds path (d): execute editorConfig.js in-process via a Go JS engine (goja) as a
research spike for a no-mxbuild-dependency path; kept deferred behind the
recommended (c) v2-safe update-widgets (mendixlabs#764).

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.

mxcli docker check silently converts MPRv2 projects to MPRv1 (via mx update-widgets), deleting mprcontents

3 participants