1) dsh-server-docs/ 从工作区(原 E:\...\aliyun-dsh-server\dsh-server-docs)**整体并入本仓**,
保留目录名 ⇒ 仓库内 dsh-server-docs/... 的相对引用天然继续有效;旧目录(含其 .git)已归档到
工作区 _中间产物_待清理/,未随本提交带入。
2) .gitattributes:新增 `dsh-server-docs/** -text` —— 原文档库是 `* -text` + autocrlf=false,
必须保持纯 LF,否则会被本仓的 CRLF 规则翻掉。
3) 活引用里的绝对路径已全部改到新位置(docs 的 INDEX / README / scripts / skills + 用户级 skills
+ ~/.workbuddy/settings.json 的 hooks);历史档案(04-调整方案/、archive/)按「只增不改」未动。
⚠️ hooks 路径改动需「完全重启会话」才生效(配置是会话启动快照)。
4) 交接单/T08:新增 §16「生产整体切换执行记录」(形态 / 落地动作 / **4 个只有真上线才暴露的真 bug** /
验收证据 / 回滚命令 / 残留项);台账 T08 行 → 已完成并归档;03-路线图 §二 登记 T08 收尾项。
5) 统一称谓:**「本机」只指跑 WorkBuddy 的开发机**,47 / 106 一律写「远程服务器」。
4.6 KiB
4.6 KiB
34 · 功能插件启用:探活 + 快照回滚 + 逐插件隔离标记
- 日期:2026-09-11
- 触发:用户确认「批量 + 统一重启 + 刷新 + 死循环风险」,并明确「标记错误插件禁用并标记(让 admin 方便知晓),其余正常插件不需要回滚」
- 状态:✅ 已实施(commit
0283c1b)并端到端验证通过(含「好插件保留」真实插件复核)
一、暴露的既有缺陷
/api/plugins/mine/apply 的异步任务原本是:
pnpm add 各插件 → reconcileBundles → restartMain → 直接标 success
全程没有任何探活 → 插件把实例搞崩(崩溃循环)时,任务照样报「✓ 已应用」,用户以为成功、实际实例起不来。档案 25 的事故(duplicate loader entry id)正是这一类。排查时还发现第二个更严重的既有缺陷 → 见档案 35。
二、设计(采纳用户方案)
① 快照 profile(package.json / pnpm-lock.yaml / cordis.patch.yml)
② 禁用项 → pnpm remove(不引入新代码,永远安全,不需探活)
③ 启用项 → 先「整批试一次」(正常路径只花 1 次重启,与旧行为一致)
探活通过 → 完成
探活失败 → ④
④ 回滚快照 → 逐插件隔离定位:能起来的保留,起不来的单独摘掉 + 记事故
结束状态 = 快照 + good 集合 ← 好插件不被坏插件连累
⑤ 事故写入 audit_log(action='plugin_incident')
⑥ 任务结果新增 rejected[],stage/error 写明「哪些已自动禁用」
三、实现
| 文件 | 改动 |
|---|---|
orchestrator.ts |
新增 restartAndProbe(userId, budgetMs=60000) |
spawner.ts |
接口加该方法 |
k8s-spawner.ts |
兜底实现(k8s 未启用;返回 ok:true 保持旧行为) |
business-plugins.ts |
apply 任务重写 + snapshotProfile/restoreProfile |
探活判定的两个关键点(都是实测踩出来的):
- 不能只判
restartMain的返回值 —— 编排器里没有该用户实例时(portal 重启清了内存态、或实例被空闲回收)restartMain返回undefined,会把无辜插件误判为不兼容。→ 改为:无实例时先按/api/dsh/enter同路径launch一个。 - 固定短等待会把好插件判死 —— 初版用「固定 2.5s 稳定窗口」,实测好插件也报「未产出 launch token」。→ 改为轮询(500ms 间隔、总预算 60s),拿到 token 且
status==='running'后再过 2.5s 确认没闪崩。
四、验证(2026-09-11,端到端)
用两个自建 fixture 插件:good-test(合法 bundle)、bad-test(cordis.patch.yml 插入已存在的 loader id permission → 必然启动失败)。
| 检查项 | 结果 |
|---|---|
| 坏插件被准确诊断 | ✅ 回传真实错误 TypeError: duplicate loader entry id: permission(cordis-plugin-loader/lib/index.js:91),非泛化文案 |
| 隔离循环执行 | ✅ 阶段依次 整批试→定位(1/2)→定位(2/2)→恢复实例,耗时 3s→30s→33s→42s(真实重启+探活) |
| 实例未进崩溃循环 | ✅ 全程 instance 存活,最终回到干净状态 |
| 快照回滚生效 | ✅ final bundles = 原始 5 个;deps 无残留 |
| 事故可追溯 | ✅ audit_log.action='plugin_incident' 记录 pluginId + 真实 reason |
| 好插件被保留 | ✅ 已用真实插件复核通过(2026-09-11):从官方白名单导入真实 npm 插件 dsh-status-rotator,与 bad2(npm-pack 布局、仅 patch 有差异)一起启用 → 任务返回 完成:1 个已启用,1 个已自动禁用;bundles/deps 均含 dsh-status-rotator 且不含 bad2,plugin_incident 记录 bad2 的完整真实错误,实例存活。→ 「好插件不被坏插件连累」成立。此前两轮 fixture 失败纯属 fixture 自身问题(平铺 tar 布局 pnpm 不接受 / 包名与 patch 引用不一致)。 |
五、后续
用真实插件复核「好插件保留」✅ 已完成(2026-09-11):白名单导入dsh-status-rotator+bad2一起启用 → 好插件保留、坏插件被摘并标记。- admin 可见性:事故已入
audit_log,但读取/聚合并未实现 —— 门户「插件管理」页应显示「⚠ N 个用户启用失败」。需要新增一个 audit 读取方法(adapter 目前只有写)。 - 重启前预检:应用后、重启前跑
dsh --profile web --dump-config,可 0 重启拦住配置类错误(未做)。 - 前端:
location.reload()与被禁用插件的展示,随批次 3(client bundle 改造)一起做。