范围 = 本线(插件投放与分库线)产物 + 记忆类,共 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/规则与载体/、归档/、接续包_*。
12 KiB
移动端修复「迁包 → 重投 47/106」+ 真机视觉验收(第 16 棒)
工作区:
E:/ProgramData/AIProject/aliyun-dsh-server|线:插件投放与分库线|日期:2026-09-25 依据:接续入口_插件投放与分库线_20260922.md §2「🎯 本轮动作(第 16 棒)」⛔ 本棒未 commit / push;⛔ 未改src/web/routes/im.ts;⛔ 未做 MCN 数据面接入改造。
一、结论(先说判定)
| 件 | 判定 | 一句话 |
|---|---|---|
| 第 1 件 · 迁包 | ✅ 完成 | mcn-mobile 样式段已从从未投放的 dsh-plugin-mcn v0.1.0 整段平移进线上真身 dsh-plugin-mcn-suite,38 个引用类名在目标包内齐备 0 缺 |
| 第 2 件 · 版本自增+重投 47/106 | ✅ 完成 | 0.3.13 → 0.3.14;47 + 106 共享层双双为 0.3.14(106 由节点拉取机制一轮收敛);池内候选同步、回滚素材在位 |
| 第 3 件 · 375×667 真机视觉验收 | ❌ 未通过(未取到真机读数) | 不是代码问题,是链路上游断了:已启用用户的实例仍指向旧副本,而平台的「重装配」操作稳定失败(两次复现)。⛔ 未宣称通过 |
🔴 本轮最重要的产出 = 一条可复现的新缺陷:共享层更新对「已启用」的用户不生效,且平台没有可用的补救路径 —— 详见 §四。
二、第 1 件 · 迁包(逐字平移,只改包名一处)
| 项 | 值 |
|---|---|
| 源 | D:/dshworkspace/plugin_package/dsh-plugin-mcn/lib/client.js 第 216–268 行(//#region 移动端 / 窄屏兼容) |
| 目标 | D:/dshworkspace/plugin_package/dsh-plugin-mcn-suite/lib/client.js(插在 var McnNav_module_css_default 之前) |
| 改动 | 仅 1 处:el3.dataset.plugin = "dsh-plugin-mcn" → "dsh-plugin-mcn-suite" |
| 体量 | 段 3642 B / 53 行 → 剥行尾后 3595 B;目标 401936 → 405531 B |
| 语法 | node --check OK(Node 22.22.2) |
三条自证(脚本 tmp/p16-port-mobile-20260925.py,可重跑):
- 类名齐备:段内引用类名 38 个,目标包内缺失 0 个 ⇒ 第 15 棒的"可移植性核对表"在字节级上被独立复核。
- 行尾一致:源段是 CRLF、目标包是纯 LF ⇒ 已归一(产物 CR = 0)。⚠️ 首版没做归一,产物混入 53 个 CR,已回滚重做。
- 幂等:目标已含
data-plugin-css="mcn-mobile"⇒ 脚本直接 SKIP。 - 可回滚:改前副本
tmp/p16-backup-20260925/dsh-plugin-mcn-suite-client.js.bak(401936 B)。
三、第 2 件 · 版本自增与重投(47 + 106)
3.1 重投前对账("别把别的漂移一起发出去")
改前副本 md5 = 0955f6b8b70f0c1357d29395643fdeb6 = 47 共享层线上 lib/client.js md5 ⇒ 本地 suite ≡ 线上真身,逐字无漂移。
全包逐文件比对(323 项,按路径建索引,排除 node_modules):
| 差异 | 内容 |
|---|---|
| 内容不同 | 仅 lib/client.js(就是本棒的改动) |
| 仅本地 / 仅线上 | tests/smoke.mjs(本地有、不进包)/pnpm-lock.yaml(服务端安装时生成) |
⇒ 漂移面 = 1 个文件且是本棒的预期改动。
3.2 打包(以池内 tgz 为基底,只替换 3 个文件)
| 项 | 值 |
|---|---|
| 基底 | /var/lib/dshs/business-plugins/dsh-plugin-mcn-suite.tgz(1989680 B,322 文件,顶层 package/) |
| 新包 | tmp/p16-pool/dsh-plugin-mcn-suite-0.3.14.tgz(1982036 B,322 文件) |
| sha256 | 3ca65153605b3c48f7de628d4754dc872b6f4fb7b617e9e57e704eb90bbab61c |
| 新旧差异 | 只差 3 个文件:package/lib/client.js、package/package.json、package/skills/.manifest.json;零增删 |
版本自增两处(该包「版本号唯一真源 = package.json」,SUITE_VERSION 由运行时读它 ⇒ 无字面量可改):
package.json version → 0.3.14(+ 变更史前置一条)|skills/.manifest.json → suite: …@0.3.14、技能 version: 0.3.14。
⚠️ files/bytes/sha256 三项未动(技能载荷未变,只动版本字段)。
🔴 行尾坑(本棒第二次踩):Windows 上用 Python 写文本默认把 \n 转成 \r\n ⇒ 首版把 CRLF 写进了 package.json 与清单,已回滚重做(写时显式 newline="\n",产物 CR = 0)。
3.3 上传递交(走平台正规口,非手改文件)
POST /api/plugins/business(admin 会话)→ http=200:version 0.3.14、replaced: true、trustedOverride: false、
兼容性裁决 level: ok(platformPkgs 240、sawPlatformRef true);安全检测只出 warning 未拦([email protected] / network-egress@lib/client.js / …)。
3.4 铺共享层(两段式:先预演、后确认)
- 预演(
confirm缺省)→conflict: true,并顺带暴露一条台账缺陷(见 §四 P2)。 - 确认 →
action: "replaced"、version 0.3.14、fileCount 657、新treeSha256 f414df79…、 回滚素材/opt/dsh/backups/plugins/dsh-plugin-mcn-suite/0.3.14.tgz。
47 实况复核:lib/client.js md5 = d387089bd28c91cdbe46056e21288cbd(= 本地新文件)、mcn-mobile 命中 2 处、package.json = 0.3.14、文件数 657(含 pnpm install --prod 重建的 node_modules)。
3.5 106 同步(走节点拉取机制,非手工 scp)
106 上手动触发一轮:POST http://127.0.0.1:19000/shared-layer/pull(带 x-dsh-agent-token)
→ {"stale":["dsh-plugin-mcn-suite"],"pulled":["dsh-plugin-mcn-suite"],"failed":[]}
→ 106 盘上 0.3.13 → 0.3.14、client.js md5 = d387089…。一轮收敛。
3.6 回滚
| 素材 | 位置 | 说明 |
|---|---|---|
| 上一版(内容 = 0.3.13) | 47 /opt/dsh/backups/plugins/dsh-plugin-mcn-suite/0.3.9.tgz(md5 f47a07b9a2a889770e35315e22156e90) |
⚠️ 文件名叫 0.3.9、内容是 0.3.13(见 §四 P2) |
| 本棒投出的版本 | 同上目录 0.3.14.tgz |
内容是新包(share 时补落的语义错位,见 §四 P2) |
| 回滚动作 | 用上一版 tgz 重走上传递交 + POST /api/plugins/business/share {confirm:true} |
共享层目录被整体替换 ⇒ 指向它的用户下一次 reconcile 生效 |
四、抓出的缺陷(⛔ 均未擅自修,按纪律只报告)
🔴 P0 —— 已启用插件拿不到共享层新版本,且平台无可用补救路径
判据链(全部可复现):
- 实例依赖形态:admin 实例 profile 的
dsh-plugin-mcn-suite声明为file:/var/lib/dshs/users/cce6d1cd-…/home/.dsh-stage/dsh-plugin-mcn-suite.tgz—— 是一份.dsh-stage副本(1989680 B = 旧版内容,09-20),与共享层无关。 ⇒ 共享层升到 0.3.14 对它零影响,这与第 15 棒"修复落在没投放的包里"是同一病根的下一层。 - 正规补救路径 = 关掉再开(
apply是全量裁决:已启用的项再发enabled:true判noop,不会重装配)。 - 该路径稳定失败:两次全量「关闭」均返回
taskId,各约 52 s 后{"status":"failed","stage":"失败","error":"安装失败,请重试或联系管理员"}:- 任务
0df1ea39ed3026c4(08:32:38 → 08:33:30) - 任务
3eb4b72b73373e07(08:34:42 → 08:35:34)/mine复核仍为enabled: true⇒ 平台没有把用户切到新版本的可用手段。 ⚠️ 已核实不是"暂存包缺失"(4 个.dsh-stage包全部在位)。
- 任务
- 失败原因不可观测(第二条缺陷):
journalctl -u dshs在失败时段只打了 HTTP 请求行,pnpm 的 stderr / 装配失败原因未落任何日志 ⇒ 用户与运维都只能看到"请重试",无法定位。本棒的根因因此未能定位。
影响面:凡是已经启用过某插件的用户,都停留在启用当时那份副本上 ⇒ 平台的「插件更新」对存量用户不成立;共享层只对"新启用"的用户有效。这条直接命中本线目标 P2("插件更新的情形要有定义")。
建议落点(下一棒):① 给 applyPlugins 的 pnpm 失败路径补可观测性(stderr 落日志 + 失败原因回传到任务对象);② 定位失败根因(候选:47 的 egress 护栏挡住了平台自身的 registry 解析 / pnpm store 锁竞争);③ 重装配要能把 file: 形态换成 link:。
🟡 P1 —— 移动端真机视觉验收仍未取得(第 15 棒遗留,本棒被上面那条挡住)
"能不能真渲染"这件事本棒已做成判定,且结论是"能":
| 探针 | 读数 |
|---|---|
| 47 侧浏览器 | ❌ 无(chromium/chrome/firefox 全无、无 playwright/puppeteer 缓存) |
| 本机侧浏览器 | ✅ 有:C:/Program Files/Google/Chrome/Application/chrome.exe(真 Chromium 内核) |
| 平台门户 | ✅ https://ai1net.com/portal.html → 200 |
| 实例 UI | ✅ https://ai1net.com/u/<uid>/dsh/ → 200,且平台自动下发实例凭据 Set-Cookie: dsh-auth-… |
| 实例地址 | https://admin.ai1net.com/?token=…(/api/dsh/status 直出,可直接作为渲染入口) |
⇒ 路径已通,只差"实例上跑的是新版本"这一步。修好 P0 即可直接补做五条读数(无需再做任何环境建设)。
🟡 P2 —— 共享层清单与磁盘不同步 + 回滚素材命名语义错
- 清单 vs 盘:本次预演读到的清单是
version: "0.3.9"、tgzSha256 d14cd5fe…、treeSha256 cb1c73fb…,而盘上实际是 0.3.13 内容(package.json version = 0.3.13、client.jsmd5 与池内 0.3.13 tgz 一致)。 ⇒authoritativeShared()拿这份treeSha256做节点对账 ⇒ 节点会永远判stale/ 反复重拉。本棒 put 新版本后该条自愈(新treeSha256是现算的),但机制上的不同步来源未查明。 - 回滚素材命名:
doShare里补落的备份路径用 incoming 版本号 命名、内容取 incoming 的 tgz (backupTgz = <backupDir>/plugins/<flat>/<version>.tgz且copyFileSync(existing.tgzPath, …),而existing= 上传后的池条目) ⇒ 产出的0.3.14.tgz是"新包的自拷贝",不构成"回到上一版"的素材;真正可回滚的那份躺在0.3.9.tgz里(名字与内容不符)。 ⇒ 建议:备份应记被替换的那一版,或名字带上"replaced"语义。
五、复现命令(第三方可照抄)
# ① 共享层版本与字节
ssh bt-server 'grep -m1 "\"version\"" /var/lib/dshs/bundled-plugins/dsh-plugin-mcn-suite/package.json'
ssh bt-server 'md5sum /var/lib/dshs/bundled-plugins/dsh-plugin-mcn-suite/lib/client.js' # 期望 d387089bd28c91cdbe46056e21288cbd
ssh test106 'md5sum /var/lib/dshs/bundled-plugins/dsh-plugin-mcn-suite/lib/client.js' # 应与 47 同值
# ② 池内候选
ssh bt-server 'ls -la /var/lib/dshs/business-plugins/dsh-plugin-mcn-suite.tgz' # 期望 1982036 B
# ③ 实例依赖形态(P0 判据)
ssh bt-server 'python3 -c "import json;print(json.load(open(\"/var/lib/dshs/users/cce6d1cd-b376-4304-80f0-0e1c58c9ffde/home/profiles/web/package.json\"))[\"dependencies\"][\"dsh-plugin-mcn-suite\"])"'
# 期望输出 file:…/.dsh-stage/dsh-plugin-mcn-suite.tgz ← 即「未指向共享层」
# ④ 重装配失败复现(需要 admin 会话):全量 apply 一条 enabled:false,随后轮询
# GET /api/plugins/mine/task/<taskId> → 约 52 s 后 status=failed / "安装失败,请重试或联系管理员"
本棒用到的临时脚本(均在 tmp/,可重跑):p16-port-mobile-20260925.py(迁包)· p16-build-0314.py(改版打包)· p16-hash-list.py / p16-cmp.py(逐文件对账)· p16-pull-106.sh(触发节点拉取)· p16-cleanup.sh(清临时会话与临时物)。只读工具:p16-reach.sh(连通性探路)。
六、清理与合规
| 项 | 状态 |
|---|---|
| 临时 admin 会话 | ✅ 已删(DELETE FROM sessions WHERE user_agent='p16-mksess' ⇒ rowCount 1,复核残留 0) |
47 /tmp 临时物 |
✅ 零残留 |
| 实例半状态 | ✅ 无:实例 running、profile 依赖未被改动(两次失败均未落盘) |
| 47/106 服务 | ✅ 正常(共享层 0.3.14、池 0.3.14) |
装配协议 / plugin-assembly.ts |
✅ 零改动 |
| 可见面开关 | ✅ 未开;⛔ 未开任何新口 |
src/web/routes/im.ts |
✅ 零改动(IM 线 D 单判据 2 冻结遵守) |
| commit / push | ✅ 未做 |