Files
dsh_shenxian/dsh-server-docs/04-调整方案/25-实例崩溃循环修复与not_running兜底.md
T
admin 5ad755116e chore(docs): 文档库并入代码仓(R4 选 a)+ 索引/台账跟进
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 一律写「远程服务器」。
2026-09-15 18:47:13 +08:00

62 lines
4.0 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 25 · 实例崩溃循环事故(重复 loader id)+ not_running 浏览器导航兜底
- 日期:2026-09-11
- 触发:用户刷新后看到 **`{"error":"not_running"}`** 且无自动跳转
- 结论一句话:**这是一次自造事故** —— 插件包内的 `cordis.patch.yml` 与 profile 层**重复 insert 同一个 loader id** → 实例启动失败并**崩溃循环**(被新上线的熔断拦住),用户停在 `not_running` JSON。已修:包内改空补丁(单点插入)+ 浏览器导航永不吐 JSON + 并发进场等待在飞实例。
- 状态:**已修复并上线**(服务已重启;插件 0.1.2 已装到两个 profile)
---
## 一、事故链(journald 实证)
```
Error: dsh: plugin tree failed to load: failed to apply loader entry include (cordis:include):
duplicate loader entry id: workspace-scoped-picker
→ 实例启动即退出 → 自动重启 ×5(1→2→4→8→16s)
[crash-restart] {"event":"crash-loop-circuit-open","restartsInWindow":5,"maxRestartsInWindow":5,...}
→ 熔断生效、停止自动重启、条目标 failed 并移除
→ 用户刷新子域页面 → 代理判定 not_running → 回落到 reply.code(404).send({error:'not_running'})
→ 浏览器显示裸 JSON
```
**根因**:把包内 `cordis.patch.yml` 从"覆盖行"改成 `insert: workspace-scoped-picker` 后,**profile 层也 insert 同一 id** → loader 认为重复 id 是致命错误(`duplicate loader entry id`)→ 插件树加载失败。
> ✅ 值得记录的正面结果:**档案 20 的崩溃熔断按设计工作**(5 次后开断,避免无限重启风暴),并且它把实例标 `failed` 后允许用户重新进入(清空计数、给足新预算)。
## 二、修复
| # | 修复 | 位置 |
|---|---|---|
| **1** | **包内 bundle patch 改为空补丁 `[]`** —— 该行只在 profile 层**单点插入**,避免双写;插件版本 0.1.2 | `poc/workspace-scoped-picker/cordis.patch.yml` |
| **2** | **not_running 浏览器导航兜底(档案 25 主体)**:`GET + accept: text/html` 时**永不**返回 JSON;并发进场(另一请求正在拉起 → `AlreadyRunningError`)改为**等待在飞实例的 launch token(≤20s)** 再 302;仍拿不到则 **302 回门户** | `src/supervisor/proxy.ts` |
| **3** | 插件 v0.1.2:面包屑根节点用友好名(不暴露 `/var/lib/...`)+ **加载标记** `[workspace-scoped-picker] loaded root=…`(用于确定性验证是否生效) | `poc/workspace-scoped-picker/lib/index.js` |
| **4** | (同批)预置默认工作区 `seedDefaultWorkspace`(见档案 24 后续):编辑器要求"必须选工作区"才能开会话 → 平台直接种 `ws` | `src/supervisor/orchestrator.ts` |
**安装要点(踩坑)**:
- `pnpm add file:<tgz>` 必须带 `HOME=<userRoot>/ws`;
- **profile 是 pnpm workspace 根时才需要 `-w`**(guest 需要),否则会报 `--workspace-root may only be used inside a workspace`(admin 不需要);
- 换包版本(0.1.1→0.1.2)以确保 pnpm 不复用缓存。
## 三、验证
| 检查 | 结果 |
|---|---|
| `ci.sh`(typecheck + build + 38 单测) | ✅ |
| 两 profile 安装 0.1.2 | ✅ 软链均指向 `0.1.2.tgz`;包内 `cordis.patch.yml` 确认为 `[]` |
| profile patch 幂等 | ✅ 仅一处 `insert`(另一处为 `name:` 值) |
| 插件自检 | ✅ 26/26 |
| 服务重启 | ✅ active / 0 残留 / 门户 200 |
| 端到端(实例启动无 duplicate 报错 + 加载标记出现) | ⏳ 待用户进入会话后核对(我会在日志里查 `[workspace-scoped-picker] loaded` 与 `[seed-workspace]`) |
## 四、回滚
| 项 | 回滚 |
|---|---|
| 插件包 | 退回 0.1.1(但 0.1.1 含重复 insert,**不要**;应退回空补丁版并保持 0.1.2) |
| 编排器 | `/opt/dsh/backups/orchestrator.ts.bak-<TS>`、`proxy.ts.bak-<TS>`;或 `git revert ce1b3ac` → `npm run build` → 重启 |
| profile patch | `<profile>/cordis.patch.yml.bak-*` |
## 五、红线遵守
只改编排器自身代码与自建插件包;未触碰官方 dsh 主程序与缓存;未读取任何用户数据。