mirror of
https://github.com/got-feedBack/feedBack.git
synced 2026-08-11 03:09:57 +00:00
fix(plugins): don't treat transient absence from /api/plugins as uninstall (#741)
* fix(plugins): don't treat transient absence from /api/plugins as uninstall The backend clears its plugin registry at the start of load_plugins() and repopulates it incrementally while HTTP stays up, so every backend restart (desktop: Audio Quality soundfont switch, LAN toggle, update restart) serves a window of partial — even empty — /api/plugins responses. loadPlugins() treated absence from the current response as an uninstall, with three destructive consequences for still-loaded plugins: 1. Their settings-panel and screen DOM were wiped while their _loadedPluginScripts entry survived, so the NEXT refetch failed the DOM-existence check and re-evaluated the plugin's screen.js mid-session. For the desktop audio_engine plugin that re-ran init() against the surviving native audio chain and exactly duplicated every VST/NAM/IR stage (the alpha testers' "chain duplicates after leaving the Audio menu" / blown-out gain reports). 2. _reconcilePluginStyles dropped their stylesheet, leaving them visible but unstyled until they reappeared. 3. The stale-contribution sweep unmounted their UI contributions and unregistered their capability participant with no re-registration path (plugin scripts don't re-run thanks to the loadedScripts guard). Absence is now a non-signal everywhere in loadPlugins: the DOM wipe and style reconcile are scoped to plugins the response actually names, and the absence sweep is removed. Present plugins still fully re-sync via _registerLegacyPluginUiContributions each round; failed plugins are present in the response and still cleaned up; nav is rebuilt from the response so genuinely uninstalled plugins drop out of it, and their (un-unloadable) already-evaluated scripts keep their DOM until reload. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * test: update idempotence contract to the absence-is-not-uninstall invariant The removed-plugin sweep contract pinned the old behavior this branch deletes; pin the new invariant instead (no absence sweep + respondedIds scoping on the DOM/style reconcilers). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Fable 5
parent
d2b2a7e9f7
commit
68e29a8b6e
@@ -204,15 +204,20 @@ test('does not collide tags across two different plugins', () => {
|
||||
assert.deepEqual(headLinks.map((l) => l.dataset.pluginId).sort(), ['a', 'b']);
|
||||
});
|
||||
|
||||
test('reconcile removes the <link> of a plugin that vanished from /api/plugins', () => {
|
||||
test('reconcile keeps the <link> of a plugin absent from a partial response', () => {
|
||||
const { inject, reconcile, headLinks } = setupSandbox();
|
||||
inject(plug({ id: 'a' }));
|
||||
inject(plug({ id: 'b' }));
|
||||
assert.equal(headLinks.length, 2);
|
||||
// `a` is no longer returned (uninstalled) — its stylesheet must be dropped.
|
||||
// `a` is missing from this response. That happens transiently during a
|
||||
// backend restart (the plugin registry repopulates while HTTP stays up),
|
||||
// so absence is NOT an uninstall signal — the still-loaded plugin must
|
||||
// keep its stylesheet or it renders visible-but-unstyled until it
|
||||
// reappears. Explicit removal still happens via the not-ready/unstyled
|
||||
// paths (tests below).
|
||||
reconcile([plug({ id: 'b' })]);
|
||||
assert.equal(headLinks.length, 1);
|
||||
assert.equal(headLinks[0].dataset.pluginId, 'b');
|
||||
assert.equal(headLinks.length, 2);
|
||||
assert.deepEqual(headLinks.map((l) => l.dataset.pluginId).sort(), ['a', 'b']);
|
||||
});
|
||||
|
||||
test('reconcile removes the <link> of a plugin that is no longer ready', () => {
|
||||
|
||||
Reference in New Issue
Block a user