范围 = 本线(插件投放与分库线)产物 + 记忆类,共 27 件:
- 接续入口_插件投放与分库线(1 件):§0 新增 22:44 拍板行;§2 第 36 棒范围改 5 步、条件步转无条件
- 交付物(22 件):MCN 数据面接入阶段一/阶段二系列(含 B 案落地与 shadow 读数)、
P0 修通与移动端真机验收、pnpm-EPERM、两机 lib 差异、共享层台账语义、
基础插件身份与回滚、插件接入验收、插件数据面取数口、移动端迁包与字号扩面、跨机错误消息
- 记忆(4 件):2026-09-24 / 2026-09-25 日志、MEMORY.md、本棒 automation 执行记录
⛔ 未含他线在途改动(只报告、不代提交):机制层 CODEBUDDY.md / state.py /
.codebuddy/rules/server-ops.md、接续入口_IM线、接续入口_StoryForge验收线、
其余 29 个 automation 目录、docs/规则与载体/、归档/、接续包_*。
14 KiB
插件投放与分库线 · 第 23 棒(执行棒)落地件
工作区:
E:/ProgramData/AIProject/aliyun-dsh-server| 日期:2026-09-25 12:1x–12:4x 代码基线:D:/github/dsh_shenxian(生产口径dshs)/HEADe6207aa三件:① §5-12 跨机错误消息截断放宽(小改,已投两机)② §5-18「清理前必查」只读取证 ③ §5-22 本机lib↔ 部署面 3+7 差异定性 锁:插件投放与分库线-第23棒,域 =src/supervisor/test/remote-spawner.test.mjs/aliyun-dsh-server/接续入口_插件投放与分库线_20260922.md
一、第 1 件 · §5-12 跨机错误消息截断放宽(200 → 1200)
1-1 改了什么(一件文件 + 一个测试文件)
| 文件 | 改动 |
|---|---|
src/supervisor/remote-spawner.ts |
新增导出常量 AGENT_ERROR_TEXT_MAX = 1200(带口径注释:只放宽保留长度,⛔ 不动重试次数/退避,⛔ 不改 4xx 不重试语义)|两处 text.slice(0, 200) ⇒ text.slice(0, AGENT_ERROR_TEXT_MAX)(行 299/303 两个分支:4xx 具名抛与 5xx lastErr) |
test/remote-spawner.test.mjs |
新增 T6/T7/T8(+3 例) |
1-2 单测钉住的不变量
- T6 4xx:973 字符正文逐字完整进 Manager 侧
message(旧口径只留前 200)——正对第 17 棒的现场(真根因只能从 worker journald 补)。 - T7 边界:恰好 1200 字符一个不少;多 1 个字符即截到 1200(钉住"上限还在,不是关了限制")。
- T8 5xx 分支:走同一上限,且
calls === 4(钉住"5xx 仍重试 4 次"这条既有语义未被本次改动碰到)。 - ⚠️ T6 首发因夹具自身超上限(1453 > 1200)被断言挡住 ⇒ 已改 973 字符夹具;这正是该断言的价值(夹具错了会被抓出来)。
1-3 门禁与部署(✅ 判据 ①②③ 全绿)
| 判据 | 读数 |
|---|---|
| ① 放宽到 ~1200 | ✅ AGENT_ERROR_TEXT_MAX = 1200,两处分支同用 |
| ② 单测 | ✅ node --test test/remote-spawner.test.mjs = 8/8 通过(原 5 例 + 新 3 例) |
| ③ 门禁 | npm run build rc=0 | npm test = 592 tests / 590 过 / 0 败 / 2 跳过(第 21 棒 589/587/0/2 ⇒ +3)|npm run check:layering rc=0 ✅ 无新增违规(未归类 1 条 = src/platform-paths.ts,属既有基线) |
部署(两机同一文件)
| 项 | 47 | 106 |
|---|---|---|
| 目标 | /opt/dshs/lib/supervisor/remote-spawner.js |
/opt/dshs-cluster/lib/supervisor/remote-spawner.js |
| 投前 md5 | 269d3d20ffceb726298434972a349d56(20564 B) |
同左(逐字一致) |
| 投后 md5 | e9ab05d92a8666459aa486e6893e1bf5(21529 B) |
同左 |
| 三方一致 | ✅ 本机 = 47 = 106 = e9ab05d9… |
✅ |
| 备份 | /opt/dsh/backups/lib-p23/iZrj99af19cibck1ge93tqZ-pre-p23-20260925-122854.tgz(sha256 0089585e6f4e910e…)+ 同名 .manifest + 单文件副本 |
/opt/dsh/backups/lib-p23/VM-0-8-opencloudos-pre-p23-20260925-122855.tgz(sha256 d6f71f98efd5191d…)+ .manifest + 单文件副本 |
| 生效 | ✅ restart dshs ⇒ active · NRestarts=0 · 门户 HTTP 200 · journald dshs listening on http://127.0.0.1:3080 |
⛔ 未重启(中断在线用户 ⇒ 待窗口)⇒ 待重启面 47 → 48 件 |
- ⛔ 未重启 106(约束遵守);⛔ 未 commit / push;⛔
src/web/routes/im.ts零改动;⛔ 未手改任何用户home下的 profile /.pnpm。 - 47 重启时旧进程停止阶段有一次
dshs.service: Failed with result 'timeout'(优雅退出超时),新进程随即Started并监听 3080、门户 200、NRestarts=0⇒ 判为生效,如实记录。 dshs-worker(47)未重启:lib/supervisor/remote-spawner.js在 worker 模块图内(cli.js worker静态 importweb/server.js),但RemoteSpawner的唯一实例化点 =lib/web/server.js:38的 Manager 侧装配 ⇒ 对 worker 行为零影响。- ⚠️ 本棒无"长错误消息"真机读数 —— 该路径需要一次真失败的跨机调用才可观察。⛔ 不宣称运行面验收通过;本件判据 = 代码面 + 单测 + 部署面三方 md5 一致,均已取到。
二、第 2 件 · §5-18「清理前必查」只读取证(⛔ 未执行清理)
目标 profile:/var/lib/dshs/users/cce6d1cd-b376-4304-80f0-0e1c58c9ffde/home/profiles/web(已启用 dsh-plugin-mcn-suite 的那台 admin 实例;运行身份 dsh-cce6d1cdb376430480f0,uid 114801)
⛔ 全程只读:未删/未改任何软链、未改 profile、未动 .pnpm。
2-1 判据① 命中文件与命中处(含行号)
| # | 命中处 | 形态 |
|---|---|---|
| 1 | .pnpm/@dsh-local+business-plugins@file+..+..+.dsh-stage+business-plugins-0.3.25.tgz/node_modules/@dsh-local/business-plugins/lib/client.js:56 |
var React = require("react") |
| 2 | .pnpm/@dsh-local+portal-entry@file+..+..+.dsh-stage+portal-entry-0.5.6.tgz_…/node_modules/@dsh-local/portal-entry/lib/client.js:67 |
var React = require("react") |
| 3 | .pnpm/@[email protected]_…[email protected]/node_modules/@deepseek-ai/dsh-client-ui-renderer/lib/client.js:10 |
let react = require("react") |
| 4 | 包自身内部(react/react-dom 的 cjs/*、[email protected]、[email protected]、@deepseek-ai/dsh-client-ui-primitives/lib/index.js:8-9、@softspark/dsh-file-preview/lib/types/client/*.js:2) |
这些消费者在自己的 .pnpm/<pkg>/node_modules 自带 react ⇒ 不经兜底 |
全树剥离"自身包内部 / dist/assets 浏览器包 / lib/client.js"后剩余 51 条命中,全部属上述四类;四个包的 main(lib/index.js,服务端半边)零命中。
2-2 判据② 「包自己声明的依赖」 vs 「顶层兜底」——判别器实测
Node 解析优先级:① .pnpm/<pkg>/node_modules ② .pnpm/node_modules(提升层)③ <profile>/node_modules(顶层兜底所在)。以该实例 uid 运行 require.resolve(只解析、不执行模块):
| 解析起点 | react 落点 |
|---|---|
<profile> 本身 |
<shared>/dsh-plugin-mcn-suite/node_modules/.pnpm/[email protected]/node_modules/react/index.js |
<profile>/node_modules/dsh-plugin-mcn-suite |
↑同一落点(自己的 store) |
<shared>/dsh-plugin-mcn-suite |
↑同一落点(自己的 store) |
@dsh-local/business-plugins |
↑同一落点(⇒ 本次解析经顶层兜底链) |
@dsh-local/portal-entry |
↑同(经兜底) |
@deepseek-ai/dsh-client-ui-renderer |
↑同(经兜底) |
- 包自己声明的依赖:共享层
dsh-plugin-mcn-suite/package.json第 32–34 行声明react/react-dom/xlsx,其.pnpm树内自带三件 ⇒ 解析不经过顶层兜底。 - 顶层兜底:只有那 3 个
.pnpm/<pkg>/node_modules里没有react的消费者(① 优先级落空、② 提升层.pnpm/node_modules里三件都没有)⇒ 解析落到<profile>/node_modules/react⇒ 兜底链确实在解析路径上(实测三处落点全部 = 兜底链的实解目标)。 - 兜底目录自身现状:
.dsh-module-fallback/node_modules/内只剩 3 条链(xlsx/react/react-dom→ 共享层dsh-plugin-mcn-suite/node_modules/*),另有 4 个空目录(@deepseek-ai/@shikijs/@types/@ungap,各 0 项)= 历史残留。 - 三条链的字节级 mtime = 09-25 10:14:57(⛔ 非 §5-18 记的 09-08;09-08 是目录 mtime)⇒ 近两日内被平台装配动作刷新过(真值以本行实测为准)。
2-3 判据③ 结论句
结论 = 有真依赖,但依赖方全在「浏览器半边」;Node 运行期无消费者。
依据:3 个命中者全是 lib/client.js(浏览器半边)——
@dsh-local/business-plugins/lib/client.js头部原文:dsh client bundles 以普通脚本形式执行,向 window.__ModuleLoader__ 注册 factory;首次 import 时惰性物化 factory(require)@deepseek-ai/dsh-client-ui-renderer/lib/client.js首行:window.__ModuleLoader__.load({ id: …, factory: (require) => { … } })
⇒ 它们的 require("react") 由**浏览器侧模块表(window.__ModuleLoader__)**满足,不读 node_modules 磁盘。加一条反向证据:grep -rln "__ModuleLoader__" /opt/dshs/lib = 0 文件(平台服务端代码里根本没有这个加载器)⇒ 服务端不解析 client bundle 的 require。
边界(哪些没判到)
- 只核了这一个 profile(
profiles/web)与共享层 4 个包;另一台 profile9f3a1c47-…(uid 100010)没有这三条链(ABSENT),故不在讨论范围。 - ⛔ 未展开
@deepseek-ai/dsh官方包的客户端宿主实现(只读未做)——若它在服务端预解析 client bundle 依赖,结论需修正。 - 结论对当前这一版 profile 内容成立;profile 重建 / 插件版本升级后需重测。
- ⛔ 未做"删掉链再跑"的 A/B(会改实例解析面,属清理动作)。
2-4 给拍板的输入(⛔ 本棒不清理)
清理的实际影响面 = 只动 3 个浏览器半边包在 require.resolve 语义上的可见性;Node 运行期无消费者 ⇒ 风险低,但非零(见边界 2、3)。另:这三条链目前每个装配周期都要被 quarantineSharedLayerLinks 临时摘一次(第 20 棒的修法),清掉可省这一步 —— 但该修法本身已保证装配不受影响,所以"省一步"不构成必须清理的理由。
三、第 3 件 · §5-22 本机 lib ↔ 部署面 3+7 差异定性(⛔ 未投任何件)
本机
lib= 09-25 12:23 构建(本轮npm run build产物)/两机部署面 = IM 版 09-23 20:59(两机逐字相同)。
3-1 ① 那 7 件是否被引用 ⇒ 死件 / 活件
| 件 | 两机现状 | 部署面引用面 | 判定 |
|---|---|---|---|
im/gateway-token.js .d.ts .js.map |
三件全缺 | grep -rn gateway-token <lib>/**/*.js = 0 命中 ⇒ 部署面的旧版 web/routes/im.js 不引用它 |
死件(当前无引用者)✅ 不是活缺口 |
web/essential-plugins.d.ts .js.map |
缺(.js 两机在:6606 B / 09-25 10:45:57) |
两个引用者已能解析:web/routes/admin.js:10、web/routes/business-plugins.js:20 (from '../essential-plugins.js') |
纯声明/映射边车(runtime 零影响) |
web/routes/plugin-data.d.ts .js.map |
缺(.js 两机在:15523 B / 09-25 08:04:57) |
引用者已能解析:web/server.js:55 (from './routes/plugin-data.js') |
同上 |
🔴 一条必须记住的耦合(本件新出):本机 lib/web/routes/im.js:42 已 import … from '../../im/gateway-token.js' ⇒ 新版 im.js 与 im/gateway-token.* 必须同批投;⛔ 单投 im.js 会让实例在启动期 ERR_MODULE_NOT_FOUND。当前两机构成"旧 im.js + 无 gateway-token"的自洽态,没有人处于半投状态。
3-2 ② 3 件运行时 .js 谁更新
| 件 | 本机(12:23 构建) | 两机(09-23 20:59) | 谁更新 |
|---|---|---|---|
im/backends/gateway.js |
16241 B | 12077 B | 本机 |
im/connection-backend.js |
4154 B | 3342 B | 本机 |
web/routes/im.js |
47262 B | 36738 B | 本机 |
- 两机三件逐字相同(md5 相同、mtime 均
2026-09-23 20:59:31)⇒ 两机部署面 = 同一版 IM 构建(scp 保留源 mtime,故该时间 = 源构建时间,非投放时间)。 - 代码仓
git log无法作区分判据:IM 面整体落在同一 commite6207aa(chore(仓库对齐): 文档库结构治理 + IM/插件线落地)⇒ 以构建时间 + 体积为判据。 - 差异成因 = 本机含 IM 线 09-23 之后的改动,含本件新出的
gateway-token抽取。
3-3 ③ 结论表与建议
| 归类 | 件 | 本线可否补 |
|---|---|---|
| IM 线未投放件(⛔ 不归本线) | im/gateway-token.{js,d.ts,js.map}(3 件,须与新版 im.js 同批) |
⛔ 不补(单投即事故;同批属 IM 线投放面) |
| IM 线未投放件(⛔ 不归本线) | 3 件运行时 .js 的新版本 |
⛔ 不补(src/im/** + src/web/routes/im.ts 属 IM 线 lane,且 im.ts 有冻结口径) |
| 声明/映射边车(死件) | web/essential-plugins.{d.ts,js.map}、web/routes/plugin-data.{d.ts,js.map}(4 件) |
可补但无收益:两机运行面为纯 node,无 TS 消费者;.js.map 只影响调试栈 ⇒ 建议不投(投 = 制造 4 件永远用不上的文件) |
本线无"部署面活缺口" ⇒ 第 3 件无需任何落地动作;后续若要投,只能由 IM 线按"
im.js+gateway-token.*同批"排棒。
四、卫生与未闭合项
卫生(逐条遵守):⛔ 未 commit / push |⛔ src/web/routes/im.ts 零改动 |⛔ 未重启 106 |⛔ 未手改用户 home 下 profile / .pnpm |⛔ 未清 .dsh-module-fallback 软链(只取证)|⛔ 未改 D:/github/dsh_shenxian/lib 之外的别线产物 |✅ 47 /tmp 无残留(探针脚本已 trap 清理且已复核)|✅ 取证脚本全部落在本工作区 tmp/。
未闭合
- 🔴 106 待重启面 = 48 件(原 47 件 + 本轮 1 件)⇒ 生效待用户给窗口。
- ⚠️ 第 1 件无真机长错误消息读数(需一次真失败的跨机调用)⇒ 未宣称运行面验收。
- ⚠️ 第 2 件结论有 4 条边界(见 §2-3),其中"官方客户端宿主是否在服务端预解析依赖"未展开。
- ⚠️ 第 3 件两机
lib树里的 33 条部署备份残留仍未处置(第 22 棒记,属"部署卫生"独立议题)。
复现命令:tmp/p23-item2-probe{,2,3,4,5}.sh(第 2 件五批)|tmp/p23-item3-probe.sh(第 3 件,两机同一脚本)|tmp/p23-deploy-backup.sh <代码根>(投前备份)。