Plug-ins: an update left in the store by a failed put-back turns a disabled plug-in on

When plugin.install writes a module that lists permissions, its grant
can't be written, and the old module can't be put back (or, on a first
install, the new module can't be removed), the store holds the new
module. install now takes put_back_plugin's result, so the plug-in's
disabled entries (by id and by stored file) are cleared as for any
install the store holds. The old grant, or none, still doesn't cover the
new module, so the next start skips it until it is approved.

Docs: docs/plugins.md, docs/mcp.md, docs/target-app-parity.md.
This commit is contained in:
dotCooCoo
2026-11-11 13:03:38 -08:00
parent b08e421445
commit fd7ff70941
5 changed files with 114 additions and 7 deletions
+3 -1
View File
@@ -7593,7 +7593,9 @@ pendingItem}`; a command does not turn Safe Mode on or off. `plugin.setEnabled {
off for the next launch, as Help › Safe Mode's Enable on Re-launch does; it does not load, unload or uninstall anything
now, and `plugin.install` of a disabled plug-in turns it on. Turning a plug-in on by `id` or by installing it also turns
on the file it is stored in, which names it when it stopped a launch while loading; when the new version can't be saved
over that file, the plug-in stays disabled. `plugin.list` reports `safeMode` and `disabled`, and
over that file, the plug-in stays disabled. When the new version is saved but its grant can't be, and the file can't be
returned to what it held before (the old module, or no file), the plug-in is turned on, and the next launch skips it
until it is approved. `plugin.list` reports `safeMode` and `disabled`, and
`plugin.reload` skips disabled plug-ins. A change to the disabled list is saved on top of the list the file holds at
that moment, so a change another window saved to the same preferences meanwhile is kept. When the list file can't be
read (it is too large) or holds another format or version, it is left as it is; a file that isn't JSON is replaced.
+3 -1
View File
@@ -240,7 +240,9 @@ capability model proposed for PhotoCraft plug-ins
records the new hash. When the new module or its grant can't be written to the `Plug-ins` folder, the folder keeps
the old module and its grant, and the old module loads at the next start. If the write that puts the old module back
fails as well, the folder holds the new module with the old grant, the grant does not cover that module, and the
plug-in is skipped at the next start until it is approved again. A grant whose hash is not 64 lower-case
plug-in is skipped at the next start until it is approved again. When the folder held no module for that id, that
write removes the new module; when the removal fails, the folder holds the new module and no grant covers it. In
both cases a disabled plug-in is turned on, as for any install the folder holds. A grant whose hash is not 64 lower-case
hexadecimal digits covers nothing. `plugin.remove` forgets the grant, and so does a successful `plugin.install` of a
module that lists no permissions under that id, so the earlier bytes need a new approval. When `plugin.remove` can't
remove the grant from `grants.json`, it returns an error, the plug-in stays installed, and a second `plugin.remove`
+1
View File
@@ -1652,6 +1652,7 @@ Moved from the ROADMAP's "Shipped so far" on 2026-10-10. Update it when a featur
- **Plug-in data kept with the document (G17):** a filter writes JSON of at most 1 MiB with `documentData` (one undo step with its objects; `null` removes it) and reads it back in `_context.documentData`, which no other filter is given. It is the document's foreign data (`unknown.pluginData`, by plug-in id), as the perspective grid is, so the native file keeps it with no section of its own; an exporter is given the whole document, so it can read every plug-in's. Regressions: `crates/engine/src/tests/plugins.rs`, `crates/engine/tests/plugin_export.rs` (the exporter), `crates/doc/src/plugin_data.rs`, `crates/plugins/src/objects/tests.rs`. Scores and hours unchanged.
- **Heavy filter plug-ins (G17):** a filter's manifest may ask for `"budget": "heavy"`: 1 G instructions (20 times a light run's) and 300 s a run, for routing, packing or simulation. Install Plug-in… and File › Open ask the user first, `plugin.install` needs `allowHeavy: true` (`plugin.inspect` tells beforehand), the Additional Plug-ins Folder skips such filters, and the plug-in store keeps the answer. Its dialog has no Preview; the app runs it on a thread of its own with a progress window (Cancel, Continue in Background) and applies the result as `plugin.run` with the same parameters, one undo step, unless the document or the plug-in changed meanwhile. Regressions: `crates/plugins/tests/abi.rs`, `crates/plugins/src/manifest.rs`, `crates/engine/src/tests/plugins.rs`, `crates/ui-egui/src/tests/plugin_runs.rs`. Scores and hours unchanged.
- **Safe Mode for plug-ins (G17):** `vectorcraft --safe-mode` and `vectorcraft-cli --safe-mode <subcommand>` start without plug-ins. When a launch stopped while a plug-in was loading or drawing its first live effect, the next launch names that plug-in, without saying it caused the stop, and offers Disable and Continue, Start Normally or Start in Safe Mode. Help › Safe Mode lists the disabled plug-ins with Enable on Re-launch, which takes effect at the next launch, and the app bar and the window title show Safe Mode. The help page (https://web.archive.org/web/20210601162814/https://helpx.adobe.com/pdf/illustrator_reference.pdf, pages 65 to 69) describes the same choice between diagnosing and starting normally, a Safe Mode with the problem item off and the rest of the app working, and turning the item back on at the next launch. In Safe Mode no plug-in loads or runs, and a live-effect plug-in draws its object's geometry unchanged while the object keeps the effect, which the Appearance panel marks "(Safe Mode)" with the plug-in's id in its tooltip and `effect.list` marks `skipped`; Expand Appearance refuses such an object. The web build keeps no plug-ins between page loads and has no marker. Each desktop launch with preferences keeps a marker in the `Launch` folder next to them; `vectorcraft-cli` and a desktop launch without preferences keep none, so a stop there leads to no choice at the next launch. Disabled plug-ins are listed in `Plug-ins/disabled.json`. `plugin.safeMode` reports Safe Mode and the disabled plug-ins, and `plugin.setEnabled` changes the list. Regressions: `crates/plugins/src/safe_mode.rs`, `crates/plugins/tests/safe_mode.rs`, `crates/engine/tests/plugin_safe_mode.rs`, `crates/ui-egui/tests/safe_mode.rs`. Scores and hours unchanged.
- **A disabled plug-in whose update stays in the store is turned on:** when `plugin.install` writes a module that lists permissions to the `Plug-ins` folder, its grant can't be written, and the folder can't be returned to the old module (or, on a first install, the new module can't be removed), the folder holds the new module, so a disabled plug-in is turned on, by id and by stored file, as for any install the folder holds. The old grant, or none, doesn't cover the new module, so the next start skips it until it is approved. Regressions: `crates/engine/src/tests/plugins.rs`. Scores and hours unchanged.
- **Plug-in instruction budget at a bulk memory instruction:** a run that pauses where one slice of its instruction budget ends keeps the instructions the slice still held. A `memory.fill` or `memory.copy` that the slice can't pay for in full runs on the next slice, so a run whose budget covers its work finishes; a run ends with `Limit("instruction budget")` only when the instructions it has left can't pay for the next instruction. Regressions: `crates/plugins/tests/abi.rs`. Scores and hours unchanged.
- **Bulk memory fuzz cases use the host's fuel constants:** `crates/engine/tests/plugin_fuzz.rs` samples runs around one slice of the instruction budget and lengths around the bytes one unit of fuel pays for with `vectorcraft_plugins`' own `FUEL_SLICE` and `BYTES_COPIED_PER_FUEL`, so the sampled cases move when either value changes. Regressions: `crates/engine/tests/plugin_fuzz.rs`. Scores and hours unchanged.
- **Plug-in permissions (G17):** a manifest may list `permissions` (`doc.read`, `doc.write`, `state`, `effect.render`, `export.write`; the first three follow storytold/photocraft#662) and an `api` level; without the list a plug-in gets what its kind implies and installs as before. Install Plug-in… and File › Open show the name, id, version, file SHA-256 and each permission in plain words, with control and bidirectional characters escaped, and install nothing on Cancel; for a plug-in that lists permissions, `plugin.install` needs `grant` and folders install it only when it is granted. The grant is kept with the module's hash, a module asking for more asks again, and a filter run refuses output its permissions don't allow. Regressions: `crates/plugins/src/permissions.rs`, `crates/plugins/src/manifest.rs`, `crates/engine/src/tests/plugins.rs`, `crates/engine/tests/plugin_fuzz.rs`, `crates/ui-egui/src/tests/plugins.rs`. Scores and hours unchanged.