Merge pull request #7460 from dotCooCoo/fix/plugin-store-folder-lock

Plug-ins: windows and processes take turns on the Plug-ins folder
This commit is contained in:
BFlat
2026-11-14 21:49:19 +07:00
committed by GitHub
7 changed files with 482 additions and 110 deletions
+3 -1
View File
@@ -9717,7 +9717,9 @@ and turn, shear and flip about it (`origin`).
WebAssembly plug-ins ([plugins.md](plugins.md)) are reached through commands; the desktop app and an unconfined
`vectorcraft-cli` keep the ones `plugin.install` installs in a `Plug-ins` folder next to the preferences and install
them again at startup (`plugin.remove` deletes them there):
them again at startup (`plugin.remove` deletes them there). A window or process locks `disabled.lock` in that folder
while it changes it, so installs, removals, grants and plug-in settings from two of them do not mix; when the file can't
be locked, `plugin.install` and `plugin.remove` fail and change nothing:
`plugin.install {path | dataBase64}` installs one, `plugin.list` / `plugin.info {id}` describe them (parameters,
schema, defaults). An object filter runs on the selected paths and compound paths with `plugin.run {id, params}` (one
undo step; it may change, delete or add objects, and the result is selected). A filter whose manifest lists `inputs`
+7 -1
View File
@@ -269,7 +269,13 @@ capability model proposed for PhotoCraft plug-ins
while `plugin.install` checks a module, the install is refused, before anything is written to the `Plug-ins` folder,
if the other plug-in would need `replace: true` or the changed grant no longer approves the module. A
`plugin.remove` in another session waits until the install has written the `Plug-ins` folder, and then removes the
module and its grant. Loading the `Plug-ins` folder or a plug-ins folder in another session waits for it too. When
module and its grant. Loading the `Plug-ins` folder or a plug-ins folder in another session waits for it too.
Another process using the same `Plug-ins` folder, for example `vectorcraft-cli` beside the app, waits as well: each
window or process locks `disabled.lock` in the folder while it reads and changes the stored modules, `grants.json`,
`disabled.json` and the plug-ins' settings and caches. An install therefore keeps a module with its own grant, and a
grant or setting written in one process does not undo one written in another. When `disabled.lock` can't be
locked, `plugin.install` and `plugin.remove` fail and change nothing, `plugin.setEnabled` keeps its change in the
process until a later change can be saved, and loading the folder writes nothing to it. When
an install of a module that lists no permissions can't
write the module to the `Plug-ins` folder, or can't remove the grant from `grants.json`, the folder keeps the module
it held and `grants.json` keeps the grant. The new module runs until the session ends. When the grant can't be
+1
View File
@@ -1994,6 +1994,7 @@ Moved from the ROADMAP's "Shipped so far" on 2026-10-10. Update it when a featur
- **A dialog's OK runs what it was opened for:** `ui.dialog.set`, and MCP's `answer_dialog` through it, can set a `__` field only when it is a control of that kind of dialog (a section or tab shown, a link between two fields, a name or search being typed). Every other `__` field, such as the `__command` and `__params` of a confirmation or the generic parameter dialog, a plug-in command dialog's `__command` and `__plugin`, or the paste a swatch conflict waits to finish, is refused with "the `<kind>` dialog cannot set protected field `<field>`"; the request sets no field, and with `action: "confirm"` OK does not run. Regressions: `crates/ui-egui/src/tests/automation.rs`, `crates/ui-egui/src/control/tests.rs`. Scores and hours unchanged.
- **Only an approval records a plug-in grant (G17):** `vectorcraft_plugins::registry::grant` takes a `GrantToken`, which the engine makes after an approval and when it loads `grants.json`, and `registry::install`, `install_bytes` and folder loads refuse a module that lists permissions no recorded grant gives this very module (`Error::NeedsApproval`), leaving the plug-in under its id and the revision as they were. A module without `permissions`, or with an empty list, installs as before. `docs/plugins.md` says what the approval does not protect against (native code linked into the app, another program running as the same user, the unsigned `grants.json`). Regressions: `crates/plugins/src/registry.rs`, `crates/plugins/tests/grant_sites.rs`, `crates/engine/src/tests/plugins.rs`. Scores and hours unchanged.
- **A refused plug-in install writes nothing (G17):** `plugin.install` checks the plug-in installed under the module's id and its grant again while it records the grant and installs the module (`registry::install_checked`), and only then writes the module and its grant to the `Plug-ins` folder, attaches its settings and cache and turns a disabled plug-in on. When another session installed a plug-in with the id that would need `replace: true`, or changed the grant the checks relied on, the install is refused and the folder, `grants.json`, the disabled list and the installed plug-in stay as they were. Without a plug-in store, the approval comes from the grant the install checks again. A `plugin.remove`, or a load of the folder or of a plug-ins folder, in another session waits until the install has written the folder. VectorCraft's own rule. Regressions: `crates/engine/src/tests/plugins.rs`. Scores and hours unchanged.
- **Windows and processes take turns on the Plug-ins folder:** each one locks `disabled.lock` in the `Plug-ins` folder while it reads and changes the stored modules, `grants.json`, `disabled.json` and the plug-ins' settings and caches, so two installs of one id keep each module with its own grant, and grants or settings written by two processes are all kept. When the file can't be locked, `plugin.install` and `plugin.remove` fail and change nothing. VectorCraft's own rule. Regressions: `crates/engine/src/tests/plugins.rs`. Scores and hours unchanged.
- **Plug-in approval waits before OK (G17):** the approval dialog's OK stays disabled until its question has been on screen in the focused window for half a second, VectorCraft's own value; a click, Enter, Space, or a press begun before then installs nothing, and the dialog stays open. The wait starts over for every new question, even one that reads the same, and when the window gets the focus back, so a question swapped in just before a click is not approved by it. Cancel works at once. Regressions: `crates/ui-egui/src/tests/plugins.rs`. Scores and hours unchanged.
- **An install automation asked for needs the person to start it (G17):** an approval question that a control request, MCP, injected input or a file dialog one of them opened leads to keeps OK disabled and refuses every confirm, a person's click included; it says to choose Object › Plug-ins › Install Plug-in…, and `ui.installPlugin` answers `approval: "personMustStartInstall"`. The person's own Install Plug-in…, File › Open or drop inspects the module again and asks a new question. Automation's `replace: true` no longer skips the question about taking another module's place, and that question now opens in the approval dialog for everyone. VectorCraft's own rule. Regressions: `crates/ui-egui/src/tests/automation.rs`, `crates/ui-egui/src/tests/plugins.rs`. Scores and hours unchanged.
- **A plug-in with no permissions ends its id's grant (G17):** installing a module that lists no permissions under a granted id forgets the grant, in the process and in `grants.json`, so the formerly approved bytes need a new approval. Attaching a `Plug-ins` folder whose module for that id lists no permissions forgets it too, and plug-in folders forget the process's grant. A refused install leaves the grant. Regressions: `crates/engine/src/tests/plugins.rs`, `crates/engine/tests/plugin_safe_mode.rs`. Scores and hours unchanged.