Files
dsh_ai1net_server/.workbuddy/memory/2026-09-下12.md
T
admin c1b5e4d966 chore(工作区): 全量入库 + 补齐 .gitignore(以工作区为准)
- 变更规模:新增 514 / 修改 62 / 重命名 155 / 删除 4(归档重组与文档轮次)
- .gitignore 修:`归档/**/db-cwd归一-备份-*/` —— 原规则写绝对层级(归档/db-cwd归一-…),
  目录搬进 归档/配置与备份/ 后**静默失效**,43 MB 的 DB 备份又变成未跟踪
- .gitignore 补:嵌套 git 内部数据(归档/内嵌git-20261008/、归档/skills-git-旧线-20261007/dotgit-原样移出/)
- .gitignore 补:运行态与部署副本(.workbuddy/collab/、.workbuddy/tools/、.workbuddy/.load-pending、.workbuddy/tmp-*)
- .gitignore 补:备份件(*.bak-*)
- 未跟踪文件从 2190 降到 890(其余为 归档/ 归档件与 .workbuddy/memory/ 知识文件,按口径入库)
2026-10-10 23:13:22 +08:00

47 KiB
Raw Blame History

工作日志 · 2026-09(第 13 片)

⚠️ 本目录日志已按【月】分片(2026-09-15 用户定,单文件 ≤50 KB):同月续片 2026-09.md / 2026-09-下.md / 2026-09-下2.md / 2026-09-下3.md / 2026-09-下4.md / 2026-09-下5.md / 2026-09-下6.md / 2026-09-下7.md / 2026-09-下8.md / 2026-09-下9.md / 2026-09-下10.md / 2026-09-下11.md / 2026-09-下12.md / 2026-09-下13.md / 2026-09-下14.md / 2026-09-下15.md / 2026-09-下16.md / 2026-09-下17.md / 2026-09-下18.md / 2026-09-下19.md / 2026-09-下20.md / 2026-09-下21.md / 2026-09-下22.md / 2026-09-下23.md 写入约定:一律 append 到当月最后一片;该片超 50 KB ⇒ 新建 2026-09-下N.md;⛔ 不要再按日新建 2026-09-DD.md。 覆盖来源:2026-09-13.md 上一片:2026-09-下11.md 下一片:2026-09-下13.md


08:20–08:45 会话 3c12a818(锁名 浮层排障-0822):我 08:15 的改动把注入脚本写崩了 → 热修 + 触发面补全 + 固化校验

用户报障:「admin 主页面断开连接,但没显示『工作区已休眠正在唤醒』浮层,模型一直显示连接异常」。

一、根因(我引入的)

  • 08:15 浮层视觉升级时写了 st.textContent = [...].join('\n'); —— 该代码在 TS 模板字面量 SESSION_RECOVERY_JS 内部, \n 在模板求值时变成真换行 ⇒ 注入浏览器的 JS 直接 SyntaxError ⇒ 整段注入脚本不执行 ⇒ 浮层 / 自检 / 自愈全废,页面只剩 dsh 自己的「连接异常」。
  • 为什么之前的校验没拦住:只做「从 lib/ 抽原始文本 → new Function()」——跳过模板求值,所以是假绿。

二、顺带挖出的第二个(更早、更严重)

  • 同文件的 SESSION_ASSIST_JS(档案 56「我的文件 / 能力」助手)8 处 '\n' 同样是单反斜杠 ⇒ 从来没在浏览器里执行过。 此前验收只 grep 页面 HTML 有没有 __dshAssist 字样 —— 验的是"文本在不在",不是"脚本跑不跑"(同一假绿模式)。

三、修复与固化

  1. 我引入的那处 → .join(String.fromCharCode(10));
  2. 助手脚本 8 处 → 补成 \\n(只补未转义的);
  3. ⛔ 新增 scripts/verify-inject.cjs 并接入 npm test:先模板求值成运行时字符串 → node --check, 并断言运行时串无反引号 / ${。⇒ 这类假绿再也不可能溜过 npm test。

四、触发面补全("页面没反应"的另一半,实测证明)

  • 实测:用户一直盯着页面 + 连接悄悄断 → 原设计一个事件都不会来(只有切标签/focus/pageshow 才探活)⇒ 既不提示也不恢复。
  • 补两条静默触发面:① 页面可见时每 25 s 心跳;② 包装 EventSource/WebSocket 的 error/close。 两者走新 soft 模式:连续两次失败才恢复(恢复=原地 location.replace,会丢未保存输入,一次抖动不该触发)。
  • ⚠️ 推翻了档案 77 §五 方案 B「不做周期性探活」(原前提"用户在场却不操作没有收益"被现场证伪)。

五、验收(真 Chrome + 临时会话,只读)

项 结果
修复后基线 ✅ __dshRecover=true、__dshAssist=true、语法类错误 0(助手脚本首次真正运行)
事件路径回归 ✅ pageshow(persisted) → 浮层出现、class=__dsh-ov、新视觉节点齐
无操作自动恢复 ✅ 只打挂实例侧探针、不派发任何事件 → 47.4 s 后浮层自动出现(两次心跳判定),0 页面错误
npm test ✅ 45 pass / 0 fail / 1 skipped + 新增注入脚本校验双通过
部署 ✅ 两轮热修 PID 415625→416910→417427;备份 …bak-before-hotfix-0825 / …bak-before-triggers-0835

六、文档 / 技能 / 锁

  • 档案 77 追加「现场故障 + 热修 + 触发面扩展」;档案 56 追加「修正」节(其注入脚本一直没跑起来,原验收结论需重判);INDEX 04-77 行补记;四件套 rc=0;对账 一致 130。
  • dsh-change-workflow 技能 v2.8.0 → v2.9.0:① 纠正三行过期规则(原文写"服务器是文档唯一源 / 禁止本地改 / docs-sync-check.sh 已删"——与现行协议相反)② 新增「注入脚本两条铁律 + 两条浏览器验证路径(playwright-core 独立无头 vs browser-harness 附着用户 Chrome)」。两副本 md5 一致 + scp。
  • 双锁已释放(op-lock 释放需带 ME=<原占用者>)。

08:41 清点「还有哪些待执行」(只读取数,未写库)

  • 读的单一来源:交接单/README.md §一(已规划待执行)+ 03-路线图 §二 进行中/待办 + 各档案挂起清单。
  • 顺手核销一个挂起项:档案 59 的「wake.html 本机 4708B vs 服务器 4591B 待核对同步」→ 实测双端一致 (本机/服务器均 4962 字节、md5 同为 00b81728…;变大是因为档案 78 今天给 wake.html 加了熔断文案)⇒ 该挂起项可关闭 (待持锁会话释放后回填 03-路线图)。
  • 当前持锁者:t03-ledger-0841(08:42 起持全局执行锁,在做 T03 台账)⇒ 本会话此刻只能读、不能写文档库。
  • 结论清单(见当日回复):待执行 = T01(待认领)|T03 剩 guest 启用(用户动作)+ 验收归档|档案 76 的 400(P1 待查)| 档案内挂起(57 四项 / 66 两项 / 68 / 42 / 38b / 15 / 32)|P3 门户浏览文件加下载入口|触发式(dsh 升级回归 / 会话 GC)| 我自己待补 = 档案 78 L1 熔断实测(要窗口)· 档案 56 面板真机验收(原验收是假绿)· 档案 76 的 400 复现 · 自动化「遗留项自动推进」重建(待授权)。

08:41–08:5x 会话 48a9c14c:盘点「还有哪些方案待执行」+ 线上只读核对(账实相符)

一、★新取证:实例数据根与 profile 的真实路径(此前只知"有 profile",不知确切位置)

  • 实例数据根 = /var/lib/dshs/users/<user-uuid>/ —— ⚠️ 不是 /opt/dsh/users/(后者只有平台账号 main;/opt/dsh/users/main/.dsh/profiles/web/ 是平台账号级 profile,其 bundles 只有 dsh-base + dsh-web-app,别拿它当实例的)。
  • 实例 profile = <数据根>/home/profiles/web/package.json,看 dsh.profile.bundles 数组 = 该实例实际加载的插件清单。
  • 当前两个实例:admin uid 114801 = cce6d1cd-b376-4304-80f0-0e1c58c9ffde;guest uid 100002 = 4092b965-2f68-4977-9989-68b3966f7df0(另存在 uid 100003 / 100004 / 196490 的账号,未起实例)。
  • 候选池目录 = /var/lib/dshs/business-plugins/(09-13 00:02 更新);平台 DB = /var/lib/dshs/dshs.db。
  • 实例进程命令行(从 scope 可见):setpriv --reuid <uid> … dsh --profile web --host 127.0.0.1 --port <N>,并 ro-bind 了 home/profiles/web/{cordis.patch.yml,package.json,pnpm-lock.yaml} ⇒ 这三份是实例侧真相源。

二、T03 实测(08:4x):admin 已启用、guest 未启用 —— 与文档记载一致

实例 bundles 末项 mcn-suite
admin …@dsh-local/workspace-scoped-picker, dsh-plugin-mcn-suite ✅ 已在 bundles
guest …@dsh-local/workspace-scoped-picker, dsh-univer-office(0.2.15) ❌ 未含

⇒ T03 只差「用户在 guest 实例「功能管理」里启用」这一步(用户动作,平台不代劳)→ 再重启实例 → 跑 §八验收 → 归档。

三、待执行清单现状(三处单一来源已对齐)

  • 交接单 §一:T01(⏳ 待执行,决策点 1 已定 = A 扩展 business-plugins)|T03(🔄 进行中,见上)
  • 03-路线图 §二:档案 76(P1 待查 = /univer-api/state 持续 400,需取响应体)|档案 78 遗留 L1(故意反复搞崩验熔断,需维护窗口)|档案 27(暂缓)|档案内挂起一批(57 四项 / 59 wake.html 字节差 / 66 三项 / 68 / 38b 三项 / 15 / 32)|P3 门户「浏览文件」下载入口|触发式(dsh 升级回归 / 会话 GC)
  • 已消解或明确不做(免得重复问):档案 65 部署 ✅(本就生效)|档案 78 部署 ✅(08:02)|AnySearch ✅ 放弃|T05 ✅|@liustack/modlens ✅ 已无残留|Cookie 域收窄 ❌ 不做|管理类插件化 ❌ 不做|白名单源码安装 ✅ 关闭

四、只读核对附带发现(只报告,未动)

  • 文档库 33 项未提交(末次 commit da9d1ff,09-12 17:45)—— 期间所有改动都在工作树里。
  • 服务器 op-lock 空闲(仅 README);dshs active;两个实例 scope 均 running;平台 DB 08:30 有写入。

08:41–09:05 会话 a2665dd3(锁名 t03-ledger-0841 → fix79-0851):待办盘点 + 档案 79 落档与代码修复(已 build,待重启生效)

一、本轮做掉的(全部已 scp 到 /opt/dsh/docs,档案 600 / README 644)

# 动作
1 04-67 文末追加 v0.2.7 / v0.2.8 段(内存预估 + 页内弹窗,含触发原话、口径、部署与线上自证)
2 新建 04-79「插件启停的两处平台缺陷」:现象链(journalctl)→ D1/D2/D3 → 连带后果(半应用态)→ 修法已备 → R8 影响
3 INDEX.md 登记 04-79(🔴)|03-路线图 §二 加 🔴 P0 待修 行
4 交接单/README §一 T03 行:补「07:4x 启用失败 → 已手工回滚 profile → 实例恢复」+「前置 = 先修档案 79 + 解决堆容量(不改档位)」
5 四件套:audit / manifest / consistency rc=0;对账 仅服务器 0

二、★档案 79 的代码修复已落地(src/web/routes/business-plugins.ts,已 build,未部署)

  • D1(P0):try { uninstall(sel.id) } catch {} → await uninstall(sel.id)(+ 三行注释说明为何必须 await)
  • D2:['remove', id, '--ignore-workspace-root-check', …] → ['remove', id, '-w', …](add 的该 flag 合法,未动)
  • 加固:外层 void (async () => {…})() 补 .catch() 双保险(按 taskId 从 tasks 取回任务表项 —— task 是 IIFE 内局部量,第一次写错被 tsc 抓出 TS2552)
  • 验证:tsc rc=0(npm run build 成功)|产物 lib/web/routes/business-plugins.js 三处标记齐全(await uninstall×2 / 'remove', id, '-w'×1 / apply task crashed×1)|npm test 45 pass / 0 fail / 1 skipped(含注入脚本文本校验 ✓)
  • 未部署:生效需 systemctl restart dshs = 中断在线用户数秒 ⇒ 已作为 R8 问题请示用户。

三、踩坑(已写进 dsh-server-docs/CODEBUDDY.md 同步链路)

scp 到文档镜像必须带「同一相对目录」:scp 04-调整方案/X.md bt-server:/opt/dsh/docs/ ❌ 会落到 docs 根目录(对账报「仅服务器」);scp 交接单/README.md bt-server:/opt/dsh/docs/ ❌ 会覆盖根 README.md(本次实际发生,已从本机重传复原)。✅ scp <相对路径> bt-server:/opt/dsh/docs/<同一相对目录>/。 另:文件名别带空格(scp 远端路径经远端 shell 解析会被拆成两个文件 —— 本次 79-…pnpm flag.md 就中招,已改名去掉空格);远端 chmod 也要给文件名加引号,否则报 cannot access 而误判没传上去。

四、仍待办(详见给用户的清单)

改档位(用户已定暂不做)|提交/推送(文档 137 / 代码仓 24 项未提交)|T03 guest 启用(前置=容量+修 79)|univer 端到端复测需重建 .univer|档案内挂起项(57/59/66/68/42/38b/15/32)|T01|工作区历史临时探针清理。

08:50–09:40 会话 a2665dd3:univer 剩余问题的根因(worker 侧没 socket 支持)+ 已打补丁并构建 0.2.16

用户报:「guest 最新会话显示 univer 插件功能还是有点问题」。

一、根因(来自实例内 agent 自己的取证 + 我的代码核对)

  • 报错原文:Error: gatewayOrigin must be an HTTP origin without credentials or a path
  • agent 的核对输出:插件版本: 0.2.15|宿主 socket 支持: 3 处|worker socket 支持: 0 处|socket: …/tmp/dsh-univer-gateway-2.sock(08:42 存在)
  • 代码链:resolveTarget() 把端点原样塞进 worker 请求 → worker requiredHttpOrigin()(src/workers/unit-content/entry.ts:454-467)只认标准 HTTP origin ⇒ socket 模式下传的 unix:<path> 被拒。
  • ⇒ 这正是档案 76 里我标注的"已知限制":宿主侧我改了 3 处,worker(unit-content-worker.mjs,截图/内容类操作走它)没改,而它只能走 TCP loopback —— 被 nft 封。
  • 附带:socket 文件存在 ⇒ 宿主侧改造是通的(不是白改)。

二、补丁(v0.2.16,5 处,全部加法式;TCP 路径不受影响)

文件 改动
src/host/adapters/unit-content/protocol.ts UnitContentWorkerTarget 加 readonly gatewaySocket?: string(宿主↔worker 内部字段)
src/host/adapters/unit-content/worker.ts spawn 时 unitContentWorkerEnvironment(request.gatewaySocket);该函数把已解析的 socket 路径写进 env(auto 含 pid,不能让 worker 自己重算)
src/host/provider/unit-content-operations.ts resolveTarget():socket 模式下把 gatewayOrigin 换成合成 origin http://unix(过 worker 的 HTTP 校验)+ 传 gatewaySocket
src/workers/unit-content/entry.ts main() 首行 installUnixGatewayFetchShim():把 http://unix/... 的 fetch 改走 unix socket(复用 shared/unix-http.ts 的 requestOverUnixSocket)
  • package.json 版本 0.2.15 → 0.2.16。

三、踩坑(可复用)

  • 多行锚点在 CRLF 文件里必须写 \r\n:本仓库文件是 CRLF,读用 newline=""(保行尾)时,含 \n 的多行锚点命中 0;单行锚点不受影响。(本次先踩后修)
  • 联合类型取值:request.gatewaySocket 在 UnitContentWorkerRequest(联合)上会 TS2339 ⇒ 改 'gatewaySocket' in request ? … : undefined。
  • import 相对层级:src/host/adapters/unit-content/ → shared 是 ../../../shared/(少一级会 TS2307)。
  • npm run build | head -N 会因 SIGPIPE 把构建打断;长构建要重定向到文件 + 后台跑。

四、状态

tsc 对 4 个改动文件零错误;npm run build 后台执行中(worker 产物 artifacts/unit-content-worker.mjs 已重建)。未 pack、未部署。

09:22 三问核实(T03 启用什么 / 档案78 实测什么 / 自动化是什么)—— 只读取数

  • T03 待启用 = dsh-plugin-mcn-suite(池内 0.3.9,1.95 MB);实测现状:admin bundles 已含、guest 不含; 且 guest 的 node_modules 里连旧的 7 个自研插件也没有(无 mcn/liustack/dsh-plugin 命中)⇒ 对 guest 是首次启用,不是"切换下架旧包"。 启用路径(档案 60/67 措辞):guest 登录 → 实例设置面板 →「功能管理」→ 勾选卡片 → 「应用更改」→ 弹窗「确认应用」(会 restartAndProbe,重启该实例数秒)。
  • 档案 78 实测参数:熔断阈值 DEFAULT_CRASH_MAX_RESTARTS=5 / DEFAULT_CRASH_WINDOW_MS=600000(10 min);冷却 10 min、封顶 6 h; /var/log/dsh-crash-breaker.log 当前 0 行(尚未发生熔断)⇒ 实测要新造 6 连崩才能看到 503 + 告警 + 冷却。 ★ 干净做法:按技能 R4 的专用测试账号模板(role='active'、approved_by=真实 admin id)建一个临时用户来做, 零影响你自己的账号(用 admin 做会让 admin 被冷却 10 分钟);用完删号。
  • 自动化「遗留项自动推进(DSH 平台)」(1e1db4eb-…):每 8 h;cwds = D:/AI技能/aliyun-dsh-server(迁移前旧路径)⇒ 停止调度; 最后记录 = 09-12 14:54 run → failed: unknown_interrupted。 ⚠️ 原始 prompt 读不到:~/.workbuddy/workbuddy.db 的 automations 表只有 4 条 8 月的老测试自动化, 这条与「代码仓三方同步」都不在表里(配置未落在任何可读位置)⇒ 只能靠名字/频率/项目记忆复原,要重建必须用户给口径。 对比口径:「代码仓三方同步(DSH)」每 3 h 仍在正常跑(cwd 未变),其记录显示它只报告、零改动, 且被 lock-guard 钩子拒写(lock-hook.log 有 PreToolUse-deny Write …automations\5840ce59…memory.md)—— 即自动化会话改了 cwd 也不会被钩子放行。

09:17–09:45 会话 a2665dd3:univer 0.2.16 已构建 / 打包 / 投放 / 装进 guest(只差重启生效)

过程:

  1. 构建:第一次前台跑被我自己的 timeout 900 掐死(09:06:58 日志停止;教训:长构建不要自加 timeout,用后台)。第二次后台跑时任务丢失,但产物已写出 → 用「index.html 引用完整性」判定:viewer 引用 6 个资产缺 0(assets 153 文件)、render-machine 引用 4 个缺 0(32 文件)⇒ 产物完整可用。
  2. 打包:pnpm pack 报 pnpm 内部错(detect-libc os error 2)⇒ 改用 npm pack(files 字段两边都认)→ dsh-univer-office-0.2.16.tgz(42,045,215 B / 279 文件,md5 d8f044d77390bb24fc6c97e45da6beae)。核验:包内 lib/index.js 与 artifacts/unit-content-worker.mjs 都含 http://unix ✓、版本 0.2.16 ✓。
  3. 投放:走门户 API POST /api/plugins/business(admin)—— ⚠️ 经 Cloudflare 直传 56 MB base64 得 502 Bad Gateway(CF 挡了);改从服务器本机 http://127.0.0.1:3080 + -H 'Host: alotbuy.com' 重试 → http=200、version 0.2.16、replaced:true、池文件已换(42,045,215 B / 09:36)。临时 admin 会话用完即删(deleted_sessions=1)。
  4. 装进 guest profile:setpriv --reuid 100002 … pnpm add -w file:…/dsh-univer-office.tgz(与既有部署同姿势)→ node_modules/dsh-univer-office 已是 0.2.16、worker 产物带垫片、bundles 仍含 univer ✓。
  5. 清理:本机(_uv_upload.sh/_uvbuild.log/_sess_univer.cjs)与服务器(/tmp/uv-0.2.16.tgz 等)临时文件全清。

唯一未做 = 重启生效(两处都卡在这一步):

  • guest 实例需重启 → 才会加载 0.2.16(client/plugin 在实例启动时加载);
  • 平台需重启 dshs → 才会加载档案 79 的修复。 ⇒ 已作为 一个 R8 窗口问题报给用户:重启一次服务可同时让两处生效(影响:全体在线用户断约 4–10 秒;之后实例下次访问自动拉起)。

仍未验证的边界:worker 内 Univer 的协同客户端若用 WebSocket(ws://unix/...)——本次只补了 HTTP 路径;重启后实测即可定论。


09:39–09:50 三问答复后的取证:T03 的真卡点 = 内存预算(不是"用户没点")+ 自动化已被用户删除

一、用户三答

  1. T03 启用「已经试过了没问题」;2. 档案 78 实测「也试过了没问题」;3. 自动化「我删除了,会和执行任务冲突」⇒ 勿重建。

二、⭐ 现场取证(只读)——查出 T03 卡住的真原因

时间 事件 证据
07:42:52 guest 在「功能管理」提交启用 mcn-suite POST /api/plugins/mine/apply(任务 c8ac2686539aeb7c)
07:43–07:45 实例连崩 5 次 exitCode=134(SIGABRT);栈 = V8 JsonParser + HeapAllocator::AllocateRawWithRetryOrFailSlowPath ⇒ V8 堆打满 abort
07:46 熔断(旧代码,JSON 里无 opens/cooldownUntil) journal [crash-restart]
07:45 / 08:31 平台自动回滚 → guest 现未启用 package.json.bak-rollback-20260913T0748;pnpm-lock/cordis.patch 0 命中
— 实例 env 实测 NODE_OPTIONS=--max-old-space-size=160(档案 74 的下调在生效)
— cgroup 也贴顶 dsh-instance-mem.log:guest 峰值 382/384 MiB(01:30 触发 91% 告警)
— 退出码分布(09-12 起) 1 ×21(插件/配置)、134 ×8(V8 堆)、137 ×1(cgroup OOM)
⇒ 结论:160 MiB 是在空闲实例上调的,对"重插件 + 大 JSON"的真实负载不够。要让 T03 可用必须先加内存预算:
--max-old-space-size 160→256(回平台默认)+ cgroup MemoryMax 384→512 MiB(否则只是把 abort 134 换成 OOM 137)。
两处都由平台进程启动时读取 ⇒ 须重启 dshs(R8,2–5 s)。

三、档案 78 的 L1 澄清(与用户的"试过了"对账)

  • 新熔断代码尚未被触发:/var/log/dsh-crash-breaker.log 0 行、journal 无 crash-breaker-* 事件;
  • 用户今早看到的"熔断"是 07:46 的旧代码那次(guest 真连崩 5 次)⇒ 若用户指的是浮层/自愈,那是档案 77 的(已验);熔断拦截本身仍待实测。

四、顺手核销

  • 档案 59 wake.html 挂起项:双端一致(4962 B、md5 00b81728…)⇒ 可关闭。

五、⚠️ 文档回填被锁挡住(R9)

  • 持锁者 opensource-export-0913(09:41 起,未声明单号)⇒ 我停止写文档库;回填脚本已备好 _patch77/docs-mem.py(档案 74 追加「现场反证」+ T03 卡点更正 + 03-路线图 关 59/加"内存预算待窗口")——锁释放即执行。
  • 未 commit / 未 push。

09:47–09:55 T03 §八 只读验收(服务器实测,8/14 通过)

用户第二次表态「我都试过可以了」⇒ 不再追问,改为用现状核对 + 只读验收。 结论(admin 侧,自 00:02 起启用): | 通过 ✅ | A 包结构(v0.3.9 / md5 2ff1a2fa… / 顶层仅 package、node_modules 0、.bak 0)|B 上传扫描 blocked=0|C 池内 0.3.9|E [mcn-suite] loaded + duplicate=0|H 池内只剩 2 个包(旧 7 包已下架)|I 技能落 <home>/skills/mcn-short-video(4.6 MB / 4 个一级子技能)|K skills ensured: 跳过@0.3.0(版本一致)|M BRIEF 384M | | 未验 ⚠️ | D 入口逐个点开(需浏览器)|J agent 按名加载|L 凭据扫描|N 死引用扫描|P/Q 撞名三场景|F 探活(但平台已自动回滚过一次 ⇒ 链路活)|G 内存偏紧(guest 峰值 382/384)|O 口径待核:实测 18 个 SKILL.md vs §八 期望 36 | 两条自我纠错(先看错、复核后翻案):① 技能落点是 home/skills/(不是 home/.dsh/skills/)⇒ I 通过;② 内存采样日志时间戳是 UTC("01:40"=本地 09:40)⇒ 采样没停。 guest 那次尝试的事实:07:42:52 启用 → 07:43–07:45 连崩 5 次(exitCode 134 / V8 堆)→ 07:46 熔断 → 平台自动回滚 ⇒ guest 至今未启用。 回填脚本备齐待锁:_patch77/docs-mem.py(74 反证 + T03 卡点 + 03-路线图)→ _patch77/docs-t03-accept.py(T03 §九 验收表 + 03-路线图 T03 行)。锁仍被 opensource-export-0913(09:41 起)占用。

09:41–09:55 会话 a2665dd3:重启已执行(用户批准)—— 两处修复生效;univer 通道修好了,但撞上内存墙

一、重启动作(持 op-lock restart-0942,已释放)

  1. 先只读 diff 服务器那份 business-plugins.ts → 差异只有我这 3 处补丁(确认服务器 = 我的改前基线,不会覆盖别人的本地改动)✓
  2. tr -d '\r' 转 LF 后 scp(服务器侧先备份 .bak-0942)→ 服务器 npm run build → lib/web/routes/business-plugins.js 标记齐全(await uninstall×2 / apply task crashed×1;ignore-workspace-root-check 只剩 1 处 = add 那处合法保留)✓
  3. systemctl restart dshs → active(新 MainPID 424413 / 09:44:57)✓

二、univer 0.2.16 验证(guest 实例侧,临时会话 R4,用完即删)

  • POST /api/dsh/enter → 实例拉起(scope dsh-100002-8ee7ec3c active、dsh web: …:42307)✓
  • GET /univer-api/status → 200 {"gateway":{"phase":"stopped"},"unitContent":"bundled"} ✓
  • ★ socket 通道在生产成立:POST /univer-api/gateway/start → gateway 进程真的被拉起(pid 424934 = …/dsh-univer-office/artifacts/gateway.cjs)⇒ 0.2.16 的 worker/host 改造与 socket 传输在生产可用(不再是"通道不通")。
  • ⚠️ 但立刻撞上内存墙(D3 复现):gateway 加载期间实例 cgroup 冲到 383.9 / 384 MiB(/univer-api/status 停在 starting)⇒ 384 MiB 装不下「dsh 本体 + univer 插件 + univer gateway」。
  • ★ 我随即回收了我自己触发的那个 gateway(pkill -u 100002 -f artifacts/gateway.cjs → 剩余 0)→ 实例存活、近 2 分钟无 OOM/无 crash-restart ✓(测试残留必须自己清)。

三、结论(性质变了)

  • 通道问题已修(0.2.16 有效:host + worker 都走 socket);
  • 剩下的唯一阻碍 = 内存配额(用户档位约束:"暂不改档位")⇒ 要真跑通 univer,需要三选一:调档位(384→512 / 堆 224) | 只装 univer 不装 mcn-suite(省 39 MiB,但 gateway 需求可能更大) | 给 univer gateway 瘦身/懒加载(改造工作量最大)。

四、顺带观察

外网扫描器 198.251.85.116 在探测 /.git/config、/.git-credentials、/.git/config.bak 等 —— 平台均返 404,无泄露迹象,但值得知道(这类探测会持续)。

09:55 追加:墙的量化 —— Univer 的 gateway 单进程 ≈ 350 MiB

  • 回收 gateway 后重测:活跃 scope dsh-100002-8ee7ec3c(09:45:43 起、同一进程 424519、Tasks 13)只占 27.9 MiB(rss 10.3 / cache 15.4)。
  • ⇒ 带 gateway 时 383.9 MiB 与不带时 27.9 MiB 相减:Univer 的 gateway.cjs 单进程约 350 MiB —— 远大于此前"import 插件 +63.7 MiB"的估算(那是插件宿主的加载成本;gateway 是另一个完整服务进程,内含 Univer 协同运行时)。
  • ⇒ 结论定死:本平台每实例 384 MiB 的规格在物理上装不下 Univer(27.9 + 350 ≈ 384,且网关尚未完成启动)。这不是传输/代理问题(那层已修好并验证),而是容量问题。
  • 实例与服务当前均健康(页面 200、注入脚本在、无 OOM)。

09:51 用户裁定(关键意图):univer 是他有意保留的「内存压力探针」

  • 用户原话:「C 我就是用它来检测 dsh 服务内存压力的」⇒ 选项 C(留着不管)不是将就,而是目的: 用 dsh-univer-office 这个重型第三方插件去压实例内存,观察平台在什么条件下撑不住。
  • ⇒ 行为约束(已写入 MEMORY.md):任何会话不得以「撑爆实例 / 打不开 / 兼容性差」为由下架或禁用 univer; 要把「实例被压到 384/384」当预期结果看,而不是当缺陷修。
  • 它为探针提供的价值:可复现(gateway 懒启动,只在真用表格时才压)、量级已知(gateway ≈350 MiB)、 边界清晰(383.9/384 顶格;平台侧 crash-restart + 档案 78 熔断冷却负责兜底)。
  • 已备探针脚本(可复现配方):_probe_instance_mem.sh(本机草案)→ 待文档锁释放后落进 dsh-server-docs/scripts/probe-instance-mem.sh。 动作:铸临时 guest 会话 → enter 拉起 → POST /univer-api/gateway/start → 逐秒采样 cgroup(usage/max_usage)→ 回收 gateway → 清理会话 → 输出峰值汇总。
  • ⚠️ 文档库当时被 opensource-export-0913 持锁(09:41 起)⇒ 按 R9 未动文档;BRIEF §3 / 03-路线图 / 档案 76 的对应措辞(「univer 是探针,勿下架」)待锁释放后补。

09:36–10:0x · 会话 opensource-export-0913:开源导出一份「无本机/服务器信息、无已投放插件」的代码副本

产物:E:\ProgramData\AIProject\ai1net-dsh-server\_开源导出_20260913\

  • dshs\ = 仓库根(139 文件 / 1.2 MB),可直接 git init 推送;未做 git 初始化/提交/推送(按 §4)。
  • _导出说明与脱敏台账.md = 给人看的清单(不随仓库上传):脱敏台账、发布前占位符清单、已验证项、可选后续。
  • _build_export.py = 复制+脱敏脚本,带安全闸(默认拒绝重跑,需 --force,否则会抹掉手写的 README/LICENSE/install.sh)。

做法(可复用):

  • 源仓库 D:\github\dsh_shenxian 全程只读;改动一律落在导出副本上。
  • 字节安全的脱敏:Python 按行 splitlines(keepends=True) 处理,完整保留 CRLF;抽样 cmp 验证未改动文件逐字节一致。
  • 收尾用阻断性探针断言(域名/IP/账号//opt/dsh/插件名/MCN/凭据关键字)0 命中。
  • 编译验证:导出目录临时 New-Item -ItemType Junction 软链源仓库 node_modules → tsc --noEmit 退出码 0 → 删联接。

按需求落实:

  1. 脱敏:alotbuy.com→<baseDomain>(web/wake.html 改成运行时从 location.hostname 推导注册域,彻底去硬编码);47.77.182.89/172.18.16.212→占位符;/opt/dshs→<INSTALL_DIR>(require('/opt/.../better-sqlite3')→require('better-sqlite3'));/opt/dsh/{state,artifacts,backups}→/var/lib/dshs/*(上游文档一致的数据根);maogeigei→占位符。
  2. 去掉已投放插件(严格口径):删 poc/business-plugins/(功能管理分区插件 + 全部 tgz)、poc/workspace-scoped-picker/、以及 6 个插件投放脚本(ensure-anysearch-*、ensure-biz-plugins、ensure-portal-entry、install-workspace-picker.sh、provision-new-users.sh);并把代码注释里的插件名(anysearch / modlens / univer-office)改为中性描述。MCN 工作台与 skill 本就不在代码仓,另经探针确认 0 命中。
  3. 授权文档:LICENSE(分层授权:个人/非商业免费 + 商业需授权)+ LICENSE-UPSTREAM-MIT.txt(上游 MIT 原文,必留)+ THIRD-PARTY-NOTICES.md(DeepSeek Harness = MIT 商用无限制、不分发其代码;6 运行依赖 + 4 开发依赖 + 178 传递依赖全部宽松许可)。
  4. README.md 按主流开源结构重写(徽章/目录/特性速览/快速开始/功能详解/架构/部署形态/配置/开发/版本与迭代/安全/目录结构/FAQ/贡献/授权/致谢);版本表登记 v1.0.0 = 首个公开发布,并单列「相对上游 dshs 的六组改造」。
  5. 不带项目文档与 skill:删 docs/(上游 7 篇)、STANDARD.md、poc/README.md、.workbuddy/、lib/、.git(历史里全是内部信息,必须全新仓库)。
  6. 新增 install.sh 一键部署(模式 A):预检 → 装依赖并构建 → 装 DSH CLI → env(0600) → 数据根(0700) → 证书 → nginx → systemd → 首个管理员 → 健康检查;带 --dry-run / --uninstall [--purge] / --isolation / --port-guard / --install-node,幂等。

⚠️ 两个必须交代的前提:

  • 法律前提:上游 dshs(上游作者(已按要求不再具名))是 MIT,MIT 不允许对上游代码加限制 ⇒ 只能做分层授权(上游留 MIT,只对本项目新增部分收商用费)。若 上游作者(已按要求不再具名) 就是用户本人,可整体简化为单一授权 —— 已在台账里留作可选后续。
  • install.sh 未在真实 Linux 跑过(本机只有 Windows:仅 bash -n + --help + 逻辑审查)。建议先非生产机器 --dry-run 再真跑。

保留未动:代码注释里 183 处「档案 NN」内部编号引用(属内部档案号,非本机/服务器信息;已列入可选后续)。


09:53–10:0x · 同一会话续做:把开源规则沉淀成技能 dsh-opensource-release

用户要求:「把这个项目开源的规则 整理成 skill 迭代管理」。

产出:E:\ProgramData\.workbuddy\skills\dsh-opensource-release\SKILL.md(v1.0.0 / agent_created: true) 十节:① 何时用 ② 事实(硬编码路径/版本)③ 五条硬规则 R-O1–R-O5 + 两条纪律 R-O6/R-O7(含「源仓库只读」「阻断性探针 0 命中」「不得被重建抹掉」)④ 保留/移除清单(delta,含「本就不在代码仓」的 MCN/插件清单)⑤ 脱敏映射表(唯一口径) + 发布前占位符清单 ⑥ 保留未动的 183 处档案编号 ⑦ 授权结构 + MIT 硬前提(分层才合法;上游作者(已按要求不再具名) 若为本人可简化)⑧ 迭代发布 SOP(抢锁 → 基线 → 改口径 → --force 重建 → 改版本三处 → 六项验证 → 推送)⑨ 验证六件套(探针 / cmp / CRLF / tsc-via-Junction / bash -n / 移除项核对)⑩ 8 条实测坑。

顺带把构建脚本升级为「可安全迭代」:_build_export.py 新增 OVERLAY 手工撰写层(README / LICENSE / LICENSE-UPSTREAM-MIT / THIRD-PARTY-NOTICES / install.sh)—— --force 重建前先快照到 _overlay/、重建后自动恢复;实测重建后 5/5 逐字节一致,探针仍 0 命中。此前 --force 会抹掉手写文件(真实风险,已消除)。

⚠️ 被锁挡住的部分(按 R9 未抢、未接管):补「归档副本 + INDEX 登记 + manifest 刷新」时,文档库全局锁被 清理文档债-0955(09:54 起)占用 ⇒ 只读确认后跳过这一组步骤,继续做完其余不冲突的工作。待办已写进技能 §10(含逐步补做清单)。另有既有漂移一并登记:dsh-instance-diagnose 无归档副本、INDEX.md 未登记 dsh-knowledge-upkeep —— 未擅自修。


10:0x · 同会话续做②:univer 转为保留 + 新增《插件移植指南》+ 技能归档补齐

用户指令:「univer 可以保留这个插件 专门讲讲如何把开源插件改造为可在多租户平台运行」。

A. 撤回 univer 的脱敏(只撤 univer,其余插件仍中性化)

  • _build_export.py:删掉 dsh-univer-office / DSH_INSTANCE_UNIVER_SOCKET / UNIVER_DSH_GATEWAY_SOCKET 三条 GLOBAL 规则;删掉 orchestrator 的 6 行 DELETE_LINES(unix-socket 适配段回归);whitelist.ts 注释改写为「(实测:多个第三方插件均如此,例如 dsh-univer-office)」。
  • LEAK_PROBES 移除 dsh-univer / UNIVER(刻意不在列)。
  • 重建后确认:src/supervisor/orchestrator.ts 的 UNIVER_DSH_GATEWAY_SOCKET 段回来了;阻断探针仍 0 命中(档案/交接单 属有意保留)。

B. 新增 PLUGIN-PORTING.md(本次主交付) —— 面向插件作者与平台维护者,七节 + 附录: 0 结论速查表 · 1 托管平台 vs 本地的六条假设差异 · 2 六个失败模式 H1–H6(绝对 URL[阻断级] / loopback 依赖 / 自建监听 / 客户端拼地址 / 平台包不兼容 / 重型依赖静态 import,各附症状+判定) · 3 五条改造规范 R-a~R-e · 4 dsh-univer-office 完整实战(两层独立阻碍 Host→Gateway 与 Browser→Viewer、为什么换服务器 IP 不行[3 条硬理由]、改造清单、3 条设计决策[socket 是可选传输/只有两条代理规则/两个平台侧机制坑]、不碰生产的验证法、已知限制、回滚) · 5 平台侧需配合的能力 · 6 上架自查清单 · 7 提上游的论证口径 · 附录 可粘自检命令。 素材来源(只读):内部 04-调整方案/75(H1–H4 判据 + R-a~R-e + 实测内存基线)与 76(univer 改造全过程);已去掉全部内部档案号与服务器信息。 README 加「插件移植指南」章节 + 目录项;① 组改造表补「插件 unix-socket 适配通道」。

C. 把上一轮被锁挡住的活补完(10:01 锁已释放,重新 --claim-exec 后完成,完工已 --release-exec)

  • 归档副本:dsh-server-docs/skills/dsh-opensource-release/SKILL.md —— 两副本 md5 一致(b0986201…)。
  • INDEX.md:§一 场景表加「开源导出 / 发新版本」行;§二 清单表加 skill 行。
  • 四件套:docs-manifest.py 刷新 ✓ | docs-audit.py exit 0(无 P0) ✓ | docs-consistency.py exit 0 ✓ | docs-index-stats.py --write 刷新摘要行 → 复跑 exit 0 ✓。
  • 未 scp(按 §4,等用户发话);docs-sync-check 如实显示「仅本地 2 / 内容不一致 5」的既有待推状态。

D. 技能迭代到 v1.0.1(示范「迭代管理」):更新 R-O3、脱敏映射表(新增特许保留项小节)、OVERLAY 增 PLUGIN-PORTING.md;tsc 验证法改为跑 _verify_tsc.mjs,并加硬警告:绝不可 recursive 删联接(Windows 下会顺着联接删掉源仓库 node_modules)—— 本次已实测 tsc exit = 0、junction removed = true、源仓库 node_modules 164 项完好。

导出物现状:140 文件;手工撰写层 6 个(README / LICENSE / LICENSE-UPSTREAM-MIT / THIRD-PARTY-NOTICES / PLUGIN-PORTING / install.sh),重建时自动快照→恢复。


10:0x–10:2x · 同会话续做③:确认上游是三方的 + 项目改名 dsh-hosting + 正式致敬

用户指令:「dshs 这个是不是引用的三方插件项目确认下,如果是不能直接使用 需要一个更符合这个项目定位的名称,并且在说明中致敬这个项目」。

A. 核实结论:是三方项目(不是我们能直接用的名字)

  • 本机 git:upstream = 上游骨架仓库(已按要求不再具名);192 个提交里 123 个是 上游作者(已按要求不再具名)(2026-08-18 → 08-26 建骨架 P1 auth/P2 desktop/P3 single-DSH/P4 每文件夹插件/P5 watchdog),我方最早 09-08(服务器侧 deploy)/ 09-11(maogeigei)。
  • 线上核对:GitHub 仓库存在;已被 DSH 插件目录收录(dsh.so/artifact/dshs,含 L1–L5 验证记录与 dsh plugin --profile web add github:上游骨架仓库(已按要求不再具名));另有第三方博客评测。授权 MIT。

B. 新名 dsh-hosting(多租户 DSH 托管平台)

  • 实测未被占用:npm registry.npmjs.org/dsh-hosting → 404;插件目录 dshbase.com/plugins/dsh-hosting → 404。
  • ⚠️ dsh-hive 已被占用(第三方插件 llluchy/dsh-hive,跨会话消息)—— 候选名已排除,别再捡(本次实测踩到)。
  • 改名范围(149 处自指标识):包名/bin/cordis plugin id、36 种环境变量前缀(DSHS_* → DSH_HOSTING_*)、/var/lib/dsh-hosting、dsh-hosting.service/.env/nginx conf、默认库文件 dshs.db → dsh-hosting.db、镜像/k8s/CI/Dockerfile、导出目录名。

C. 致敬(用户明确要求)

  • README 新增 「名称与渊源」 章节(本项目名 / 上游基线 上游作者(已按要求不再具名) / 为何改名 / 两类标识如何区分 / 上游仍按 MIT 且本项目改动不改变这一点)+ 「致敬」一句。
  • 致谢章节扩写;THIRD-PARTY-NOTICES.md §4 扩为「上游项目 / 作者 / 本项目 / 更名说明 / 保留要求 / 致谢」。
  • LICENSE-UPSTREAM-MIT.txt 逐字节未动;README 4 处 / LICENSE 2 处 / NOTICE 1 处出处引用保持旧名(不改)。

D. 这一步又暴露 3 个脚本漏洞(全部已修 + 重建验证)

  1. Dockerfile / Dockerfile.dsh 从未被脱敏 —— is_text() 按扩展名判断,二者无扩展名 ⇒ 整份跳过。
  2. package.json 的项目自有字段会被重建覆盖 —— 它属「源派生」而非 OVERLAY(version / description / repository / license 已改成 GLOBAL 规则)。
  3. ⚠️ 不要对 _build_export.py 自身做批量替换 —— 脚本里同时有「源模式」与「目标值」,盲替打断了改名规则(误伤 4 处,已逐条修正并重建验证)。这条已写进技能 §8 坑 9。

E. 最终验证(全绿):阻断探针 0 命中(新增自指残留探针 DSHS_ / dshs.{service,env,db});全仓 grep 'server-login' 只剩致敬与出处引用;tsc --noEmit exit 0(Junction 法,联接已拆);bash -n install.sh OK;导出物 140 文件。

F. 技能升到 v1.0.2:新增 R-O8 命名规则(不沿用三方名 + 实测占用 + 保留出处致敬;附「dsh-hive 已占用」记录)、§3 脱敏映射表加改名行、§8 补 3 条坑(编号 9–13)、路径全面改为 dsh-hosting;归档副本已同步(md5 09ed761c… 一致),四件套 exit 0,锁已释放。

10:0x 排障:「admin(Edge) 有浮层 / guest(Chrome) 没有」= 界面不同,不是浏览器问题

实测(真 Chrome 无头 + guest 临时会话,用完即删):

  • 服务器发给 guest 的注入脚本已是修复版(__dsh-ov/HEARTBEAT_MS/String.fromCharCode(10) 全在,坏特征 0);
  • guest 页面 __dshRecover=true、__dshAssist=true、0 语法错误;
  • 不做任何操作 → 46.2 s 后浮层自动出现(心跳 2 拍),新视觉节点在。 ⇒ Chrome/Edge 都是 Chromium,同一份脚本;差异不在引擎、不在账号。

真正原因(结构性)——两处界面对比:

界面 注入脚本 加载态
实例页 ✅ 有 「AI 核心」浮层
过渡页 wake.html(刷新/首次进入且实例不在时落这里) ❌ 无 旧的 border-top 转圈
guest 实例今早崩 5 次 + 被回滚 ⇒ 刷新时多半落在过渡页,自然"看不到浮层"。另:若那个标签页是 08:15–08:27(坏脚本期)打开的,刷新一次即恢复。

已做:web/wake.html 加载态统一成同一套「AI 核心」视觉(conic 扫描弧 + 反向虚线环 + 脉冲核心 + 三轨道粒子 + 渐变进度条 + 等宽小字),配色跟随平台主题(#534AB7/#8B7CF6,不硬套暗色)。

  • JS 逻辑一行未动(diff 只差档案 78 加的 instance_circuit_open 分支);行尾保持 LF;静态页免重启 ⇒ scp 即生效;
  • 线上取回验证:wk-orb/conic-gradient/prefers-reduced-motion/dsh · auto recovery 命中,旧 .wk-spin{width:34px} 消失;截图 _patch77/shots/wake-aicore.png;回滚 /opt/dsh/backups/wake.html.bak-aicore-20260913。

文档债已清(锁刚好空出):档案 74 追加「现场反证」、T03 追加 §九 验收表 + 卡点更正、03-路线图(关 59 挂起项 + 内存预算待窗口 + T03 主体通过)、INDEX、档案 77 追加「wake.html 统一视觉」节;四件套 rc=0,对账 一致 133(余 3 不一致 + 1 仅本地 = 其它会话积压);锁已释放。 诊断口诀(已写进档案 77):客户说"看不到浮层"→ 先分清是实例页还是过渡页,再看该标签页是不是修复前打开的旧页面。

10:02–10:10 「guest 一直显示恢复但起不来」根因定死:univer gateway 390MB > 实例上限 384MB ⇒ cgroup OOM kill

铁证(服务器实测):

RSS=390.4MB uid=dsh-eecc…(guest)  node /var/lib/dshs/users/4092b965…   ← univer gateway 单进程
RSS= 44.0MB uid=dsh-eecc…(guest)  dsh --profile web --port 43829                    ← 实例本体
  • 10:00 内存采样:guest 当前 384 / 峰值 384 / 上限 384(高位 100%);
  • 10:03:11 崩溃 = exitCode=137(SIGKILL = cgroup OOM kill);此前 8 次是 134(V8 堆 160MB abort)⇒ 两个天花板都在顶;
  • 页面侧:GET / 每 ~35s 一次(恢复→实例起来→又被杀→再恢复的循环);期间 fetch('/') 曾拿到 502 且响应体是 Cloudflare 错误页(命中 /cdn-cgi/styles/main.css)⇒ 502 是 CF 的(实例不在/正在起的窗口),不是 nginx 的(nginx error.log 无 502);
  • 页面还在 1 秒 1 次猛打 /univer-api/state(档案 76 那个返 400 的接口)—— 与 gateway 高占用同源。 结论:这是"内存压力探针"的必然结果 —— 用户 2026-09-13 明确「我就是用它来检测 dsh 服务内存压力的」⇒ 384MB 装不下 univer 这套。 恢复逻辑本身没问题(真 Chrome 实测:不操作 46.2s 自动出浮层)。 宿主余量:1870MB 总 / 1027MB 可用 ⇒ 单实例升到 640MB 可行,但 maxIdleInstances=4 必须降到 2(4×640 > 1870)。 待用户拍板(R8 + 资源承诺):(a) 升预算(cgroup 384→640、V8 堆 160→256,需一次重启 2–5s,我推荐)|(b) univer 瘦身(插件侧大改)|(c) 保持现状 + 把恢复做成有界(连续 N 次失败即停在明确失败态,不要无限转圈;也需一次重启上线)。

10:05–10:15 会话 a2665dd3:★找到并修好「插件无法启动」的真因 —— 陈旧 socket 挡住 bind(0.2.17)

用户报:「guest 最新会话 这个插件还是报错 无法启动」。 实例内 agent 当时看到的(session eda867a0 turn 7):无 gateway 进程、残留一个 08:42 的 socket 文件、宿主 dsh 已重启(pid 2 / 已升 0.2.16);univer_inspect → Error: bundled Gateway did not become ready within 10000ms。

一、根因(★真 bug,不是内存)

  1. 实测判定:只做一件事 —— 删掉那个陈旧 socket 文件,再 POST /univer-api/gateway/start → 立刻 ok:true,phase:"running" 连续 30s 稳定,新 socket 就位。⇒ 陈旧文件就是阻塞源。
  2. 机理:socket 路径 = join(tmpdir(), 'dsh-univer-gateway-<pid>.sock'),而实例在 PID namespace 里宿主恒为 pid 2 ⇒ 路径每次重启都一样;实例 /tmp 又是 --bind 到用户目录的持久挂载(不是 tmpfs)⇒ 上次 gateway 死掉留下的 socket 文件能活过实例重启。
  3. 为什么永久:bind() 到已存在的 socket 路径 → EADDRINUSE(Univer 的 gateway 未先 unlink)⇒ 之后每次启动都失败 ⇒ 对外只看到 did not become ready within 10000ms(10s 超时把真实 errno 吞了)。

二、修复(v0.2.17,1 处)

src/gateway-app/server.ts 的 listen():unix socket 分支绑前先 rmSync(target.socketPath, { force: true })(ENOENT 忽略)+ 补 import { rmSync } from 'node:fs'。 只重建 gateway(npm run build:gateway,秒级)→ npm pack(42,045,253 B / md5 16fd345b4d15fdec03285c80a9982b8a)→ 投放候选池(本机直连,http=200 / version 0.2.17)→ 装进 guest profile(node_modules 已是 0.2.17,gateway 产物含 rmSync ×4)。

三、修复验证(复现故障场景)

① pkill 掉在跑的 gateway → 故意留下陈旧 socket(10:06 那个);② 不删任何文件直接 POST gateway/start → ok:true → phase:"running" 在 t+5/10/15/20s 全部稳定 ✓;③ 新 socket(10:11)+ 1 个 gateway 进程 ✓。⇒ 0.2.17 修复成立。

四、附带事实与注意

  • 无需重启实例:gateway 是按需新起进程,下一次启动自然用上 0.2.17 ✓(这与 client bundle 必须重启实例不同)。
  • ⚠️ 内存仍在 383.7 / 384(gateway 热身后的稳态)—— 功能通了、但内存墙依旧,这正是用户要的探针现象(选项 C)。
  • 回滚:池内已被 0.2.17 覆盖;本地保留 dsh-univer-office-0.2.16.tgz / 0.2.17.tgz 两个包。