Files
dsh_ai1net_server/交付物/移动端字号扩面与共享层自足性转正-20260925.md
T
admin 318430c9e9 chore(工作区): 插件投放与分库线收口入库(第 35 棒 + 22:44 拍板落盘)
范围 = 本线(插件投放与分库线)产物 + 记忆类,共 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/规则与载体/、归档/、接续包_*。
2026-09-25 23:06:29 +08:00

87 lines
9.2 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.
# 第 18 棒落地件 · 共享层自足性转正 + 回滚残留 + 移动端第 ④ 条(2026-09-25)
> **线**:插件投放与分库线(入口 `接续入口_插件投放与分库线_20260922.md`)
> **棒次**:第 18 棒 · 执行棒(automation `ed2b5e61-25b9-4b97-849c-1e0b204b669e`)
> **代码基线**:`D:\github\dsh_shenxian`(生产口径 `dshs`)|⛔ 未 commit / push
> **结论**:第 1 件 ✅(判据 ①②③ 全绿)|第 2 件 ✅(判据成立,另发现"缺失侧"未覆盖 ⇒ 已上报)|第 3 件 ✅ **5/5**(含本次修的第 ④ 条)
---
## 一、第 1 件 · 共享层自足性从探针转为正式修法
**病**:共享只读包库 `<flat>/node_modules` 里只有插件自己的依赖、**零个 `@deepseek-ai`** ⇒ `link:` 形态下实例从共享层加载宿主代码必报 `Cannot find package '@deepseek-ai/dsh-llm'` ⇒ 实例 crash-restart ⇒ 探活判"实例未能启动"⇒ 插件被自动禁用。
**修法(唯一一份实现落在 ③ 能力层,① 入口层复用)**
| 落点 | 内容 |
|---|---|
| `src/supervisor/plugin-assembly.ts` | 新增 `resolveDshPackageRoot()`(解析顺序:env 显式覆盖 → 平台配置的 dsh bin 解软链 → `npm root -g`)、`resolveHostDshScopeDir()`、`ensureSharedScopeLink()`、`healSharedLayerSelfSufficiency()` |
| `src/web/routes/business-plugins.ts` | `doShare` 目录就位后调用 `healSharedLayerSelfSufficiency(root)`(**刻意放在 `hashTree` 之后** ⇒ 指纹不含软链,不引起重拉)+ 审计与响应体带 `sharedSelfSufficiency` 摘要 |
| `src/worker/agent.ts` | `syncSharedLayerOnce` **每轮**幂等自愈(worker 是独立单元,Manager 与 worker 各自把关) |
| `src/web/dsh-install.ts` | 该文件原有一份 dsh 根解析副本 ⇒ **下沉到 ③ 层复用**,删本地副本(代价:不再是"零内部 import",验证改走 `scripts/verify-dsh-install.mjs`) |
**软链落点 = 包目录内 `<flat>/node_modules/@deepseek-ai`**,⛔ 不落共享层根 —— 否则 `syncSharedLayerOnce` 的 `onDisk` 对账面会把 `node_modules` 当"多出来的产物"上报 `extra`。
**判据与读数(真机,`47 = bt-server` / `106 = test106`)**
| # | 判据 | 读数 |
|---|---|---|
| ① | **删掉人工探针链后重新 share ⇒ 共享层自带解析链** | 响应 `sharedSelfSufficiency.scope = /usr/local/lib/node_modules/@deepseek-ai/dsh/node_modules/@deepseek-ai`,`linked = ['_dsh-local_im-conversation-tabs','_dsh-local_storyforge','dsh-plugin-mcn-suite']` |
| ② | **47 + 106 两端都成立** | 47 三包 scope 均 LINK 可解(240 项);**106 三包 scope 均 LINK → `/usr/lib/node_modules/...`**(两机 `npm root -g` 路径不同 ⇒ 必须各机本地解析,⛔ 不能由 Manager 算好随包分发) |
| ③ | **重新启用 mcn-suite ⇒ 实例加载 0.3.15、任务 success、`rejected:[]`** | `off` 任务 `a524537251ff570a` = `success`;`on` 任务 `28dd88e4c391cad6` = `success`、`rejected:[]`、实例探活通过;journald:**`[mcn-suite] loaded v0.3.15(host 面 2 个:mcn, voxemw-cloud)`** + `skills ensured: 装 [email protected](升级自 0.3.14)`;`/api/dsh/status` = `running:true port:20000 restarts:0` |
**⛔ 未污染对账面**:共享层根**没有**多出 `node_modules`;`hashTree`/`hashTreeAt` 本就不计符号链接 ⇒ `treeSha256` 不变 ⇒ 不触发节点重拉。
---
## 二、第 2 件 · 回滚残留
**病**:`restoreProfile()` 只还原 `package.json`/`pnpm-lock.yaml`/`cordis.patch.yml` ⇒ 回滚后 `node_modules` 与声明不一致。
**修法**:新增 `declaredNamesIn(snap)`(`dependencies` ∪ `dsh.profile.bundles`,快照解析失败 ⇒ 取空集**宁可不删**)+ `pruneOrphanDepLinks(dir, snap)`(**只删软链**、**只删未声明**、跳过 `.` 开头与真目录、scoped 目录下钻),在 `restoreProfile()` 末尾调用。单测钉住四条不变量:声明内不删 / `bundles` 内不删 / 真目录不碰 / `.pnpm` 不碰。
**判据**:回滚后 `node_modules/<pkg>` 与 `dependencies` 一致(至少不残留无主软链)⇒ ✅ 成立。
> 🔴 **本棒实测到的另一半(⛔ 未修,已上报)**:残留有**两个方向**。本次 `off` 动作在真机上撞 `pnpm remove` 失败(见 §四-1),结果是**声明还在、`node_modules/dsh-plugin-mcn-suite` 软链被删掉了** —— 实例启动直接崩:
> `Error: dsh: cannot resolve profile bundle "dsh-plugin-mcn-suite" from the dsh installation or <profile>` ×5 ⇒ `crash-loop-circuit-open`(冷却 600 s)。
> `pruneOrphanDepLinks` 只治"多",不治"缺"。**建议修法(下一棒,小改)**:`restoreProfile()` 里对**声明中的 `link:` 依赖**做一次确定性重建(`link:` 目标就在快照里,纯 `symlinkSync`,**不需要跑 pnpm**、不碰 `.pnpm` 实体)⇒ 两个方向都收敛。
>
> **恢复过程(走平台正规口,未手改 profile)**:`off`→`on` 循环重跑,两次任务均 `success` ⇒ `pnpm install` 自动重建软链 ⇒ 实例重启探活通过。⛔ 全程未手工 `ln -s`、未手改 `package.json`/`.pnpm`。
---
## 三、第 3 件 · 移动端第 ④ 条(聚焦不缩放)+ 五条重跑
**改了什么**(插件真源 `D:/dshworkspace/plugin_package/dsh-plugin-mcn-suite/lib/client.js`,`mcn-mobile` 段第 ③ 条)
```css
.mcnNav_input,.mcnNav_select,.mcnNav_textarea,.v1rksel,.dvAcc_input,.dvAcc_select,.dvAcc_textarea{font-size:16px!important}
.mcnNav_panel input:not([type=checkbox]):not([type=radio]),…,.mcnNav_page select,…,.dvAcc_page textarea{font-size:16px!important}
```
理由:`.dvAcc_*` 基础样式用 `font:var(--dsw-font-s-14)` **简写**且**按需注入**(可能晚于本段)⇒ 必须 `!important`;`SELECT.v1rksel` 此前**不在任何移动端规则内** ⇒ 13.33px。
**投放链**:版本 `0.3.14 → 0.3.15`(`package.json` + `skills/.manifest.json` 两处真源)⇒ 以池内 0.3.14 tgz 为基底**确定性重打**(322 文件,变化面**恰 3 个文件**,自证基底 md5 ≡ 线上 `d387089b…`)⇒ `POST /api/plugins/business` ⇒ 两段式 share(`action:"replaced"`、`treeSha256 939206c76abb…`、`fileCount 657`)⇒ 47/106 共享层双双 0.3.15(106 首轮 `stale→pulled`)⇒ 47 四件、106 两件 md5 逐字一致 ⇒ restart ⇒ 门户 200 / worker 401。
**五条真机读数(`tmp/p17-visual.mjs`,零依赖 CDP 驱动本机真 Chrome,视口 375×667 + 横屏 667×375)**
| # | 判据 | 读数 | 判定 |
|---|---|---|---|
| ① | 工作台独占 | 开面板后 `navCount=1`,面板盒 `{x:56,y:0,w:319,h:667}`;会话区 `display:none` | ✅ |
| ② | 能退出 | 二次点 `.mcnNav_trigger` ⇒ URL `#/mcn`→`/`、`navCount` 1→0、会话区复原(现版 UI **不渲染** `.mcnNav_close` ⇒ 用等价判据) | ✅ |
| ③ | 无横向溢出 | `scrollWidth==innerWidth`(竖 375=375、横 667=667,溢出 **0**) | ✅ |
| ④ | **聚焦不缩放** | 工作台内唯一可见表单控件 `SELECT.v1rksel` 计算字号 = **16px**(修前 13.33px);注入样式段文本级取证:`style[data-plugin-css="mcn-mobile"]` 命中新规则 | ✅ |
| ⑤ | 横屏可用 | 667×375:面板 `{x:56,y:0,w:611,h:375}`,溢出 0 | ✅ |
**零点**:`style[data-plugin-css="mcn-mobile"]` 计数 = 1 ⇒ 新段确在浏览器里生效(不是"以为改了")。
---
## 四、两条新发现(⛔ 未擅自修,已写进 §2 后续)
1. 🔴 **`pnpm remove/install` 在真 profile 里会**偶发**失败(exit 255,`--reporter silent` 把原因全吞掉)** —— 第 16/17/18 棒累计三次(09:01 apply timeout、09:05 isolate install 失败、09:56 remove 失败)。**本棒已排除的假设(全部实测反证,别再重复走)**:㈠ 共享包目录内多出的 `node_modules/@deepseek-ai` 软链(A/B 对照**双双 rc=0**)㈡ 真实路径条件(真实路径副本 rc=0)㈢ worker 的最小 spawn 环境(`env -i PATH=… HOME=…` rc=0)㈣ 祖先目录不可 `scandir`(`/var/lib/dshs` 与 `/var/lib/dshs/users` 是 **711 ⇒ 该 uid `r` 位确实没有**,但实测不影响 pnpm 运行)㈤ 磁盘 / inode(23 GB 可用)。⇒ 高度指向**并发/时序**(同一 store 下并发 pnpm、或与共享层拉取重叠)。**下一次复现时的正解动作**:给失败路径补一次 `--reporter default` 重跑(现在 `--reporter silent` 结构上让 `pnpmExecError` 拿不到任何正文)—— 属 `plugin-assembly.ts` 的小改。
2. 🟡 **`restoreProfile()` 不重建缺失的 `link:` 软链**(见 §二 的红框)⇒ 今日真实致实例崩溃。
---
## 五、门禁与卫生
- `npm run build` rc=0|`npm test` = **575 tests / 573 过 / 0 败 / 2 跳过**(基线 449/447/2;新增 6 个用例:软链补建与幂等 / 悬空与指错 / 真目录不碰 / `no_scope` 零改动 / 全量补含老包且不落共享层根 / 回滚清无主软链)|`check-layering` **✅ 无新增违规**(现存 5 条全在基线内)。
- 部署备份:`/opt/dsh/backups/lib-p18/{47,106}-pre-p18`;插件回滚素材 `/opt/dsh/backups/plugins/dsh-plugin-mcn-suite/0.3.15.tgz`。
- 清理:临时 admin 会话**残留 0**(两机 `/tmp/p18*` 残留 0)|⛔ 未 commit/push、⛔ `src/web/routes/im.ts` 零改动、⛔ 未手改用户 `home` 下的 profile / `.pnpm`。