fix: clear a plugin's patch-layer rows when it is uninstalled - #147
Merged
Conversation
Uninstalling a plugin took its dependency, its bundle row and its files. It
never took the rows the user patch layer aims at that plugin — an
id-targeted override, or an insert naming the package — so those rows
outlived it and went on pointing at something that no longer composes.
Nothing rejects that. A row aimed at a missing id is inert, and a config
value routing a service at a backend the plugin used to provide simply
leaves that service waiting, so the profile reads as a slow start. That is
how a leftover plugin config hid a fatal startup bug on macOS for a full
release: clearing the residue was what let boot run far enough to reveal it.
Uninstall now drops exactly the rows that name the plugin, reading the ids
from its own bundle patch before the package goes away. The layer is the
user's own work, comments included, so it is edited as a document rather
than rewritten — and only a reset with no plugin named, the deliberate
"start over", still clears the whole thing. Before this, removing one
plugin's rows meant wiping every override the user had written.
A launch-time report covers what uninstall cannot reach retroactively: a
bundle declared but not installed, a package installed but never composed,
one left installed while every declaration is gone, a layer inserting
something uninstalled. Reporting only — the launch proceeds either way —
because the point is that these states stop being silent. Run against the
profile that prompted this, it names the real one:
dshmarket is installed but declared nowhere in the profile manifest
Updates downloaded themselves. The first the user heard of one was a banner saying the download was already under way, and the only choice on offer was when to restart. Closing that banner silenced it for the sitting, so a release someone had decided against came back every launch. Accepting the update is now the same action as starting the download: autoDownload is off, `update-available` only offers, and the download begins when the user takes it. Nothing leaves the network for an update nobody agreed to. Skipping is the durable answer to the same question, kept per version beside the profile, so a later release still asks. A manual check ignores the skip — that is how a skipped version is taken back, without needing a way to unskip.
This was referenced Aug 22, 2026
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.
问题
卸载插件时清了三样东西里的一样:
dependencies+bundles+node_modulesdsh plugin remove→reconcilePluginscordis.patch.yml里指向该插件的行留下来的行不会报错。指向已消失 id 的行是惰性的;把服务路由到该插件曾提供的 backend 的配置值,只会让那个服务一直等——于是 profile 表现成"启动慢",而不是"启动坏了"。
这正是 v0.4.3 macOS 那次事故的第一层:
dsh-doudizhu残留让服务卡在等storageDomain,把后面真正致命的 HMR 缺陷整整挡住了一个版本(见 #146)。修复
一、卸载时定向清理用户层
在插件文件被删之前,先从它自己的 bundle patch 里读出它声明的 entry id;卸载成功后,只摘掉
id命中这些 id、或insert中name为该插件的行。用户层是用户自己写的东西(含注释),所以按文档编辑而非重新序列化:无关的行、缩进、注释都原样保留;首行注释(文件头)在首行被删时会搬到下一行,不会丢。没有任何行命中时,文件一个字节都不动。
同时修掉
resetPluginProfile的另一个极端——它为了清掉一个插件的几行,会把整个cordis.patch.yml重写成[],连用户自己的覆盖层一起铲。现在只有"不指定插件的整体重置"才会清空。二、启动时报告不一致
inspectProfileConsistency在启动前检查四类状态,写进 harness 日志:只报告,不改变启动流程——重点是让这些状态不再沉默。
验证
npm run typecheck通过;npm test37 文件 253 测试全绿(新增 12 条)。关键性质有测试兜底:卸载
dsh-doudizhu后,它的id:行和两条insert都消失,而用户自己的accent: violet和dsh-my-own完好,文件头注释也在。一致性检查跑在真实出问题的 profile 上,准确报出那一条:
这正是让市场装不上、又反复提示重启的那个状态。
范围
对应内部文档第十节任务 5(插件系统 / P1)。与 #146(macOS 打包版 HMR)无重叠——那是启动能力问题,这是状态一致性问题;两者叠加正是那次事故难查的原因。
仍未覆盖:其它行对该插件所提供服务的引用(比如把 storage 路由到插件的 sqlite backend)。那需要知道每个插件提供哪些服务,得靠上游的能力声明;在那之前,第二部分的启动报告至少让这类状态在日志里点名可见,而不是靠人肉排查。