Skip to content

fix: clear a plugin's patch-layer rows when it is uninstalled - #147

Merged
yaojin3616 merged 2 commits into
mainfrom
fix/plugin-uninstall-residue
Aug 22, 2026
Merged

fix: clear a plugin's patch-layer rows when it is uninstalled#147
yaojin3616 merged 2 commits into
mainfrom
fix/plugin-uninstall-residue

Conversation

@yaojin3616

Copy link
Copy Markdown
Contributor

问题

卸载插件时清了三样东西里的一样:

要清什么 谁在清 之前的状态
dependencies + bundles + node_modules dsh plugin removereconcilePlugins
用户层 cordis.patch.yml指向该插件的行
声明与磁盘状态不一致时的可见性

留下来的行不会报错。指向已消失 id 的行是惰性的;把服务路由到该插件曾提供的 backend 的配置值,只会让那个服务一直等——于是 profile 表现成"启动慢",而不是"启动坏了"。

这正是 v0.4.3 macOS 那次事故的第一层:dsh-doudizhu 残留让服务卡在等 storageDomain,把后面真正致命的 HMR 缺陷整整挡住了一个版本(见 #146)。

修复

一、卸载时定向清理用户层

在插件文件被删之前,先从它自己的 bundle patch 里读出它声明的 entry id;卸载成功后,只摘掉 id 命中这些 id、或 insertname 为该插件的行。

用户层是用户自己写的东西(含注释),所以按文档编辑而非重新序列化:无关的行、缩进、注释都原样保留;首行注释(文件头)在首行被删时会搬到下一行,不会丢。没有任何行命中时,文件一个字节都不动

同时修掉 resetPluginProfile 的另一个极端——它为了清掉一个插件的几行,会把整个 cordis.patch.yml 重写成 [],连用户自己的覆盖层一起铲。现在只有"不指定插件的整体重置"才会清空。

二、启动时报告不一致

inspectProfileConsistency 在启动前检查四类状态,写进 harness 日志:

  • bundle 声明了但没装
  • 装了、声明了 bundle,但没进 bundles 列表
  • 装在 node_modules 里,但 manifest 里彻底没有声明
  • patch 层 insert 了一个没装的包

只报告,不改变启动流程——重点是让这些状态不再沉默

验证

  • npm run typecheck 通过;npm test 37 文件 253 测试全绿(新增 12 条)。

  • 关键性质有测试兜底:卸载 dsh-doudizhu 后,它的 id: 行和两条 insert 都消失,而用户自己的 accent: violetdsh-my-own 完好,文件头注释也在。

  • 一致性检查跑在真实出问题的 profile 上,准确报出那一条:

    dshmarket is installed but declared nowhere in the profile manifest
    

    这正是让市场装不上、又反复提示重启的那个状态。

范围

对应内部文档第十节任务 5(插件系统 / P1)。与 #146(macOS 打包版 HMR)无重叠——那是启动能力问题,这是状态一致性问题;两者叠加正是那次事故难查的原因。

仍未覆盖:其它行对该插件所提供服务的引用(比如把 storage 路由到插件的 sqlite backend)。那需要知道每个插件提供哪些服务,得靠上游的能力声明;在那之前,第二部分的启动报告至少让这类状态在日志里点名可见,而不是靠人肉排查。

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.
@yaojin3616
yaojin3616 merged commit a455abb into main Aug 22, 2026
4 checks passed
@yaojin3616
yaojin3616 deleted the fix/plugin-uninstall-residue branch August 22, 2026 09:00
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant