Skip to content

fix(mpr): refuse file writes while Studio Pro has the project open (mendixlabs/mxcli#849) - #915

Merged
ako merged 5 commits into
mainfrom
fix/ml849-studio-pro-open-guard
Oct 1, 2026
Merged

ako merged 5 commits into
mainfrom
fix/ml849-studio-pro-open-guard

Conversation

@ako

@ako ako commented Oct 1, 2026

Copy link
Copy Markdown
Owner

Fixes mendixlabs#849

What

Studio Pro keeps the model in memory and does not reload it when another process changes the files. A file-based mxcli write while the project is open succeeds, mx check passes, and Studio Pro's next save silently discards it. Nothing in mxcli checked for this; the rule lived only in prose.

Fix

modelsdk/mpr/studiopro_lock.go: Writer.guardWrite refuses, with *StudioProOpenError, every write that would actually reach storage while Studio Pro's lock file is beside the .mpr:

  • updateUnit and WriteTransaction.WriteUnit — after no-op elision, so re-running an already-applied script with Studio Pro open stays a no-op rather than an error (twice-exec rule);
  • insertUnit, deleteUnit, MoveUnit (after its own elision), UpdateUnitContainer.

Guarding at the storage layer rather than per command covers every command that writes the model (exec, -c, REPL, alter, the fluent API), and leaves every read (show, describe, check, lint, catalog) untouched. The MCP backend (--mcp) connects read-only and writes through Studio Pro, so it is unaffected — the message points to it.

Override, opt-in only: mxcli exec --force (prints a warning naming the lock), or MXCLI_ALLOW_STUDIO_PRO_OPEN=1 for commands without a --force. The error names the lock path and its mtime and says what to do, including the crash-left-a-stale-lock case.

The signal, and what was measured

  • Lock file name. Studio Pro's own generated .gitignore in ako/TestApp (Studio Pro-authored) lists TestApp/testapp.mpr.lock beside TestApp/TestApp.mpr — Studio Pro lower-cases the project name, so the match is case-insensitive on <mpr basename>.lock. The same file lists mprcontents/mprjournal*; that was not used as a signal (its lifetime is not known to be tied to the project being open).
  • Not measured live: the running Studio Pro has TestApp open on the Windows host; its project directory is not reachable from the devcontainer, and the built-in MCP server's file tools only expose theme/jsactions/version-control roots, so the lock's presence while open could not be observed directly. The .mpr.lock lifecycle (created on open, removed on close, left behind on a crash) is the one .claude/skills/debug-bson.md already relies on. A stale lock therefore produces a refusal with the remedy, which is the direction the issue asks for (fail toward noise).

Not a change of MDL meaning; it refuses a write that would be silently lost (ADR-0011), under mdl 0 and mdl 1 alike.

Test plan

  • modelsdk/mpr/studiopro_lock_test.go: with APP.MPR.LOCK beside app.mpr (proves case-insensitivity) UpdateRawUnit, DeleteUnit, InsertUnit are refused with *StudioProOpenError and the unit file is byte-identical afterwards; reads still work; a no-op update is not refused. Controls: the same write lands with no lock, with MXCLI_ALLOW_STUDIO_PRO_OPEN=1, and with AllowWritesWhileStudioProOpen (--force). StudioProLockFile ignores other projects' locks, .bak, .lock.old.
  • Revert check: with guardWrite stubbed to nil, TestStudioProOpen_WriteRefused fails (err = <nil>, want *StudioProOpenError).
  • End-to-end on a TestApp copy with testapp.mpr.lock created: exec refused at the first write with the message (nothing written); show modules works; -c "create module …" refused; exec --force writes with a warning; re-running the same script without --force reports already exists / Unchanged entity with no refusal; MXCLI_ALLOW_STUDIO_PRO_OPEN=1 writes; with the lock removed writes land normally.
  • go test ./modelsdk/... ./cmd/mxcli/ ok; make build, make lint (Go + TS + conformance) pass.
  • Docs: exec --help, docs-site quickstart troubleshooting (also replaces the dangling "F4 sync support" link the issue noticed) and capabilities table, check-syntax skill. Finding appended (modelsdk.jsonl); make check-findings ok.

Follow-ups not in this PR: generated Java/JS source files written next to the project are not model writes and are not guarded; sharing a lock implementation with PROPOSAL_concurrent_access.md (mxcli-vs-mxcli) is left to that proposal.

🤖 Generated with Claude Code

ako and others added 5 commits October 1, 2026 19:27
…627)

The fluent API's AttributeModifier.Apply() rebuilt the attribute with
raw == nil and kept only its $ID, so the codec minted GUID = $ID - the
mendixlabs#1119 data-loss class. The write guard refused it, leaving the API
unusable on any Studio Pro-authored attribute. Carry the stored raw
bytes and export level via carryStoredAttribute, now shared with the
entity rewrite's carryAttributeIdentity.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
MoveEntity scanned only the plain associations of the source unit, so a
cross-association created by an earlier move was invisible: moving its
second endpoint left a ParentPointer naming an element absent from its
unit and Studio Pro could not open the project. Handle the three shapes:
convert back to a plain association when both endpoints share a module
(raw transform, GUID carried), let an own cross-association travel with
its FROM entity, and re-point a cross-association in another module.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…endixlabs#849)

Studio Pro does not reload the model from disk, so a write mxcli makes
while the project is open is silently discarded by Studio Pro's next
save. The writer now refuses any write that would reach storage while
Studio Pro's <project>.mpr.lock is beside the .mpr (matched without
regard to case, as Studio Pro lower-cases it). Reads and writes elided
as no-ops are never refused; exec --force or
MXCLI_ALLOW_STUDIO_PRO_OPEN=1 override.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@ako
ako merged commit a870054 into main Oct 1, 2026
33 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.

No guard on file-based writes when Studio Pro has the project open — silent data loss, docs-only warning

1 participant