Repository navigation
fix(plugins): activate a first install staged without Restart on the next launch - #505
Closed
nt-nerdtechnic wants to merge 3 commits into
Closed
nt-nerdtechnic wants to merge 3 commits into
nt-nerdtechnic wants to merge 3 commits into
Conversation
…next launch Installing a plugin (from a file or the Marketplace) stages it as a candidate; only plugins:restart promoted it. Reopening the App without pressing Restart left the lifecycle selector with a candidate and no active package, so the plugin never loaded, the startup scan fell back to <root>/<id>/manifest.json and logged a misleading "installed plugin quarantined: ... manifest.json is not valid UTF-8: ENOENT", and nothing was shown on screen. - plugins:restart's body becomes restartStagedCandidate(id); the handler keeps its authorization and target checks and calls it. - New controller method activateStagedFirstInstalls(), run at startup: promotes each candidate that has no active package, no activation in progress and nothing loaded under its id (a factory rollback also leaves only a candidate), exactly as Restart would. Updates and packages a rollback displaced stay staged: the selector does not tell them apart. - The startup scan skips a staged first install instead of reporting it as a broken package (a legacy package at the root still loads as before). - A candidate that cannot be promoted stays staged, is logged with the real reason, and the main window shows a toast naming the plugin, the reason and Settings -> Navide Plugins (Restart / Discard). en-US, zh-TW, ja-JP. Not a 0.2.27 regression: the scan fallback dates from 088b4ca (#133) and the promote path is unchanged since v0.2.26. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…Apply now" A package staged beside the active one (an update installed without Restart, or the package a rollback displaced) is not applied for the user at launch: the selector does not tell the two apart. Reopening Navide used to run the old version with nothing on screen. - pluginIpc: stagedUpdates() lists every record with an active package, a candidate and no activation in progress. - index.ts: after the first-install activation, the list goes to the main window through pluginStagedCandidateRelay (now carrying both notices, held until a main window has loaded). - Renderer: an announcements-centre row per package, "The Navide plugin X has version Y waiting to be applied; version Z is running", with "Apply now" (plugins:restart for that package, only while the same version is still staged beside the same active one), plus a toast. en-US, zh-TW, ja-JP. - Tests for the three relaunch cases: first install (promoted), update (stays staged, disclosed) and rollback (the rollback stands, disclosed). Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This was referenced Oct 20, 2026
nt-nerdtechnic
added a commit
that referenced
this pull request
Oct 20, 2026
…decision A) User decision A (2026-10-21 00:13): an update an agent applied without asking (O1: it discloses exactly what the running version does) also takes effect immediately. - commitUpdate stages the update as before, then runs the same restartStagedCandidate switch with the requesting agent as the trigger. - The switch record carries the agent: the announcements row and toast say "An automatic update started by <agent> switched X from vA to vB" (en-US, zh-TW, ja-JP), with the same "Back to vA". - A failed switch restores the old version (the restart's own rollback), keeps the update staged, and the main window keeps a "switch failed" row with the reason and Apply now (the #505 staged-package row, which now merges a newer report of a plugin instead of replacing the whole list). - The O1 rules themselves are unchanged: anything that discloses differently still goes back to the user's approval. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
nt-nerdtechnic
marked this pull request as ready for review
October 20, 2026 17:22
nt-nerdtechnic
added a commit
that referenced
this pull request
Oct 20, 2026
…next launch (#505) Squash-merged from origin/pr-505. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Collaborator
Author
|
Squash-merged into main as 8f51ac0. |
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.
Summary
A plugin installed (from a file or the Marketplace) is staged as a candidate and only
plugins:restartpromoted it. If the user reopened Navide without pressing Restart, the plugin never loaded, the log saidinstalled plugin quarantined: … manifest.json is not valid UTF-8: ENOENT, and nothing appeared on screen (packaged checklist 0.2.27, finding 2). This PR activates such a first install at launch exactly as Restart would, makes the log accurate, and shows a main-window notice for any candidate that still needs the user.Why
Developer experience: the sideload → edit → reinstall loop must work without a hidden extra step. A relaunch has nothing running, so promoting the staged first install is the same cutover Restart performs, with the same checks (trust re-verification, digest identity, version-bound grant from install-time consent).
What changed
pluginIpc.ts: theplugins:restartbody is nowrestartStagedCandidate(id)(header and footer only; the body is unchanged). The handler keepsassertAuthorized+assertPluginRemovalTargetand calls it. New controller methodactivateStagedFirstInstalls()promotes each candidate with no active package, no activation in progress and nothing loaded under its id, and returns those it could not promote (they stay staged).activatePrevious/completeRollback/demoteActiveToCandidatekeep it as the candidate). The selector does not tell these apart, so the user still decides with Restart, as on main.installedPlugins.tsscanInstalledPlugins: a staged first install (candidate, no active, nomanifest.jsonat the root) is skipped instead of being reported as a broken package. A legacy package at the root still loads as before.index.ts: runsactivateStagedFirstInstalls()at startup, next toloadDeveloperFolders(). Each failure is logged with its real reason and sent through the newpluginStagedCandidateRelay(same hold-until-loaded pattern aspluginAgentActionRelay).onPluginStagedCandidateNotActivated, App.vue error toast, and thelabel.plugin-staged-not-activatedstring in en-US, zh-TW and ja-JP. The toast names the plugin, the version, the reason, and Settings → Navide Plugins, where Restart and Discard are.Second commit: disclose a staged package beside the active one (coordinator request)
pluginIpc.tsstagedUpdates()lists{pluginId, activeVersion, stagedVersion}for every record that has an active package, a candidate and no activation in progress.index.tssends the list to the main window after the first-install activation, through the same relay (plugins:stagedUpdates).plugins:restartfor that package, only while the same version is still staged next to the same active one (pluginStagedApplyApplies). Otherwise the row is dropped with a "changed since" notice.Regression check vs 0.2.26
Not a regression.
git diff v0.2.26..origin/maintouches nothing on the scanner or promote path (installedPlugins.tsunchanged; the selector only gainedwebin grant validation). The root fallback inscanInstalledPluginsdates from 088b4ca (#133, 2026-09-26). No App was launched.Test plan (red → green)
Red on origin/main, 4/4 (
pnpm vitest run src/main/plugins/pluginIpc.localInstall.test.ts -t "on the next launch" --maxWorkers 1):is not reported as a broken package by the startup scan: the scan returned the ENOENT error entryis activated exactly as Restart would have done it: no such method (active, grant,local-unsignedsource and the next launch's scan are all checked)leaves an update staged next to the active package for the user to restartreports a candidate that can no longer be activated and keeps it staged: a tampered candidate is reported as quarantined and stays stagedGreen: all 4.
Also:
src/main/pluginStagedCandidateRelay.test.ts(2) andsrc/renderer/src/composables/__tests__/pluginStagedCandidateNotice.test.ts(2: text in all three locales, and wiring main → relay → preload → App.vue).Regression: existing plugins
pnpm vitest run pluginIpc.localInstall pluginIpc.localInstall.review pluginIpc.agentFileInstall installedPlugins pluginStagedCandidateRelay pluginStagedCandidateNotice plansBackendStoppedNotice pluginAgentActionRelay --maxWorkers 2: 8 files, 249 passedpnpm vitest run pluginIpc pluginIpc.developerMode pluginIpc.testplace pluginIpc.digestBlock navideBrowserRegistryInstall --maxWorkers 2(every suite that drivesplugins:restart): 5 files, 230 passedpnpm typecheck: exit 0 (node, integration, public packages, built-in plugins, web, public), on both commitspnpm vitest run pluginIpc.localInstall pluginIpc pluginStagedCandidateRelay pluginStagedCandidateNotice useAnnouncements AnnouncementsPanel --maxWorkers 2gives 6 files, 243 passedProtection check
git diff origin/main -- 'backend/agent_team_backend/credential_*.py' 'backend/agent_team_backend/quota_*.py' 'backend/agent_team_backend/usage_*.py' | wc -lgives 0. No backend file is touched.function|const|letnames, origin/main vs branch) ofpluginIpc.ts,index.ts,installedPlugins.ts,preload/index.tsandApp.vueshows nothing removed. App.vue's NUL byte is preserved.PluginTrustRefreshController.activateStagedFirstInstalls()andstagedUpdates(), the preloadonPluginStagedCandidateNotActivatedandonPluginStagedUpdates, the announcement kindplugin-stagedand the actionplugin-staged-apply.🤖 Generated with Claude Code