fix(mpr): refuse file writes while Studio Pro has the project open (mendixlabs/mxcli#849) - #915
Merged
Merged
Conversation
…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>
11 tasks done
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
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 checkpasses, 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.guardWriterefuses, with*StudioProOpenError, every write that would actually reach storage while Studio Pro's lock file is beside the.mpr:updateUnitandWriteTransaction.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), orMXCLI_ALLOW_STUDIO_PRO_OPEN=1for 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
.gitignorein ako/TestApp (Studio Pro-authored) listsTestApp/testapp.mpr.lockbesideTestApp/TestApp.mpr— Studio Pro lower-cases the project name, so the match is case-insensitive on<mpr basename>.lock. The same file listsmprcontents/mprjournal*; that was not used as a signal (its lifetime is not known to be tied to the project being open)..mpr.locklifecycle (created on open, removed on close, left behind on a crash) is the one.claude/skills/debug-bson.mdalready 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: withAPP.MPR.LOCKbesideapp.mpr(proves case-insensitivity)UpdateRawUnit,DeleteUnit,InsertUnitare refused with*StudioProOpenErrorand 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, withMXCLI_ALLOW_STUDIO_PRO_OPEN=1, and withAllowWritesWhileStudioProOpen(--force).StudioProLockFileignores other projects' locks,.bak,.lock.old.guardWritestubbed tonil,TestStudioProOpen_WriteRefusedfails (err = <nil>, want *StudioProOpenError).testapp.mpr.lockcreated:execrefused at the first write with the message (nothing written);show modulesworks;-c "create module …"refused;exec --forcewrites with a warning; re-running the same script without--forcereportsalready exists/Unchanged entitywith no refusal;MXCLI_ALLOW_STUDIO_PRO_OPEN=1writes; with the lock removed writes land normally.go test ./modelsdk/... ./cmd/mxcli/ok;make build,make lint(Go + TS + conformance) pass.exec --help,docs-sitequickstart troubleshooting (also replaces the dangling "F4 sync support" link the issue noticed) and capabilities table, check-syntax skill. Finding appended (modelsdk.jsonl);make check-findingsok.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