G2: --save runs on one project take turns, so no edit is lost (#2851)

A run reads the project, edits it and writes the whole file back, so two
at once silently dropped the edits of whichever saved first. A run that
saves over the file it opened (--save, or --save-as the same file) now
holds an OS lock on `.<name>.lock` next to the project from before its
read until it exits. The next run says so on stderr, waits, then edits
what the first one saved. Ported from filmcraft-cli
(storytold/filmcraft#3926).

The OS drops the lock however the process ends, so a crash leaves nothing
stale. If the lock can't be taken the run warns and saves as it did
before. A missing project stops the run before it opens anything, with
the same error opening gives, so a project created in between is never
saved without the lock. The desktop app and `mcp` don't take this lock.
This commit is contained in:
JonRC committed 2026-11-02 13:00:47 -03:00
1 parent 9d7baea5cc
commit 33156f4119
4 files changed
+324

No files matched your search

+8
View File
@@ -751,6 +751,14 @@ effectcraft-cli exec file.importLottie '{"path":"anim.json"}' main.ecproj --save
effectcraft-cli render main.ecproj --comp Main --out main.mov --format prores --progress # progress lines on stderr for a program that reads them
```
Runs that save over the project they opened (`--save`, or `--save-as` the same file) take turns, so
parallel scripts don't lose edits. Each holds an OS lock on `.<name>.lock` next to the project from
before it reads the file until it exits; the next one says so on stderr, waits, and then edits what
the first one saved. A crash releases the lock, and the empty file left behind is harmless. Only these
CLI runs take the lock: the desktop app, `mcp`, and a `file.save` step in a run without `--save`
don't. OS locks can also be unreliable over network shares and synced folders. If the lock can't be
taken, the run warns on stderr and saves anyway.
A long `run` doesn't fit on a command line (Windows caps it at 32 767 characters, Linux one
argument at 128 KiB). `run --args-file F` reads the same `<id> <json>` arguments one per line;
`run --commands FILE` reads steps as a JSON array of `{"command": ID, "params": {…}}` objects, or one