# 接续入口 · 插件投放与分库线(2026-09-22 开线) > **工作区**:`E:/ProgramData/AIProject/aliyun-dsh-server`(本入口**只此一份**) > **开线棒**:会话 `aliyun-dsh-server` 2026-09-22 13:2x(用户当场提要求后收口) > **代码基线**:`D:\github\dsh_shenxian` = **生产口径 `dshs`**;⚠️ 另有一份**开源导出物 `dsh_ai1net`**(`/opt/dsh_ai1net` · `/etc/dsh_ai1net.env` · `DSH_AI1NET_*`)是**另一套命名、另一个部署**,⛔ 两者不要混用(本会话已实测踩过这个坑)。 > **上游上下文**:同工作区 `.workbuddy/memory/2026-09-22.md` 的 `10:0x` 节(carbon 线"两处平台侧阻塞"判定)+ `07:5x` 节(一插件一库拍板)。 --- ## §0 状态(**最新一行 = 执行依据**) - 🔴 **2026-09-26 06:36|用户拍板 ⇒ 执行棒已登记(`p36d-platform-versioning`)—— 本线最新一行,🔴 **执行依据以本行为准** ——** **用户原话**:「**C 方案,然后B,这次就用版本更新机制去更新网络上各节点,子节点和设备**」。 **口径(⛔ 别被方案里的层名混淆)**:用户说的 **C 案** = **层 A(止血)+ 层 B-1~B-4**(版本目录化/channel 灰度/定向钉版/**设备纳入**);**随后做**的 **B 案** = **层 B-5**(全局版本矩阵视图);**层 C 跨区拓扑本次不做**(属覆盖网络线架构层),但实现须与其口径**兼容**(每区 Manager 各持内容源、节点只向本区拉、同版本号须字节一致)。 **收尾必做**:**用这套机制真跑一次分发** —— `dsh-plugin-mcn-suite@0.5.1`(含以后版本)→ **47 本机 worker + 106 子节点 + 设备(桌面端)**,并**读数验证**(判据:`w-106` 对账快照 `stale`/`failed` 清空)。 🔴 **执行棒第一件事 = 取证设备(桌面端)侧载体**(承缺陷 D6「设备不在分发链上」):桌面端有没有「包/插件目录 + 拉取入口」、凭据走哪条。**有 ⇒ 按同一对账协议接入;没有 ⇒ 具名报告缺口 + 给最小接入方案,⛔ 不硬造。** 📂 **唯一执行单** = `交付物/版本管理与包更新机制-现状与方案-20260926.md`(§6 八段交接要素 + **§8 执行棒细化** S1→S4)。⛔ 过程档案 `04-调整方案/` 不作依据。 ✅ **执行棒 automation = `da73ab87-fb2f-47f0-a8d8-8ea3ad35eb19`**(一次性 · 2026-09-26T06:52);⛔ 本线入口 automation `35e1bfbc-…`(第 36 棒)保持 `PAUSED` 不动。 ⚠️ **本轮事故留档**:`tmp/p36/patch-c3.py` 用 `open(P,"wb").write(join(...))` ⇒ Python 先求值 `open()`(**立即截断**)再求值 `join()`,join 抛 `TypeError` ⇒ **入口被清成 0 字节**;已 `git checkout --` 复原(341,330 B / 968 行,sha `1d44073b…` 与 HEAD 逐字节一致,**无损**)。⇒ **判据:改共享文件只准 Edit 精确片段替换或「先算全量 bytes 再一次性写」,⛔ 禁用 `open(...,"wb").write(表达式)` 复合式**。 - 🔴 **2026-09-26 06:29–06:5x|用户授权「你就是平台线」⇒ 第 36 棒续作二 · 规划棒(`p36c-platform-versioning`)—— 只读取证 + 出方案,⛔ 未改 `src/**` 一行;⛔ 未登记下一棒(撞 1 项待拍板)** —— **用户原话**:「**你就是平台线,看看如何修复(还有 worker 如何更新包,是不是应该有个机制,现在的网络结构各种节点和设备,如何进行版本管理和包更新机制)**」⇒ **本轮按「规划与执行分离」只产出方案**。 **成果** ⇒ `交付物/版本管理与包更新机制-现状与方案-20260926.md`(**同时是下一棒的「唯一执行单」**,含 8 段交接要素)。要点: **① 现状**:机制骨架**齐全**(worker 启动即拉+定时轮询 `src/worker/agent.ts:816-819`、`POST /shared-layer/pull` `:812`、主流程 `syncSharedLayerOnce` `:373`、对账 `:422-427`、Manager 对账面 `business-plugins.ts:373-428`、权威清单 `.manifest.json`、对账快照 `.node-sync-reports.json`)⇒ ⛔ **不是「没有机制」**。 **② 超时根因(D1)**:`src/worker/agent.ts:376` `const timeoutMs = opts.timeoutMs ?? 20000` —— **对账面 `:426` 与产物下载面 `:508` 共用同一 20 s 常量** ⇒ 2.0 MB 包跨云(47↔106 经 relay)拉不完 ⇒ `AbortError: The operation was aborted due to timeout`(与 106 上报串**逐字吻合**)⇒ **106 永远停在旧版**。 **③ 缺陷另 7 条**:D2 无重试退避 · D3 失败历史不可见(每 host 只留最新一条)· D4 版本只有「最新一份」(`rollbackTgz` 仅一档)· D5 无 channel/目标版本声明面 ⇒ 不能灰度 · D6 **设备(桌面端)不在分发链上** · D7 平台侧无全局版本矩阵视图 · D8 台账 `dsh_instances.status` 与实时视图不一致(承 §9)。 **④ 方案分层**:**层 A 立即修复**(超时分离 20 s↔300 s/按大小动态/重试退避/失败留痕 ⇒ 纯插入型)· **层 B 版本管理机制**(版本目录化 + channel 灰度 + 定向钉版 + 设备纳入 + 全局矩阵视图)· **层 C 拓扑与跨区联邦**(**每区 Manager 各持内容源**,节点只向本区拉;版本号+`treeSha256` 为跨区一致性标识;⛔ 不做全网单点源)。 **⑤ 待拍板 1 项**:只修超时(A)/A+全局视图(C)/A+完整版本管理(B)—— 我的倾向 **A 先落、C 紧随**(106 有真实用户在等,A 是止血;C 是纯读视图、不加概念)。🔴 **拍板未到 ⇒ ⛔ 未登记下一棒**。 - 🔴 **2026-09-26 00:17–00:4x|第 36 棒 续作(用户两条拍板落地,`p36b-exec-plugin-dataline`)—— A2「真实例进程内」面 ✅ 达成 · A8 改判「机制在跑 · 该件拉取超时」· ⛔ 本条不新增下一棒** —— **拍板① = A(启动实例取进程内读数)**:🔴 **纠正上一棒** —— 实例**一直在跑**(端口 `20001`;实时 `GET /api/admin/users//dsh/status` ⇒ `running:true`),上一棒所据 **PG `dsh_instances.status=stopped` 是滞后失真**。⛔ 本棒**未启动、未停止**任何实例。**进程内读数**:`curl --compressed -H "Cookie: dsh_token=<实例 token>" http://127.0.0.1:20001/mcn/api/accounts` ⇒ **`200`** + 13,155 B 正文(`{"ok":true,"total":5,"page":1,…,"account_name":"王微斯",…}`))⇒ `total:5` 与影子读 `hot_accounts 5=5` 一致 ⇒ **A2 全达成**。 **拍板② = 「worker 应该使用插件更新机制」(⛔ 不是给 106 开 ssh 通道)**:机制**已落地且在跑** —— `src/worker/agent.ts:812` `POST /shared-layer/pull`(带 agent token)+ `:816-819` **启动即拉一次 + 定时轮询**;源码注释原话「**`restart dshs-worker` 就等于完成一次共享层同步,⛔ 不必再手工 scp**」;形态 = **节点主动拉(pull)+ 主节点对账告知**(`src/web/routes/business-plugins.ts:373-377`),快照落 `/.node-sync-reports.json`。**真机对账快照**(`at` ⇒ **2026-09-25 23:52:11 CST**,即我 23:22 `share 0.5.1` 之后):`w-106` · `authoritative:4` · `missing:[]` · **`stale:["dsh-plugin-mcn-suite"]`** · **`failed:[{"flat":"dsh-plugin-mcn-suite","error":"The operation was aborted due to timeout"}]`** ⇒ **106 确实在自主拉取,不是「没通道」**;但**该件拉取超时失败** ⇒ 仍为旧版 ⇒ **A8 由「未取证」改判为「机制在跑 · 该件超时失败」**。🔴 **属平台侧参数/实现问题**(2.0 MB 包跨云拉取超时)⇒ 依硬约束**只报告**(修复方向:调大超时 / 分块续拉 / 失败退避重试)。 🔴 **另一条平台侧发现(值得单独立项)**:`dsh_instances.status` 与实时视图**不一致**(台账 `stopped`/实为 `running`)—— **它影响一切「实例是否在跑」的判断**。 📂 读数与命令原文 ⇒ `交付物/MCN数据面接入-阶段二-切档与收口-20260925.md` **§9**;R4 收尾 `DELETE … user_agent='poc-curl2'` ⇒ `DELETED=1`、`POC_LEFT=0`。 - 🔴 **2026-09-25 23:10–23:5x|第 36 棒 · 执行棒 收口(`p36-exec-plugin-dataline`)—— ①–④ 全达成 · ⑤「47 侧切档」达成 · ⛔ ⑤「实例重启/106」未达成 ⇒ A8 如实记「两机不一致」;⛔ 未登记下一棒(等拍板)** —— ① ✅ **修 `canonicalJsonText()` 键序敏感**:新增 `sortKeysDeep()` + 导出 `canonicalStringify()`(对象键**递归升序**、数组**保序**),三形态归一出口全改走它 ⇒ 单测 **67/67**(新增第 59 条)+ 本机等效证明 **10/10**(含「旧算法判不等 → 新算法判等」与「字符串形态零回归」四例)。⛔ 落库原文零改动。 ② ✅ **出包 `0.5.1`**:`2,018,751 B`/**324 条目**/md5 **`56c17c7e41a18704922e18d75816da59`**;包内 `store.js` sha256 与本机逐字一致 → 上池 `200`(`compat.level=ok`)→ 台账 **`state=ready`** → 结构预演 `ITEMS=[]` → share **`200 action:"replaced" version 0.5.1`**;47 共享层 `store.js` md5 **`62384d4d…`=本机逐字一致**(`canonicalStringify_COUNT=4`);回滚素材 `0.5.0.tgz` 在位。 ③ ✅ **删本地 5 条冗余副本(拍板①)**:**先双份留档**(`sqlite3 .backup` ⇒ 2,383,872 B/sha256 `ee75df4a…`;全表 dump 39 行/sha256 `fdfd8fa4…`;5 组原样含正文 `export-dup5.json` 542,624 B/sha256 `b0e560ca…`;本机副本在 `$WS/tmp/p36/`)⇒ 再删 `id = 40,28,29,31,32` ⇒ **39 → 34 行**、`KEY_DELTA lost=[] extra=[]`、`integrity_check=ok`;⛔ 库文件在位未删、属主未变、别的表一行未动。⚠️ **插件既有删除路径不适用**(`removeVideoSource` 按 `aweme_id` 删**整组** ⇒ 会误删保留行)⇒ 用最小 SQL + 四条 fail-closed 断言。 ④ ✅ **影子读重取**:三表 A1 **34=34 / 362=362 / 5=5**(`onlyLocal=[] onlyPlatform=[]`)· SHADOW 三表 **`match=true`**、`degraded=false` · `account_video_source` **逐行内容「正文 34/34 · 分析 34/34 · 差异 0」(第 35 棒为 23/34 不等 ⇒ ① 的修法真机确认)** · `PACKED_LEFTOVER=0` · `TOTAL_WRITES=0`。 ⑤ 🟡 **47 侧切档达成**:三表切 `platform`(读写同切)⇒ 实例档位 **7 表 platform**(**A5 达成**)· 第四批三表仍 `local` · 属主 = 实例 uid · 原文件已备份 `data-plane.json.bak-p36-1790350596159`。📌 **生效机制实测**:`dataPlaneConfig()` **每次调用读文件**(无内存缓存)⇒ 切档**运行时立即生效、不需要重启**。A2 切档后面:三表 `source=platform` 全 OK、`TOTAL_WRITES=0`。 ⑤ ⛔ **未达成两项(具名)**:**(甲)** A2「**真实例进程内**」面 —— 目标实例 `dsh_instances.status = "stopped"`(平台**按需启动**);本棒未启动它:Manager 侧未找到启停路由,worker agent `POST /launch`(`127.0.0.1:19100`)**属控制面职责** ⇒ 直调会绕过 Manager 的 `epoch/lease` 台账(状态漂移,R11)⇒ **主动不做**。**(乙)** **106(`w-106` / `106.54.21.172`)同步 + 合并重启** —— 47 与本机 ssh 106 **均 `Permission denied (publickey)`**、47 亦无法直连其 `19000`(`overlay` 台账 `via: "relay"`)⇒ **无可用通道**。 📊 **A 判据**:**A1 ✅**(三表全一致)· **A2 🟡**(取数口面=第 35 棒 + 切档后读路径面 ✅;真实例进程面 ⛔)· **A3 ✅**(`157 / 8 / 10`;本棒**未改任何调用点** ⇒ 第 34 棒「平台档路径零命中 + 三类具名豁免」结论**逐字沿用**)· **A4 ✅**(`0 / 10`)· **A5 ✅**(7 表)· **A6 🟡**(库文件在位未删、属主未变;sha256 因**授权删行**必然变化,回滚靠 `.bak` + dump)· **A7 ✅ 免跑**(`lib/client.js` mtime 09:47,本棒改动面仅 `store.js`/`tests`/`package.json`/`dist`)· **A8 ⛔ 不一致**(47 代码面=运行面=`0.5.1`;**106 未取证**)· **A9 ✅ = 23(零新增)**。 🔴 **本棒动过生产**:47 共享层投放 `0.5.1` + 实例库**删 5 行**(已授权)+ 实例档位切 **7 表 `platform`**。⛔ **未启动/停止任何实例**、⛔ **未触碰 106**、⛔ **未改 `D:/github/dsh_shenxian/src/**`**、⛔ **未删库文件/未重建库**。 🔴 **新发现(未闭合 · 插件侧)**:`tools/migrate-local-to-platform.mjs` 的 `SELECT *` **无 `ORDER BY`** ⇒ 平台折叠时保留 `id` **较小**那份,而本地读路径 `ORDER BY id DESC LIMIT 1` 取 `id` **最大** ⇒ **同一行在两处的"权威版本"不同**(本次影响 2 组)。⚠️ **因此 ③ 的删除口径如实偏差**:5 组里有 **2 组删掉的是 `id` 较大(较新)那一行**(`aweme_id 7658997937279683855` 删 40 留 20;`7648947073131625905` 删 28 留 27),这 2 组差异 = 值内 2 字节**字面 `\n`**;若按字面「删 `id` 小的」,本地剩余内容与平台**不一致** ⇒ ④ 判据达不成。**该判定属内容取舍,清单已具名列出供复核**(交付件 §4.1)。 📌 交付件 = `$WS/交付物/MCN数据面接入-阶段二-切档与收口-20260925.md`(结论/①–⑤ 状态/A1–A9 命令+输出+退出码/**判定表与偏差说明**/未达成 5 条/卫生与域锁)。 🧹 **卫生**:R10 全程 `ROOT_OWNED=0`(收尾再复核)· 临时会话清理读数见交付件 §8 · 47 侧 `/tmp/p36*` 已清(本机留档留 7 天)。 ⛔ **未登记下一棒(第 37 棒)** —— 剩余两项**要用户拍板**:**(i)** 106 的 ssh 凭据/授权通道;**(ii)** 实例何时启(用户下次访问门户即**自然启动**并加载 `0.5.1` + 新档位)。按规则「要用户拍板的,等拍了再新建接续会话」⇒ **等拍板**。 - 🟢 **2026-09-25 22:44|第 35 棒后续 · 用户拍板两条(`p35-exec-plugin-dataline`)—— ① 本地 5 组重复 `aweme_id` 处置 = **A 案「删本地 5 条旧副本」**(不可逆 · 已授权)|② 插件包源码 = **B 案「不纳入版本管理」** ⇒ 改为定期打压缩包备份,**已落地**:固定目录 `E:/dsh-backups/plugin-src/`(`backup.sh` + 基线包 `mcn-suite-src-20260925-2246-v0.5.0.tgz`/6,065,179 B/330 文件/md5 `247dd145…`)+ 每周自动备份 automation `1e3c9070-a75d-4ae4-b81e-06e2eb20a5b4`(周日 03:30 · 内容摘要去重 · 保留最近 26 份)。🔴 **两条拍板已注入第 36 棒 prompt ⇒ ⑥ 可闭环、⑦ 可执行、⛔ 不再重问**。** - 🔴 **2026-09-25 22:5x|第 35 棒 · 执行棒 收口(`p35-exec-plugin-dataline`)—— ①–⑤ 全达成 · ⑥ 未全绿 ⇒ ⛔ 未切档 · ⑦ 整条留给第 36 棒 · 已登记第 36 棒** —— ① ✅ **处方 (a) 落码**(`store.js` 的 `normalizeRow`/`rowsOf` 两个唯一必经之路;编解码只在插件侧)⇒ 单测 **66/66**、`node --check` 9 文件 `rc=0`。 ② ✅ **出包 `0.5.0`**:`dist/dsh-plugin-mcn-suite-0.5.0.tgz` **2,017,667 B**/**324 条目**/md5 **`5cbccac1348e5dcb8d11bf400a000498`**。 ③ ✅ **上池 → 共享层发布**:200 `action:"replaced"` `version 0.5.0`(47 共享层 `store.js` md5 `bc02c23c080eee58d8873508badc1c5b` **=本机逐字一致**、`gz1:` 命中 4 处)。 ④ ✅ **加列迁移** 2 条 `ADD COLUMN IF NOT EXISTS local_id TEXT`(`elapsedMs:16`/`backup:"taken"`)⇒ 库读数 `hot_accounts` **17 列/5 行**、`account_videos` **20 列/362 行**、`account_video_source` **9 列/18 行**;台账 `state: ready`、对账 `clean:true`;⛔ 不删库不重灌。 ⑤ ✅ **回填 `local_id`**:dry-run `DRY_WRITE_CALLS=0`;apply ⇒ `account_videos` **backfill=362**、`hot_accounts` **backfill=5**、`account_video_source` ins=16/skip=23、**失败 0**;幂等重跑 ins=0;本地库 sha256 `b37f8aab…` **前后逐字一致**(mtime 未变)⇒ **实例本地库零改动**。 ⑥ 🔴 **未全绿**:`account_videos` **match=true**(362/362)· `hot_accounts` **match=true**(5/5)· **`account_video_source` match=false**(platform **34** / local **39**)⇒ 依执行单 §五 S6/S7 与任务书铁律 **停住不切档**。 🔴 **根因两条(均非数据丢失、均非本棒引入)**:**甲** = 本地 **5 组重复 `aweme_id`**(39 行 = 34 唯一键 + 5 旧副本;平台主键唯一 ⇒ 必然折叠;3 组副本逐字全同、2 组仅正文差 2 字节)|**乙** = 平台 PG `jsonb` **重排对象键序** × `canonicalJsonText()` **保留序** ⇒ 逐键比对**假阴性**(**与序无关**的深度比对 ⇒ **34/34 全等、差异 0**;`gz1=true` 的 11 行全 match、`gz1=false` 的 23 行全 mismatch,两侧完全对应)。 ⑦ ⛔ **未执行(整条留给第 36 棒)**:⛔ 未切任何表(实例档仍 **4 表 `platform`**,三表仍 `local`)· ⛔ 未重启 47 · ⛔ 未触碰 106。 📊 **A 判据**:**A2 达成(平台取数口面 —— 三表 7 步读写全 200 · 6 负例全 `bad-args` · 写他人 `user_id`/缺主键/带内核列/未知表/未知列全拒 · PG 侧残留 0)**|A3/A4/A6/A7/A9 达成(红线 **23** 零新增 · `account_video_analysis` **0** · `hot_account_ext` **10**)|**A1 两表达成、`account_video_source` 一支具名未达成**|**A5 未达成 · A2「实例内」面未达成(随 ⑦)**|**A8 如实记「两机不一致」**(47 = `0.5.0`;106 本棒未触碰未取证)。 🔴 **本棒动过生产**:47 共享层投放 + 平台库 2 条 `ADD COLUMN` + 存量回填;**未动实例本地库、未重启任何服务、未切任何档**。⛔ **未 commit / 未 push**。 🧹 **卫生**:R10 修复 —— 实例 home 的 `data-plane.json` 被**第 32 棒 root 探针**遗留为 `root:root`,已 chown 回实例 uid(`ROOT_OWNED_FINAL=0`);临时会话清理 `SESSIONS_BEFORE=7 → DELETED=7 → AFTER=0`;47 侧 `/tmp/p35*` 全删(`TMP_P35_CLEAN`);本机证据留 `$WS/tmp/p35/`。 📌 交付件 = `$WS/交付物/MCN数据面接入-阶段二-B案落地与shadow读数-20260925.md`(结论/①–⑦ 状态/A1–A9 命令+输出+退出码/**根因两条取证**/未闭合 8 条/**待拍板 2 项(含优缺点)**/卫生 + 域锁 + 产物指纹)。 ✅ **本棒 automation `cf072f77-347d-4939-b437-b0f3d1ec363e` 已置 `PAUSED`**;✅ **已登记下一棒 = 第 36 棒**(automation **`35e1bfbc-d247-4db2-9d90-b0580793736f`** · 一次性 · `2026-09-25T23:10` · `cwds = E:/ProgramData/AIProject/aliyun-dsh-server`)⇒ 范围 = 修 `canonicalJsonText()` 键序敏感 → 出包 `0.5.1` → 上池 → 重取影子读数 →(**若已拍板则**闭环 ⑥ + 执行 ⑦;**未拍板则停在 ⑥、⛔ 不切档**)。 ⏳ **本棒新增待拍板 2 项(⛔ 不在接续棒内自决 · 逐条带优缺点 · 详版见交付件 §5)**: **① 「本地 5 组重复 `aweme_id` 怎么处置」** —— A 案=删本地 5 条旧副本(优点:判据如实达成、读路径本就取最新一份不可见;**缺点:不可逆,其中 2 组旧副本正文差 2 字符**)/B 案=不删数据、改判据为「同一视频只算一次 + 逐条内容比对」(优点:零数据变更;缺点:改的是规划棒定死的判据,须回规划棒重改单子)/C 案=保持现状(优点:零风险;缺点:第三批永远到不了终点)。**倾向 A 案(可推翻)**,⚠️ 因不可逆**须用户点头**。 **② 插件包源码要不要纳入版本管理**(第 34 棒遗留㈣,本棒再次上抛)—— A 案=现在纳入(优点:逐笔可回滚/可对账;缺点:要动仓库并先定"哪些进")/B 案=暂不纳入(优点:零额外动作;缺点:近一月改动零留痕,回滚只能整包回退)。**倾向 A 案(可推翻)**。 - 🔴 **2026-09-25 21:33|用户拍板 —— 第 34 棒末尾 3 条已决(2 决 1 反问 · 后棒以此为准)** —— ① ✅ **测试服务器可以重启** ⇒ 47 上用户实例的**重启窗口已获批**(第 34 棒待拍板㈠ 解除); ② ✅ **106 可以合并**(用户原话「可以合并那是 worker」)⇒ **47 + 106 合并进同一次重启窗口**(⛔ 不要分两次); ③ ⚠️ **用户反问「是什么文件 为什么之前没有纳入」**(针对第 34 棒第 3 条待拍板「6 个改动文件要不要纳入版本管理」)⇒ 已由第 34 棒会话**只读取证查清**: · **是什么** = `D:/dshworkspace/plugin_package/dsh-plugin-mcn-suite/`(**现役投放包 · `0.4.0`**)里 `lib/host/mcn/` 下的**六个 js**:`store.js`(取数原语层)/`index.js`(handler 与后台任务)/`data.js`(工作台数据层)/`workshop.js`(工作台业务)/`video-tools.js`(视频落点)/`imports.js`(导入链路)—— 本线第 32–34 棒改的就是这六件(+ `tests/mcn-store.test.mjs`)。 · **为什么没纳入** = **无 commit,⛔ 不是被忽略**。两个原因叠加:⑴ 该目录在 `D:/dshworkspace` 仓库(remote `git@work.alotbuy.com:maogeigei/dsh_MCNWorkSpace.git`)里**从未被跟踪** —— `git ls-files` 命中 **0**、`git status` 显示 `??`、**`git check-ignore` rc=1(未被 ignore)**;⑵ 该仓库**最后一次提交停在 2026-08-27 11:34**(提交信息形态 = 外部 `sync: <时间>` 流程,8-27 之后未再跑)⇒ suite 建包与演化全部发生在这个窗口之后。 · **对照(同目录)**:**旧包** `dsh-plugin-mcn`(`0.1.0`)**有 13 个文件已入库** ⇒ **入过库的是旧包,现役 suite 从来没入过**。 · ⇒ 🔴 **第 35 棒 ⛔ 仍不 commit / 不 push**(用户尚未就「要不要纳入」拍板);**必须在收尾如实上抛这一条**。 ④ ⏳ **仍待拍板(本行未决,⛔ 别当已决)**:这些改动**要不要纳入版本管理**(影响什么:近一个月改动零版本留痕,回滚只能靠备份);`json` 列改声明(本批不需要,留档)。 - 🗃️ **2026-09-25 20:53–21:3x|第 34 棒 · 执行棒(`p34-exec-plugin-dataline` / 会话 `d25f7943-e143-4f7f-a595-8b090fed6e3f`)—— 接线收官 · ⛔ 未动生产 · 交棒第 35 棒** —— ① ✅ **S3 剩 1 处 + S4 + S5 全部落地**:`index.js` **22 处** + `store.js`(新增 `patchHotAccountSplitByName`/`patchHotAccountSplitByLocalId`:**拆表 PATCH**,主表列/扩展列各一半、无事务 ⇒ `partial`+`compensate`)⇒ 三张表在 `platform` 档**处处有改写面**,`local` 档**原 SQL 逐字保留在各 `local()` 回调里**(零回归)。 ② ✅ **静态复扫(判据 A3)**:全包 JOIN/`LIMIT ? OFFSET ?`/`ROW_NUMBER|OVER(` 的残留命中**全部**落在三类 —— `local()` 回调内 · 本地专属分支(`data.js` 平台档早返回后)· **第四批主表 `rewrite_log`(具名豁免)**;**平台档路径零命中**。 ③ ✅ **单测 62/62**(新增 52–55 共 4 例:拆表 PATCH 表路由 / 两半失败 `partial` / 映射缺席 `mapping-missing` / `local` 档零请求)· `node --check` 六文件 **rc=0** · 行尾**逐文件核对**(`store/data/index/workshop` 纯 LF,`imports/video-tools` 纯 CRLF)· 红线 **23(零新增)** · `src/**` 零改动 · `lib/client.js` 零改动(**A7 免跑**)· `dsh.data.yaml` 指纹与第 33 棒**逐字相同**。 ④ ✅ **第 4 件事(真机只读取数)已完成** ⇒ **处方 (a) 行内压缩有效**:`account_video_source` 39 行中 **12 行**超 64 KiB,**全为纯文本**(`base64runs=0`/`data:image=0`),`SUM row=926,064 → gzip=327,519`(ratio **0.3537**),压缩后最大 **41,728 B ≤ 64 KiB** ⇒ ⛔ **不需要改走 (b) 正文外置**。 ⑤ ⛔ **未达成(如实列,⛔ 不许含糊)**:**A2**(三表读写 E2E —— 需 47 实例重启窗口)· **A5**(三表切 `platform` —— 前置 `local_id` 列在真机库**仍未就位**)· **A8**(两机一致 —— 本棒未出包未投放 ⇒ 代码面/运行面**都是旧的**)· **A1 部分**(`account_video_source` 21/39 上台,余 12 行卡平台单行 64 KiB ⇒ 由 (a) 处方闭合,代码未落)。 ⑥ ⛔ **本棒未动生产**:⛔ 不出包 · ⛔ 不上池 · ⛔ 不加列迁移 · ⛔ 不回填(按 20:38 拍板属 B 案最后一步);⛔ 未 commit/push;⛔ 未删本地旧库 `mcn-plugin.db`;⛔ 未改 `D:/github/dsh_shenxian/src/**` 与 `D:/dshworkspace` 以外任何工作区。 ⑦ 📌 交付件 = `$WS/交付物/MCN数据面接入-阶段二-第三批接线完成-20260925.md`(结论/改动面 6 件指纹/A1–A9 判据表/index.js 22 处对照/未闭合 6 条/卫生声明/待拍板 4 项)。 ⑧ 🔴 **本棒已知观察(⛔ 未处置,属他人 lane)**:共享锁目录存在两处**非本线遗留物** —— `.exec-lock/OWNER = __st_other_1879`(疑似他会话冒烟残留,**独占锁被占**)+ `.locks/_____________________-1844878018/`(会话 `修域锁释放缺陷`,域 `dsh-server-docs/07-scripts`)。按 **R9** ⛔ **不删锁、不接管**,只如实记录。 ⑨ ✅ **本棒 automation `40d342f3-584e-46e3-a630-b589960ec859` 已置 `PAUSED`**;✅ **已登记下一棒 = 第 35 棒**(automation `cf072f77-347d-4939-b437-b0f3d1ec363e` · 一次性 · `2026-09-25T21:38` · `cwds = E:/ProgramData/AIProject/aliyun-dsh-server`)⇒ 范围 = **B 案最后一步**(落 (a) 行内压缩代码 → 出包 `0.5.0` → 上池 → 加列迁移 2 条 `ADD COLUMN` → 重跑迁移回填 `local_id` → 三表 `shadow` 读数);⛔ **本棒只做 `shadow` 读数、不逐表切 `platform`**(切档须等待拍板㈠ 47 实例重启窗口)。 - 🔴 **2026-09-25 20:38|用户拍板 —— 阻塞解除(唯一新拍板行 · 后棒以此为准)** —— ① 用户原话:**「修改完成后 一起上服务器」** ⇒ 第 33 棒上抛的**待拍板①「要不要现在动生产」落 B 案**:**等 `S3`/`S4`/`S5` 接线做完,再一次性出包 → 上池 → 加列迁移 → 回填**。⛔ 本拍板**不授权提前动生产**(⛔ 不许读成「可以先上池试试」)。 ② 用户原话:**「数据库存不下大文件吗,有没有比较好的处理方式」** ⇒ 问的是**待拍板②**(`account_video_source` 12 行超平台**单行** 64 KiB)的处置方向 ⇒ **属技术项 ⇒ 自决(可推翻)**。 🔧 **处方(技术自决 · 可推翻)**:先厘清口径 —— **不是"存不下",是「单行」有 64 KiB 上限**(设计取值、写死在 `runtime.ts:66`、**无配置口**;源码注释原文即「取值原则**(可推翻)**」)。三条路:**(a) 行内压缩**(正文 gzip 后入库;编解码只在 `normalizeRow`/`rowsOf` 两个唯一必经之路做 ⇒ **客户端零改动**。优点=平台库仍是唯一权威、不动平台、不新增链路;缺点=原始 SQL/数据面板看到二进制 ⇒ **运维可读性下降** + 每行加 CPU)· **(b) 正文外置**(写进用户文件层、行里只留引用 + 小摘要。优点=行永远小;缺点=🔴 与「插件数据以平台库为权威、实例文件**只可缓存**」的硬标准冲突 + 新增一条文件读写链路)· **(c) 抬平台行上限**(优点=最彻底;缺点=影响**全部插件**的单行内存与 V8 堆 ⇒ ⛔ **本线不动**,确需另立单子)。 ⇒ **采用 = 先 (a)**;⚠️ **(a) 是否有效取决于「那 12 行正文的构成」**(若混了 base64 图片 ⇒ 压缩率极低、(a) 失效 ⇒ 改走 (b))⇒ **第 34 棒第 4 件事 = 真机只读取一次数**(正文构成 + 可压缩率),据读数定 (a)/(b)。 ⇒ 阻塞解除 ⇒ **第 34 棒(执行棒)已登记**:automation `40d342f3-584e-46e3-a630-b589960ec859`(一次性 · `2026-09-25T20:50` · `cwds = E:/ProgramData/AIProject/aliyun-dsh-server`),范围见 §2「🗃️ 上一棒动作(第 34 棒 · 已收官)」(✅ 该棒已收官 ⇒ 当前依据 = §0 最新行 + §2「⏭️ 本轮动作(第 35 棒)」)。 - ⏸️ **2026-09-25 19:5x–20:2x|第 33 棒 · 执行棒(A 案双项**已全落地**:声明 + 代码 + 单测 · 🔴 **实测推翻「必须删库重建」** · 真机取证三条硬结论 · ⛔ 未动生产 ⇒ **交回用户拍板 2 项**)** —— ① ✅ **第 0 步 cwd 校验通过**(`/e/ProgramData/AIProject/aliyun-dsh-server`)+ `state.py` 一次调用取全状态。**第 1 步门禁如实记账**:`preflight-lock.sh` 对本棒 **11 个目标路径全判【D】未归类** ⇒ 按字面 `rc=1`;依第 26–32 棒**既有惯例**开工并报告,⛔ **未改那个跨库脚本**。锁 = **域锁 11 个键**(`--claim-exec "p33-exec-plugin-dataline" --domains …`)`rc=0`;复读 `.locks/p33-exec-plugin-dataline/{OWNER,DOMAINS}` **断言归属**(开始 `09-25 19:57`;会话 `ee21bf7d-0f0e-4c42-9abc-29e8e15bb774`)。 ② ✅ **A 案前半段(阻塞甲)落地**:`dsh.data.yaml` 给 `hot_accounts` / `account_videos` 各加**可空** `local_id`(text);`store.js` 新增 `localIdValueOf`(取号唯一口径)/ `restoreLocalId`(读侧还原数字 `id`)/ `localIdSupported` + `allocLocalId`(懒初始化扫 `max+1`、进程内单调)/ `attachLocalId`;**写侧折算**放 `normalizeRow()` 循环**之后**(显式 `local_id` 天然优先);`splitHotAccount()` 把 `id` 折算进**主表**(⛔ 扩展表不带);`upsertByPk()`「已存在」分支加 **`backfillLocalId()` 自愈回填**(**只补空、⛔ 不覆盖非空**)。🔑 **调用方零改动** —— 折算/还原都在 `normalizeRow` / `rowsOf` 这**两个唯一必经之路**上。 ③ ✅ **A 案后半段(阻塞乙)落地**:`account_video_source.content_json` / `analysis_json` 的 `maxBytes` **30720 ⇒ 128000**(声明 + `store.js` 的 `COLUMN_MAX_BYTES` 同步;⛔ 其它表一列未动)。迁移工具加 `backfilled` 计数(表级 + `parts` + `SUMMARY`)。⚠️ **声明侧当时漏落,已于 20:2x 补齐 —— 见 ⑬**。 ④ 🔴 **实测推翻「只能删库重建」**(本棒最重要结论):真机 `diffDecl` 预演 —— **OLD 与库现值零差异**(`items=0` / `unchanged 11 表 96 列`);**NEW = `items=2×add_column`、`forbidden=0`、状态 `drift`**;**`maxBytes` 改动零 diff 项**(`diff.ts` 只比类型、⛔ 不读 `maxBytes`)。⇒ 本案落地 = **2 条 `ADD COLUMN`(秒级、零数据损失)**,⛔ **不删库、⛔ 不重灌** ⇒ 原拍板告知的「不可逆 + 数据不可用 10~20 分钟」代价**不发生**。 ⑤ 🔴 **A 案后半段有效但不彻底**:`account_video_source` 逐行现算 —— 总 39|过原 30 KB 上限 **21**|过新 128 KB 列上限 **39**|**列上限+单行上限都过 27** ⇒ 可上台 **21 → 27(+6)**;**剩 12 行瓶颈在「平台单行 64 KiB」**(`runtime.ts:66 DEFAULT_QUOTA`,而 `plugin-data.ts:86` 构造时⛔ **未传 `quota`** ⇒ **无配置口**,47 上实测 grep 零命中)。⇒ 12 行要么留本地、要么动平台侧(⛔ 本棒禁改 `src/**`)⇒ **列为待拍板**。 ⑥ ✅ **真机新口径 dry-run(只读)**:`account_videos` **362/362 坏行 0**、`hot_accounts` **5/5 坏行 0**,两表清洗列集合均为 `["id->local_id"]`(**每行都带上了承载列**);`account_video_source` 计划 **27** / 坏行 **12**(全 `quota-exceeded`)/ **重复键 5 组逐组具名**(`7658997937279683855`[20,40]/`7648947073131625905`[27,28]/`7667477088350044069`[29,44]/`7631176055600527729`[31,42]/`7629803455813243043`[32,46]);`DRY_WRITE_CALLS=0` ✅。 ⑦ ✅ **判据面**:单测 **`# tests 58 / # pass 58 / # fail 0`**(原 51 + 本棒新增 **7**:㊺折算 ㊻还原 ㊼拆表 ㊽分配器 ㊾无列降级 ㊿上限 51回填)|**A9 红线仍 23(零新增 DDL)**|**A4 = 0 / 6** ✅|**A6 免跑**(改动面**恰 4 件**,`lib/client.js` 零改动)|改动 4 件**全纯 LF(CR=0,逐字节验过)**。 ⑧ ⏸️ **未达成(⛔ 不宣称通过)**:`S3` 剩 1 处、`S4`、`S5`、`S6`、`S7` **全部未开工**(落点现算 **89 处**:`data.js` 9/`imports.js` 36/`index.js` 30/`video-tools.js` 1/`workshop.js` 13)|`A2`/`A5`/`A8` 未达成|**`local_id` 列未就位**(真机库仍 13/16 列)⇒ 即使接线完成**也不能切档**(切了新行没 id ⇒ 前端 6 入口失效)。 ⑨ ⛔ **本棒未做出包 / 上池 / 加列迁移 / 回填**:这三步都要求**动生产**(门户候选池 + 平台库),且第④条已把原授权设想的动作形态从「重建」改为「加列」⇒ 依 R8 与「影响面」判据**列为待拍板**,⛔ 未自决。**清单 + 备份现状 + 影响面**已出(交付件 §六:6 步有序动作、分钟级窗口、已在运行实例不受影响、零数据损失、回滚素材在位)。 ⑩ 🔴 **卫生**:⛔ 未改 `src/**`|⛔ 未改 `im.ts`|⛔ 未改别的工作区文件|⛔ 未重启服务|⛔ 未投放|⛔ 未 commit·push|⛔ 未删任何库(旧库在位)|⛔ 未删 `db.js`|✅ 真机探针 `/tmp/p33/` **已整体删除**。 ⑪ 📄 **落地件** = 交付件 `$WS/交付物/MCN数据面接入-阶段二-A案落地与取证-20260925.md`(十节 · 纯 LF:结论/A 案落点/判据表 A1–A9/真机读数/**不需删库重建的判定**/R8 清单备份影响面/未闭合 8 条/**待拍板 2 项 A·B·C 案**/卫生/域锁)|探针 `tmp/p33/{plan33.mjs,dry33.mjs,new/,scan.txt,batch3-live-33.txt}`。⚠️ 副作用如实记账:现算落点脚本写死覆盖了 `tmp/p30/batch3-live.txt`(第 30 棒读数),现算副本已留 `tmp/p33/batch3-live-33.txt`。 ⑫ ⏸️ **本线不排棒**(automation `534dc721-080c-4a4b-8e72-ad48ec596c77` 已**置 PAUSED**):本棒撞**待拍板 2 项**(见 §2「🎯 本轮动作」的 ⏳ 块)⇒ 依用户 2026-09-17 明令「要用户拍板的,等拍了再新建接续会话」,⛔ **未登记下一棒**;拍板到手后由用户或新会话按「⏭️ 本线后续」重建。⚠️ **该状态已于 20:38 解除**(用户两条拍板 ⇒ 第 34 棒已登记)⇒ 见 §0 顶部 20:38 行。 ⑬ 🔴 **20:2x 补正(自查发现 · 已就地做实 · ⛔ 未引入任何新决策)**:复核时发现 `dsh.data.yaml` 里 `account_video_source` 的两个 `json` 列**实际仍是 `maxBytes: 30720`**(③ 说的「声明 + 代码同步」当时**只有代码面落了**)⇒ 按 A 案**原授权**补齐为 `128000`(+ 3 行说明注释)。**同轮新查明(口径纠偏)**:平台的**列级 `maxBytes` 全程没有任何强制点** —— `schema.ts:500-515` 只做声明期「正整数 + `json` 列必填」校验;`runtime.ts:316-319` 写入期查的是**行级** `quota.maxRowBytes = 64 KiB` + **行数**配额,⛔ 从不按列读 `maxBytes`(全仓 `grep -rn 'maxBytes' src/db/plugin-data/` 除 `schema.ts` 外**零命中**)⇒ **列上限的唯一实际把关人 = 插件侧 `store.js` 的 `COLUMN_MAX_BYTES`**,声明面改的是「自洽 + 文档」。补正期间**重新抢域锁 2 键**(`dsh-plugin-mcn-suite/dsh.data.yaml` + `aliyun-dsh-server/交付物`,`rc=0`,`20:22`),改完**已释放**。`dsh.data.yaml` 新指纹 = `d6627f1c647a8c27`/8,351 B/183 行(**全 LF,CR=0**);PyYAML 实测解析通过(11 表;`json` 列 `maxBytes` 全为整数;`hot_accounts` 14 列含 `local_id`、`account_videos` 17 列含 `local_id`)。🔴 **未重新出真机预演**:`diff.ts` 不读 `maxBytes` ⇒ 读数与 §4.1 逐字相同(`items=2×add_column` / `forbidden=0` / `drift`)。 - 🔴 **2026-09-25 19:4x|用户拍板 —— 第三批阻塞处置 = **A 案**(唯一拍板行,后续所有棒以此为准)** —— 用户原话:**「A」**。对应第 32 棒上抛的三案(原文 ⇒ `$WS/交付物/MCN数据面接入-阶段二-第三批阻塞取证与存量迁移-20260925.md §六`):**A 案** = ① 给「账号」与「视频」两类记录在平台侧补一个**稳定主标识**(等价于本地自增 `id` 所扮演的角色)② 把 `account_video_source` 的**正文列声明上限从 30 KB 提到 ≥100 KB**。 ⇒ **随之授权的代价(用户选 A 时已明示)**:这两项都要**动插件表结构**,而平台规矩禁止改结构 ⇒ **只能把该插件的库删掉重建**(不可逆;重灌约 10–20 分钟、期间该插件数据不可用)。✅ **已授权**;⛔ 但 33 棒执行前**必须先出清单 + 先备份 + 报清影响面**(R8 自律),⛔ 不得扩大范围。 ⛔ **仍未拍板、不得自决的项**:㈠ 47 实例重启窗口 / ㈡ 106 重启窗口 / ㈢ `json` 列是否改声明(**另一件事,⛔ 不因本次重建而顺带决定**)/ ㈣ 插件包源码是否纳入版本管理。⚠️ 特别提示:本棒会**天然遇到一次重建窗口**,但 ⛔ **不许**拿它当理由替㈢㈣做决定;如执行棒认为合并处置更优 ⇒ **只报告、请用户拍**。 ✅ **已排棒** = **第 33 棒 · 执行棒** = automation **`534dc721-080c-4a4b-8e72-ad48ec596c77`**(一次性 · `scheduledAt = 2026-09-25T19:55` · `cwds = E:/ProgramData/AIProject/aliyun-dsh-server`)。⛔ 本线**同时只挂一个棒**。 🔴🔴 **后棒必读(2026-09-25 19:5x 本会话实测事故)**:**释放锁必须写成 `ME="<会话名>" bash $DOC/07-scripts/handoff-guard.sh --release-exec`** —— ⛔ **不带 `ME=` ⇒ `ME` 为空 ⇒ 脚本会释放 `.locks/*` 下「**所有人**」的域锁**(`handoff-guard.sh:38` 的 `ME="${ME:-}"` + 释放分支 `[ -z "$_who" ] || …` 判据;claim 用 `$2` 取名而 release **没有名字参数** ⇒ **不对称陷阱**)。 实测后果:本次把另一个会话 `恢复浮层动画-上线` 的域锁**一并删掉**(该会话 17:1x 之后建锁 ⇒ 近期/可能在跑);锁根在**文档库** ⇒ **全平台各线共用**。⇒ 后棒**一律加 `ME=` 前缀**;⛔ 不许"人工重建"那把锁(= 反向接管);⛔ 不许擅改该跨库脚本(机制层,须独占开工 + 用户拍板)。 - ⏸️ **2026-09-25 16:5x–17:1x|第 32 棒 · 执行棒(首件事 + S1 增量完成 · 真机迁移完成 · 🔴 **三表切档全部不成立** · 撞两处结构性阻塞 ⇒ **交回用户拍板** · 本棒不切档、不写死代码)** —— ① ✅ **第 0 步 cwd 校验通过**(`/e/ProgramData/AIProject/aliyun-dsh-server`)+ `state.py` 一次调用取全状态。**第 1 步门禁如实记账**:`preflight-lock.sh` 对本棒 **11 个目标路径全判【D】未归类** ⇒ 按字面 `rc=1`;依第 26–31 棒**既有惯例**开工并报告,⛔ **未改那个跨库脚本**。锁 = **域锁 11 个键**(`--claim-exec "p32-exec-plugin-dataline" --domains …`)`rc=0`;复读 `.locks/p32-exec-plugin-dataline/{OWNER,DOMAINS}` **断言归属**(开始 `09-25 16:51`;会话 `3e06798e-6379-460c-bd88-6c5ac88b767f`)。 ② ✅ **首件事完成(本棒唯一入口)**:`index.js` `/mcn/api/videos` 加**平台档分支** `buildVideosPlatform()`(8 步:`locate("account_videos")` 读 `truncated` ⇒ 具名降级 / `hotAccountMetaMap` / `videoLocalIdMap` / 爆款窗 `ROW_NUMBER` **插件内归约** / `search`·`days` **插件内过滤** / 两索引判 `parsed` / 补 6 列 / 排序切片),返回 `{ok,total,items,missingLocalId,nameMissing}`;**`local` 档原路径整体保留**(`if (items === undefined)` 分流),⛔ 未删任何老逻辑。 ③ ✅ **S1 增量**:`store.js` 新增 `hotAccountMetaMap({local})`(`id ↔ account_name` 双向 + `missingId[]` + `truncated`)与 `videoLocalIdMap({local})`(`aweme_id ↔ id` 双向)——**不新增任何写路径**,只补 D16 映射缺的两块拼图。 ④ ✅ **迁移工具扩三表**:`DEFAULT_TABLES` + `keyBuilders` 追加 `account_video_source`/`account_videos`/`hot_accounts`;新增 `splitPayload()`(`hot_accounts` ⇒ 主表 + `hot_account_ext` **拆表两写**)与**跨行重复主键预检**(`dupKeys`,⛔ 不许"最后一行赢");CLI 新增 `PART` 与 `🔴 DUP-KEYS` 段。 ⑤ ✅ **单测 `# tests 51 / # pass 51 / # fail 0`**(新增 ㊸`hotAccountMetaMap`/㊹`videoLocalIdMap`;local 档零请求、platform 档 `missingId` 具名)。 ⑥ ✅ **真机存量迁移完成(47 · 幂等 · 零数据损失)**:`account_videos` **362/362 `match=true`** | `hot_accounts` **5/5 `match=true`**(主表 + 扩展表两写)| `account_video_source` **18 行上台 / `match=false`**(见⑦);**幂等重跑全 skip**(`skip=21/362/10`、`DRY_WRITE_CALLS=0`);**旧库 sha256 迁移前后逐字一致 `b37f8aab51a90f74…`(2,383,872 B,mtime 未变)**⇒ **可回滚**;临时会话 **2 造 2 删剩 0**;`/tmp/p32` 已整体删除。 ⑦ 🔴 **两处结构性阻塞(本棒核心产出,⛔ 后棒不得绕过)**:**甲** = `account_videos` / `hot_accounts` 的**平台声明面无自增 `id` 承载位**,而前端以 `row.id` 为主标识(`client.js` **6 处**:切收藏 `:3456`·AI 视频 `:3675`·AI 创作 `:3714`·账号详情 `:3782`·删除 `:4579`·视频详情 `:4669`)⇒ 切档后**新写入行无 id** ⇒ 上述 6 个入口全失效|**乙** = `account_video_source` **18/39 行正文超自己声明的 30 KB 上限**(`content_json` 3 行 / `analysis_json` 15 行,最大 **94,889 B**)+ **5 组重复 `aweme_id`**(`7658997937279683855`/`7648947073131625905`/`7667477088350044069`/`7631176055600527729`/`7629803455813243043`)⇒ 平台侧最终只有 **18 键**,`A1 match=false`。 ⑧ 🔴 **自律判定(本棒第二重要的一条)**:由⑦ ⇒ **三表切档全部不成立**;因此**拒绝**继续写 `data.js`/`imports.js`/`workshop.js`/`index.js` 约 **30 处**平台档接线 —— 写了 = 生产包里**永不被走到的死代码**(R11 可维护性净变差)⇒ **本棒到此为止,如实上抛**。 ⑨ ✅ **判据面**:红线 **A9 仍 23(本批零新增 DDL)**|**`src/**` 零改动**|⛔ 未改 `im.ts`|⛔ 未重启服务|⛔ 未投放|⛔ 未 commit·push|⛔ 未删任何库(**旧库 `mcn-plugin.db` 在位**)|**A6 免跑**(`lib/client.js` **零改动** ⇒ 移动端五条免跑);改动面 **恰 4 件**(`index.js`/`store.js`/`tools/migrate-local-to-platform.mjs`/`tests/mcn-store.test.mjs`),**全纯 LF**。 ⑩ 🔴 **自证一条(⛔ 后棒别照抄本棒探针)**:`run-p32.mjs` 第 5 步把 `account_videos`/`hot_accounts` 设 `shadow` 后**未回滚**,第 6 步无条件切了 `account_video_source` ⇒ 跑完 `data-plane.json` 残留两表 `shadow`(**与 04 单 S7「全 `match=true` 才可切」冲突**)⇒ 当场用 `rollback32.mjs` 对三表 `clearPlaneMode()` 恢复原状(**仍是原 4 表 `platform`,本批三表回 `local`**),并把这件事**如实写进交付件 §4.6**(含偏差说明)。 ⑪ ⏳ **未达成(⛔ 不宣称通过)**:`A1`(`account_video_source` `match=false`)|`A2`/`A3`/`A5`/`A8` **需 47 实例重启窗口 / 出包投放** ⇒ **用户窗口**,如实列为「未达成」|`S4`/`S5`/`S6-S7 切档` **未开工**(等拍板)。 ⑫ 📄 **落地件** = 交付件 `$WS/交付物/MCN数据面接入-阶段二-第三批阻塞取证与存量迁移-20260925.md`(九节 · 纯 LF:结论/完成项/判据表 A1–A9/真机读数/两处硬阻塞/**待拍板 A·B·C 案**/未闭合项/卫生声明/域锁)|探针 `tmp/p32/{scan32.py,wiring.py,run-p32.mjs,orchestrate.sh,rollback32.mjs,diag32.mjs}` + 读数 `tmp/p32/{p32-live.out,scan32.out,wiring.txt,idx-a.txt}`。**本棒为收口最后一棒**:automation **`d33a6d61-3612-4b92-bcb2-28dc818aebd6`** 已**置 PAUSED**,**全线无在跑/在等的接续会话** —— **下次开工前必须先拿到⑦两项的拍板**(倾向 **A 案**:补 `id` 承载位 + 提正文列上限;备选 B=只搬 `account_videos`、C=本批打住)。 ⑬ ⛔ **本棒未排任何接续棒**(用户规则:上抛未决 ⇒ 不登记下一棒);拍板到手后由用户或新会话按「⏭️ 本线后续」重建。 - ✅ **2026-09-25 16:0x–16:4x|第 31 棒 · 执行棒(S0/S1/S2 完成 · S3 20/23 · **三表一处未切档** ⇒ **切换条件不成立** · 四步链路未走完)** —— ① ✅ **第 0 步 cwd 校验通过**(`/e/ProgramData/AIProject/aliyun-dsh-server`)+ `state.py` 一次调用取全状态。**第 1 步门禁如实记账**:`preflight-lock.sh` 对本棒 **11 个目标路径全判【D】未归类**(工作区根 + 文档库 `05-交接单` 路径都不在脚本覆盖内)⇒ 按字面应 `rc=1`;依第 26–30 棒**既有惯例**开工并报告,⛔ **未改那个跨库脚本**。锁 = **域锁 12 个键**(`--claim-exec "p31-exec-plugin-dataline" --domains …`)`rc=0`;复读 `.locks/p31-exec-plugin-dataline/{OWNER,DOMAINS}` **断言归属**(会话 `0fd6ddc4-3990-4f46-8689-2fe9b7fed5fe`)。 ② ✅ **S0 九条读数全部落地(P1–P9)**:P1 红线 **23** ✅|P2 落点 **82 处** ✅|P3 JOIN **11 真 + 1 假阳性** / `OFFSET` **3 处** / 窗口函数 **3 处** ✅|P4 `hot_accounts` **25 列** ✅|P5 空键 **0** / 跨账号重复键 **0 组** ✅|P5b 行数 `hot_accounts` **5** / `account_videos` **362** / `account_video_source` **39** ✅|P6 `account_videos.account_id` = `INTEGER` ✅|P7 备份在位 809,732 B / `6b09e368…`,源库 `b37f8aab…` ✅|P8 现档 **4 表 platform**、本批三表仍 `local` ✅|P9 **平台 `buildWhere()` 能力边界现算结出结论**(只 `col = $n` 与 `IS NULL`;**无** `neq`/`IN`/范围/`LIKE`/`OR`)✅。 ③ ✅ **S1 完成**:`store.js` 新增「八、第三批按表切换」整段(1394 → **1980 行**),**31 个新导出**(`accountNameMap`/`videoSourceCountOf`/`loadVideoSourceIndex`/`topVideosOfAccount`/`sumLikesOfAccount`/`removeVideosByLocalIds`/`upsertAccountVideo`/`patchHotAccountByName`·`ByLocalId` …),`node --check` 全绿。 ④ ✅ **S2 完成(4 处跨批衔接面)**:`workshop.js` 新增 `applyCurrentAccountNames()`(覆盖显示名 + 与平台**现存键集合对账** ⇒ 对不上者进 `staleNames[]`,⛔ 不静默、⛔ 不假装最新);`listCustomScripts`/`getCustomScriptRow` 改 **async**;`index.js:3076`(`recent-scripts` 路由)接线。⚠️ **`workshop.js:199` 计数处有意未改**并写明理由(只取 `COUNT(*)`、不读 `account_name` ⇒ 改了是净增风险)。 ⑤ 🟡 **S3 完成 20/23 处**:`imports.js` **15 处**(含 `readVideoSource`/`readVideoParse`/`backfillVideoSource` 改 async + 写入统一走 `store.writeVideoSource()`,`local` 回调内**逐字保留**原 SQL 与 `added++`/`analyses++`)+ `data.js` **2 处**(`queryAccountVideos` 平台档补算 `has_source`;索引 `truncated` ⇒ **具名降级回本地子查询**)+ `workshop.js` **2 处** + `index.js` **3 处**。 ⑥ 🔴 **剩 1 处 + 「不做切档」的自律判定(本棒最重要的一条)**:`index.js:1558` 那条 SQL **同时写 `account_videos` + `account_video_source` + `SUM() OVER`** ⇒ 与 S4/S5 **同一条 SQL** ⇒ 本棒**不改**;因此 **三张表一处未切档**(`data-plane.json` 三表仍 `local`)—— 此时切档会出现「**写到了没人读的地方**」的最隐蔽坏态(R11 净变差、**界面看起来完全正常**)⇒ **切换条件不成立**。 ⑦ 🔴 **S6 迁移未做(本棒未写任何平台行、无半迁移残留)**:§五 把迁移排在**该表全部读写点改完之后**(S3/S4/S5 之后)⇒ 按序不动。 ⑧ ✅ **判据面**:单测 **`# tests 49 / # pass 49 / # fail 0`**(新增 9 例 ㉞–㊷ 钉住:D16 映射具名降级/拆表两写 `partial`/空 `aweme_id` 具名拒收/双键 OR 拆两次 eq/游标翻页不重不漏/窗口归约+非等值过滤/D20 `String()` + `replace` PATCH/无范围删除拒绝/S2 覆盖+`staleNames`)|**A9 红线仍 23(本批零新增 DDL)**|**A6 免跑**(改动面**恰 6 件**:`store/data/imports/index/workshop.js` + `tests/mcn-store.test.mjs`,`lib/client.js` 零改动 ⇒ 移动端五条免跑)|**A4 逐条过**:⛔ 未改 `src/**`(`find src -newermt` = **0 件**)|⛔ 未改 `im.ts`|⛔ 未重启服务|⛔ 未投放|⛔ 未 commit·push|⛔ 未删任何库。 ⑨ 🔴 **行尾字节级判定(A4 面)**:从 `dsh-plugin-mcn-suite-0.3.13.tgz` 取基线 ⇒ **`imports.js` 基线本来就是全 CRLF**(592/0),改后仍全 CRLF(825)=**无污染**;`index.js`(0/3095)与 `workshop.js`(0/303)本来就纯 LF ⇒ **无混行尾**。 ⑩ ⏳ **未闭合 6 条(⛔ 不宣称通过)**:㈠ S3 剩 1 处(`index.js:1558`)|㈡ **S4 未开工**(`account_videos` 全量切换:游标 + 2 处 `ROW_NUMBER()` + 4 处 `IN` 逐主键 + `String()` 对齐)|㈢ **S5 未开工**(`hot_accounts`+`ext` 拆表 + 5 写点 + 11 JOIN;**首件事 = 核实 5 处写点是否都拿得到 `account_name`**)|㈣ **S6/S7/按表切档 未走**|㈤ **D16 映射的新鲜度缺口**(映射只能来自本地 ⇒ 切档后新建账号会缺席;已加对账 + `staleNames[]` 具名暴露,两条候选见交付件 §七)|㈥ **A1/A2/A3/A5/A8 未达成**(需 47 实例重启窗口 / 出包投放 ⇒ **用户窗口**,如实列为「未达成」)。另:平台侧 `src/db/plugin-data/runtime.ts` 对非法值返回 `reason:"disabled"` **语义误导**(第 25 棒已记档,本棒复核仍在,⛔ **只报告未改**)。 ⑪ 📄 **落地件** = 交付件 `$WS/交付物/MCN数据面接入-阶段二-第三批按表切换-20260925.md`(九节 · 纯 LF)|探针 `tmp/p31/{p4-8-47.sh,find-db-47.sh,fix-awaits.py,append-tests.py,check-eol.py,p2-batch3-live.txt,p4-8-47.out,test-out.txt}`。**下一棒(唯一)** = **第 32 棒 · 执行棒** = automation **`d33a6d61-3612-4b92-bcb2-28dc818aebd6`**(一次性 · `scheduledAt = 2026-09-25T16:49` · `cwds = E:/ProgramData/AIProject/aliyun-dsh-server`);接续点 = **先把 `index.js:1558` 那条三表混写 SQL 一次改到位 → S4 → S5 → S6 → S7**。⛔ 本线**同时只挂一个棒**;别线 `ACTIVE` 一律未动。 - ✅ **2026-09-25 15:4x–15:5x|第 30 棒 · 规划棒(只出交接单 · 零落地)** —— ① ✅ **交接单已出**:`D:/github/dsh_shenxian/dsh-server-docs/05-交接单/插件投放与分库线-04MCN数据面接入改造-阶段二第三批.md`(8 段齐全 + 附录 A 逐处落点/B **四类改写口径定死**/C 存量迁移口径/D **三条口径修正**;**42,260 B / 349 行 · 行尾纯 LF(CR=0)** · `docs-audit.py` **rc=0**)。占号 = `mkdir 05-交接单/.lock-04`(原子)+ `ls` 确认 `-04` 空位。**单子范围 = 阶段二第三批(主战场)**:`hot_accounts`(拆表 + 5 处写点 + 11 处 JOIN)+ `account_videos`(362 行 · 游标 + 2 处窗口函数)+ `account_video_source`(双键 OR + 空键具名拒收)三表全读写点切平台取数口 + 存量迁移 + **按表切 `platform`**(阶段二切档收尾)。 ② 🔴 **三条口径修正(本棒现算 · 后棒一律以此为准,⛔ 别抄父单/入口旧记数)**:㈠ **「12 处 JOIN」= 11 处真实 SQL JOIN + 1 处假阳性** —— 第 26 棒把 `index.js` 的 `UPDATE hot_accounts SET ${sets.join(", ")}` 当成 SQL `JOIN`(**JS 数组 `.join(` 未剔**);全清单 JOIN 14 处 = **11(含 `hot_accounts`,本批)+ 2(`script_review`,第四批)+ 1(假阳性)**|㈡ **「2 处聚合」实为 3 处窗口函数、`GROUP BY` 全包零命中** —— `ROW_NUMBER() OVER` **2 处**(`index.js:1513` / **`2870`**)+ `SUM() OVER` **1 处**(`index.js:1560`);⚠️ `index.js:2870` **不在 `.prepare(` 调用点**(在 `c(\`SQL\`)` 辅助函数里)⇒ **第 26 棒扫描漏了它**(与第 29 棒"写在别人 SQL 里的读点"同类)⇒ 🔴 **后棒排查一律按 SQL 文本扫,⛔ 不按调用点扫**|㈢ **`OFFSET` 第三批 = 3 处**(`data.js:75`/`data.js:107`/`index.js:1558`),第 4 处 `workshop.js:206` 主表是 `rewrite_log` ⇒ 第四批。 ③ 🔴 **两处结构性风险已具名写进单内**:㈠ **`account_videos` 本地 `UNIQUE(account_id, aweme_id)` ≠ 平台主键 `(user_id, aweme_id)`** ⇒ 跨 `account_id` 重复 `aweme_id` 的行平台会**主键冲突** ⇒ 迁移前出清单 + 迁移时具名报告(⛔ 不许"最后一行赢")|㈡ **`account_video_source` 平台主键首列 = `aweme_id`,而本地该列是 `ALTER` 加的可空列** ⇒ 空值行平台**落不了** ⇒ `badRows` 具名拒收(**预计非零**,执行棒须现算行数)。 ④ 🔴 **一条本棒新发现的连贯性缺口(不修就留坏态)**:**4 处"跨批衔接面"** —— `index.js:3076` + `workshop.js:199`/`202`/`213` 的 SQL 是 `rewrite_log r LEFT JOIN hot_accounts h`(**主表属第四批、从表是 `hot_accounts`**)⇒ 本批把 `hot_accounts` 切 `platform` 后**本地 `hot_accounts` 不再被写**,而这 4 处仍读本地 ⇒ **账号名永久停在切档那一刻(R11 净变差、界面看起来完全正常)** ⇒ 已定为**本批必做**(§附录 A-5 + §五 S2:只改 `h.account_name` 取值方式,⛔ 不动 `rewrite_log` 自身语义)。 ⑤ ✅ **列面已定论**(父单 §四 4.2-1 的"差异未定论"本棒结案):本地 `hot_accounts` **25 列** = 建表 14(含 `id`)+ `ALTER` 11;与声明面**逐列对齐** —— 主表 13 + 扩展 12(`account_name` 同键)= 25 槽位,扩展 11 列 ≡ `ALTER` 列。`account_videos` 17(含 `id`)≡ 声明 16;`account_video_source` 7(含 `id`)≡ 声明 6。 ⑥ ✅ **四类改写口径已定死**(§附录 B)**:读改写(`JOIN` ⇒ 两次单表查 + 插件内合)/写改写(拆表两写 · 无事务 ⇒ 具名 `partial` + `compensate`)/`OFFSET` ⇒ 游标(+**必须读 `truncated`**)/窗口函数 ⇒ **插件内归约**(⛔ 不是 `/count`;仅 `COUNT(*)` 型走 `/count`)。另定 **D15 非等值谓词一律插件内过滤**(平台 `eq` 只做等值 ⇒ `IS NULL`/`<>`/`IN`/`OR`/范围 **6 类逐处列出**)+ **D16 关联键换轨**(平台无自增 `id` ⇒ 对外关联走 `aweme_id`;`hot_accounts` 走 `account_name` + 插件内 `id↔name` 映射)+ **D19 具名拒收** + **D20 `account_id` 类型对齐**(本地 `INTEGER` ⇒ 平台 `text`,写侧一律 `String()`)。 ⑦ ✅ **可直接复用(⛔ 不重造)**:拆表四函数 `splitHotAccount`/`mergeHotAccount`/`findHotAccounts`/`upsertHotAccount` + `HOT_MAIN_COLUMNS` + `TABLES[].split` **全部已在位**(第 27 棒,本棒逐行核实);`locate`(自带 `afterCursor` + `truncated`)/`loadVideoAnalysisIndex`(拉全量建索引手法)/`accountIdsWithPersona`(同手法先例)/迁移工具 `KEY_BUILDERS`·`DEFAULT_TABLES`·`resolveLocalTable` 均**已备**。 ⑧ ⏳ **未闭合(⛔ 不宣称通过)**:三张表的调用点**一处未改**(本棒零落地);红线仍 **23**(本批预期**零新增**);实例内端到端**待 47 重启**(待拍板 ㈠);出包 `0.5.0`/建库/投放/两机一致(S7–S11)**全未开工**;**本线待拍板仍 5 项**(本棒**一项未新增、一项未动**)。 ⑨ **卫生(A7 逐条过)**:⛔ 未改 `src/**`(只读 grep)|⛔ 未改 `im.ts`|⛔ 未改 `D:/dshworkspace` 下任何文件(**只读**)|⛔ 未重启任何服务|⛔ 未投放|⛔ 未 commit·push|⛔ 未删任何库|✅ 未 ssh(本棒全程本机)。 ⑩ 📄 **落地件** = 上述交接单 04 + 探针 `tmp/p30/{slice-batch3.py,extract-batch3-sql.py,scan-batch3-live.py,scan-struct-live.py}` + 读数 `tmp/p30/{batch3-rows.tsv,batch3-sql.txt,batch3-live.txt}`(**82 处逐处 `file:line` + SQL 原文**,供执行棒对照)。**下一棒(唯一)** = **第 31 棒 · 执行棒** = automation **`8c76acb5-d010-4bf8-83f0-1074cdd98a35`**(一次性 · `scheduledAt = 2026-09-25T16:01` · `cwds = E:/ProgramData/AIProject/aliyun-dsh-server`);接续点 = **按 04 单执行第三批**。⛔ 本线**同时只挂一个棒**。别线 `ACTIVE` 一律未动。 - ✅ **2026-09-25 15:1x–15:3x|第 29 棒 · 执行棒(第二批按表切换完成 · 四步链路走完 · 真机读数全绿)** —— ① ✅ **两张表的**全部**读写点已接线**:`account_analysis` **6 处**(导入 2 写 + 连坐删 1 + taskDone 计数 1 + 账号分析最新一条 1 + 预览判据 1)+ `video_analysis` **10 处**(导入 2 写(去重 / 删旧保新两语义)+ `deepVideos` 连坐删 1(两 scope)+ taskDone 计数 1 + **视频总表的 `parsed` 过滤条件** 1 + **同表 `has_analysis` 输出列** 1 + 视频详情 1 + 桌面统计 1 + 账号视频列表平台档补列 1)。改动面**恰 7 件**(`store/index/imports/workshop/data.js` + `tools/migrate-local-to-platform.mjs` + `tests/mcn-store.test.mjs`);**`lib/client.js` 零改动 ⇒ A6 移动端五条免跑**。 ② 🔴 **新增一件交接单没写、但不做就不成立的东西:`loadVideoAnalysisIndex()`** —— `video_analysis` 的两处读点**写在别人的 SQL 里**(视频总表的 `parsed`(进 `WHERE`、影响分页/总数)与 `has_analysis`(输出列))⇒ 平台无 JOIN ⇒ 口径 = **一次拉全量建插件内索引**,再用它①拼 `v.id IN (<字面 int 列表>)` ②补列(同第 28 棒 `has_content` 手法)。索引拿不全 / 平台不可用 ⇒ **具名降级回本地子查询**(⛔ 不拿半份冒充全量)。 ③ ✅ **存量迁移真机完成(47 · R8 自律)**:本地 `account_analysis` **1 行 → 平台 1**(首跑 `ins=1`)|本地 `video_analysis` **12 行 → 平台 12**(首跑 `ins=12`);**幂等重跑 `ins=0/skip=1`、`ins=0/skip=12`**;逐表 **`onlyLocal=[] / onlyPlatform=[] / match=true`**(`truncated=false`);**内容往返 13/13 逐字相等**(`jsonTextToRaw(平台值) === 本地原文`,12/12 解包后以 `# ` 开头 = Markdown 原文)。**旧库 sha256 迁移前后逐字一致 `b37f8aab…`**(2,383,872 B,mtime 仍 `09-20 23:05:05`)⇒ **零数据损失 · 可回滚**。`json` 列按附录 C 的 A 案清洗(13/13 具名进 `coerced[]`),⛔ **未执行任何删库**。 ④ ✅ **`shadow` 档真机读数全绿**:4 次影子读**全部 `match=true`**(`account_analysis` 计数 1/1 · 逐账号 1/1 · `video_analysis` 12/12 · 计数 12/12)/ **`shadowMismatch=0`** / **`degraded` 一次未出现** / 平台调用 **44 次全 ok、0 失败**;影子读一律 `source:"local"`(零回归)。 ⑤ ✅ **已切档(只切这两张表)**:`data-plane.json` tables 现为 **4 表**(`account_persona` + `creative_log`(第一批保留)+ `account_analysis` + `video_analysis`),**`hot_accounts` / `account_videos` 仍 `local`**(⛔ 没有"顺手全切")。回滚 = 删该表那一项或 `clearPlaneMode('<表>')`。 ⑥ 🔴 **本棒自证抓到并修掉两处**(均属"不修就留坏态"):㈠ **`videoAnalysisCountOf` 双计** —— 初版两路相加,而本地语义是 `video_id=? OR aweme_id=?` ⇒ 同一行两键都命中会被**计两次**(本地 1、这里 2)⇒ 改**逐行 OR 判定**,单测 ㉚ 钉住;㈡ **`previewExternalDir` 三处「是否已存在」判据** —— 其中 `account_persona` 那处是**第 28 棒的遗留读点**(已切表但预览仍读本地 SQL)⇒ 三处统一走 `store.analysisExists()`(函数改 async)。 ⑦ 🔴 **两条实测事实(后棒一律以现算为准)**:㈠ **本地库表名还是旧名** —— `sqlite_master` 现算 = `account_video_analysis`(22 字符),第 27 棒在 `db.js` 的**幂等改名只在插件新代码跑起后才生效**(实例仍载 `0.3.15`)⇒ 迁移工具已加 `resolveLocalTable()`(平台名不存在才回退旧名,旧名按**拆写常量**写以守住 A4)|㈡ **`video_analysis` 平台主键 = `aweme_id`(天然键、不可为空)** ⇒ 空 `aweme_id` 的行平台侧落不了、迁移**具名拒收**(`badRows`),⛔ 不静默丢(**本地实测 0 行**)。另:红线现算仍 **23**(本棒**零新增 DDL**);`prepare/exec` 行口径现算 **230**(`client.js`2/`data.js`13/`db.js`40/`imports.js`51/`index.js`95/`video-tools.js`1/`workshop.js`28)。 ⑧ ⏳ **未闭合(⛔ 不宣称通过)**:㈠ **实例内端到端未取证**(实例仍载 `0.3.15` ⇒ 生效需 47 实例重启,**待拍板 ㈠**)|㈡ **出包 `0.5.0` / 投放 / 两机一致(S7–S11)全未开工**(包仍 `0.4.0`、共享层仍 `0.4.0`)|㈢ **红线 23 未归零**(S10 删 `db.js`,属第三批收口)|㈣ **第三批未动**(`account_videos` 362 行 + `hot_accounts` 拆表 + 12 JOIN + 4 OFFSET + 2 聚合 = 主战场 + `account_video_source` 等)|㈤ `json` 案 B 仍待拍板。 ⑨ **卫生(A7 逐条过)**:⛔ 未改 `src/**`(`find src -newermt 15:15` = **0 件**,最新 mtime `12:20`;`im.ts` 零改动)|⛔ 未手改用户 `home` 下 profile/`.pnpm`(唯一写入 = 该用户自己的**插件档位文件**,即阶段一设计的回滚落点;本地库**只读**打开)|⛔ **未重启任何服务**|⛔ 未 commit·push|⛔ **未投放任何版本**|⛔ 未删任何库|✅ 临时会话 `1 → 删 1 → 残留 0`|✅ 47 `/tmp/p29*` 残留 **0**。 ⑩ 📄 **落地件** = `交付物/MCN数据面接入-阶段二-第二批按表切换-20260925.md`(判据表 A1–A8/A1′ + 逐处落点表 + 具名豁免 + 迁移读数 + 未闭合项)|探针 = `tmp/p29/{pre-a.sh,pre-b.sh,run-p29.mjs,orchestrate.sh,local-migrate-check.mjs}`。**下一棒(唯一)** = **第 30 棒 · 规划棒** = automation **`a22a3713-8cf9-4d90-b42d-0f7b656bf059`**(一次性 · `scheduledAt = 2026-09-25T15:44` · `cwds = E:/ProgramData/AIProject/aliyun-dsh-server`);接续点 = **第三批(`account_video_source` + `account_videos` + `hot_accounts` 拆表读写)的改造口径定稿**(见 §2 顶部)。⛔ 本线**同时只挂一个棒**。别线 `ACTIVE` 一律未动。 - ✅ **2026-09-25 14:43–15:0x|第 28 棒 · 执行棒(两件全做完 + 真机读数全绿 —— 写源档落地 · 第一批两张表切换到档 · 存量迁移完成)** —— ① ✅ **写源档落地(第 1 件)**:`store.js` 新增 `WRITE_MODES`(`local`/`platform`) + `dataPlaneWriteMode(table?)` + `setDataPlaneWriteMode(mode, table?)` + `writeThrough()` 分发器,**与读源档同源同文件**(`/.dsh/data-plane.json`:env `MCN_DATA_PLANE_WRITE` → `writeMode` → `local`)。**单测 31/31 通过**(旧 24 全过 + 新增 7 例 ⑱–㉔:默认档**零平台写请求**/非法值折回/`platform` 真发写请求/**失败⛔ 不回落本地**/按表档/两档合并写/json 清洗/稳定键)。🔴 **修掉一个既有隐患**:`setDataPlaneMode()` 原来**整份覆盖**配置文件 ⇒ 读写两档共用一份时必然互相抹掉 ⇒ 改为**合并写**(单测 ⑳ 钉住)。 ② 🔴 **补了交接单没写、但不做就不成立的东西:按表档** —— 全局单档**表达不了逐表切换**(一张表迁完、其余没迁时切全局读档 ⇒ 未迁的表**读到空**)⇒ 支持 `tables:{ <表>:{mode,writeMode} }` 覆盖,顺序 = env(全局)> 该表覆盖 > 全局 file > `local`;`clearPlaneMode()` = 一键回滚。**这才是"切最小单位 = 表"的可执行形态**。 ③ ✅ **第一批两张表的全部读写点已接线(含删除)**:`account_persona` = `imports.js` 2 写 + `workshop.js` 级联删 1 处 + `index.js` 计数读 1 处 + 视频列表 `has_content` 子查询 1 处(平台档下改为**插件内补**)+ 3 处既有读点的 `keyOf` 统一为 `store.personaKeyOf`|`creative_log` = 全包唯一写点 1 处。改动面**恰 6 件**(`store/index/imports/workshop.js` + `tests/mcn-store.test.mjs` + **新增** `tools/migrate-local-to-platform.mjs`),**`lib/client.js` 零改动 ⇒ A6 移动端五条免跑**。 ④ ✅ **存量迁移真机完成(47 · R8 自律)**:本地 `account_persona` **4 行 → 平台 4 行**,逐账号 **`onlyLocal=[] / onlyPlatform=[] / match=true`**(1627 1/1 · 1628 1/1 · 1629 2/2);**幂等重跑 `ins=0 / skip=4`**;`creative_log` 本地 0 行 ⇒ 空迁移。**旧库 sha256 迁移前后逐字一致 `b37f8aab…`**(2,383,872 B,mtime 仍 `09-20 23:05:05`)⇒ **零数据损失 · 可回滚**(A5 面)。⚠️ `json` 列**按附录 C 的 A 案**清洗(4/4 行被包成 JSON 字符串,**具名**进 `coerced[]`),⛔ **未执行任何删库**。 ⑤ ✅ **`shadow` 档真机读数全绿**:`shadowReads=4` / **`shadowMismatch=0`** / 三账号 `match=true` / **`degraded` 一次都没出现** / 平台调用 **27 次全 ok、0 失败**;影子读一律 `source:"local"`(零回归)。 ⑥ ✅ **已切档(只切这两张表)**:`data-plane.json` = `{mode:"local", writeMode:"local", tables:{account_persona:{platform,platform}, creative_log:{platform,platform}}}` ⇒ `hot_accounts` 等**仍 `local`**(⛔ 没有"顺手全切")。回滚 = 删该文件或 `clearPlaneMode()`。 ⑦ ⏳ **未闭合(⛔ 不宣称通过)**:㈠ **实例内端到端未取证**(实例仍载 `0.3.15`,**尚无本棒代码**;生效需 47 实例重启 ⇒ **待拍板 ㈠**)|㈡ **出包 `0.5.0` / 建库 / 投放 / 两机一致(S7–S11)全未开工**(投放会让新版对所有登录用户可见可启 ⇒ 与 ㈠ 一起排)|㈢ `creative_log` **无任何读点** ⇒ 切档只能"写侧单边取证"|㈣ 其余 9 张表未切(`hot_accounts` 拆表+12 JOIN+4 OFFSET 主战场、`account_videos` 362 行)|㈤ `json` 案 B 仍待拍板(读侧两形态长期存在)。 ⑧ **卫生(A7 逐条过)**:⛔ 未改 `src/**`(`im.ts` 零改动)|⛔ 未手改用户 `home` 下 profile/`.pnpm`(唯一写入 = 该用户自己的**插件档位文件**,即阶段一设计的回滚落点)|⛔ **未重启任何服务**|⛔ 未 commit·push|⛔ 未投放任何版本(共享层仍 `0.4.0`)|⛔ 未删任何库|✅ 临时 admin 会话 `1 → 删 1 → 残留 0`|✅ 探针落 `/tmp/p28/**`(已清)。 ⑨ 📄 **落地件** = `交付物/MCN数据面接入-阶段二-第一批按表切换-20260925.md`(判据表 C1–C5/A1–A8 + shadow 读数原文 + 逐处落点表 + 三个技术判定 + 未闭合项)|探针 = `tmp/p28/{local-migrate-check.mjs,run-p28.mjs,orchestrate.sh,probe-a.sh,probe-b.sh}`。**下一棒(唯一)** = **第 29 棒 · 执行棒** = automation **`23cbc916-ebda-4413-84da-ce229a4230f5`**(一次性 · `scheduledAt = 2026-09-25T15:12` · `cwds = E:/ProgramData/AIProject/aliyun-dsh-server`);接续点 = **第二批按表切换(`account_analysis` + `video_analysis`)**(见 §2 顶部)。⛔ 本线**同时只挂一个棒**。别线 `ACTIVE` 一律未动。 - 🔴 **2026-09-25 14:09–14:3x|第 27 棒 · 执行棒(S0+S1+S2+S3 完成 · S4–S11 未开工 · A4 达成源面 / A5 半达成 / A1·A2·A3·A8 未达成)** —— ① ✅ **S0 只读前置 9 条 + 待核实 4 条全部有读数**,其中**两条从"待核实"变定论**:㈠ **4.2-1 本地库列面** = `hot_accounts` **25 列**(`PRAGMA table_info` 现算;阶段一记「24 列」**是错的**,本棒静态读的 25 对)|㈡ **4.2-2 `lib/client.js` 那 2 处 = 正则 `.exec()`(拆段正则),不是 SQL** ⇒ **可排除**,改造面 222 处不受影响。另:**P1 红线 22 / P2 行口径 222(分文件逐一对上)/ P3 脱钩 22 处 / P4 json 列 7 个 / P7 平台无 drop·重建口(6 端点 + `DROP DATABASE` 仅 1 处防注入单测注释 + `ForbiddenKind` 5 类)** 全与单内预期一致;**4.2-3 平台容量上限到手** = 单行 **64 KiB**/单插件 **20 万行**/`find` 硬夹紧 **200**(`find` 不传 `limit` 默认 50)。 ② ✅ **S1 表名对齐完成**:`lib/` **22 处**(`data.js`3/`db.js`5/`imports.js`7/`index.js`5/`workshop.js`2)+ `dsh.data.yaml` 3 处 + 2 份 skill 文档 3 处 ⇒ **包内字面命中 = 0**(`grep -rE "account_video_analysis" "$PKG" --exclude-dir=node_modules | wc -l`);`node --check` 全包 `lib/**/*.js` **rc=0**。🔴 **补了交接单没写、但必需的一半**:代码改成 `video_analysis` 而**本地 SQLite 表仍叫旧名** ⇒ 每次本地读都 `no such table`(R11 净变差且不易察觉)⇒ 在 `db.js` 的 `initDb()` 加**幂等表名对齐**(旧表在 ∧ 新表不在 ⇒ `ALTER TABLE … RENAME TO`),并在**临时库上真跑验证**(2 行原样、`aweme_id` 逐字不变、二次运行不改动)。⚠️ 旧表名在包内**只以「拆写常量」出现 1 处**(`"account_video" + "_analysis"`,只作 `sqlite_master` 匹配字面、⛔ 不进任何读写 SQL)—— **有意为之且在代码注释与本棒交付件里点名**,目的是同时满足"本地存量库能改名"与"A4 判据字面零命中";评审要核对旧名请按拆写形态 grep。 ③ ✅ **S2 迁移前备份与清单完成(47 真机 · R8 自律)**:8 个用户**只有 1 个**有本地库(`cce6d1cd-…`,2,383,872 B,mtime `2026-09-20 23:05:05`,uid 114801)⇒ 逐表行数入档(10 表 **441 行**:`account_videos`362/`account_video_source`39/`rewrite_log`13/`video_analysis`12/`hot_accounts`5/`account_persona`4/`storyboard_log`3/`script_review`2/`account_analysis`1/`creative_log`0);旧库打包落 `/opt/dsh/backups/plugin-data/dsh-plugin-mcn-suite/cce6d1cd-b376-4304-80f0-0e1c58c9ffde-mcn-plugin-db-20260925-141436.tar.gz`(809,732 B,sha256 `6b09e368…`,**`tar -tzf` rc=0 可解出**),**源库 sha256 备份前后逐字一致 `b37f8aab…`**(`journal_mode=delete` ⇒ 无 `-wal`/`-shm`)⇒ **"备份在位"四条全过**,S5 前置已具备。另:**P6 回滚素材在位**(`0.3.9/0.3.14/0.3.15/0.4.0.tgz`)|**P8 共享层 0.4.0 / 实例仍载 `0.3.15`(待重启)**|**P9 台账未取**(S8 未开工 ⇒ 本轮无建库动作,未消耗凭据面)。 ④ ✅ **S3 `store.js` 补齐 11 张表通用读写面(336 → 715 行)**:`TABLES` 登记(主键三分类)+ `findRows/countRows/locate/insertRow/patchRow/removeRow/patchWhere/removeWhere/upsertByPk/writeRows` + **D9 两步改写器**(`find` 定位 → 逐行 `PATCH`/`DELETE`,**逐行具名收集、失败不中断**,返回 `matched`/`changed`/`failed[]` 三个真值 + `dryRun`)+ **体积/列上限守卫**(先判后拒、⛔ 不截断;内核列与本地自增列**剥除并具名** `dropped[]`)+ **拆表读写**(`splitHotAccount`/`mergeHotAccount`/`findHotAccounts`/`upsertHotAccount`,两写失败给 `partial` + 补偿动作)+ **生成键**(`genKey` 随机 / `stableKey` UUIDv5 **可复算** ⇒ 迁移可重跑)。**单测 24/24 通过 / 0 败**(旧 13 例全过 + 新增 11 例),rc=0。 ⑤ 🔴 **S4–S11 未开工,且有结构性理由(⛔ 不是"没来得及")**:㈠ **写路径没有档位** —— `store.js` 的写函数**直接打平台**,而读源档 `dataPlaneMode()` 是**全局单档** ⇒ 一旦开始逐处改写就出现「**该表的写已进平台、读仍是本地**」⇒ 用户看到的是自己写入之前的旧数据(**R11 净变差,且是"看起来正常"的坏态**);㈡ **D10 的"按表 shadow → 按表切 platform"在当前单档开关下不可表达**,且**同一张表常有多个写入方**(`hot_accounts` 被 `data.js`/`imports.js`/`index.js`/`workshop.js` 同时写)⇒ 🔴 **切换的最小原子单位 = 表,不是文件**。⇒ 本棒自决(可推翻):**在写路径拿到与读同源的档位之前,不开工 S4**。**下一棒第 1 件事 = 补写源档**(见 §2 顶部)。 ⑥ 🔴 **两条记数修正(后棒一律以现算为准)**:㈠ **红线现算 23**(= 阶段一 22 **+ 本棒新增 1 处**:`db.js:47` 的表名对齐 `ALTER TABLE`;`db.js` 未删 ⇒ **A3 未达成**)|㈡ 阶段一记的 `hot_accounts` 24 列**错**,实为 **25 列**。 ⑦ 🔴 **一条待拍板项新增(㈤)**:插件包源码 `D:/dshworkspace/plugin_package/dsh-plugin-mcn-suite/` 在 `D:/dshworkspace` 仓库里是**未跟踪目录**(`git status` = `??`)⇒ 本轮 10 件改动**无任何版本留痕**(只靠 sha256 + 交付件)⇒ 要不要纳入版本管理,见 §5「⏭️ 本线后续」。原四项(㈠ 47 实例重启窗口/㈡ 106 重启窗口/㈢ 删库重建授权/㈣ `json` 列两案)**一项未动**。 ⑧ **卫生(A7 逐条达成)**:⛔ 未改 `D:/github/dsh_shenxian/src/**`(只读 grep)|⛔ `im.ts` 零改动|⛔ 未手改用户 `home` 下 profile/`.pnpm`(用户库一律 `readOnly: true`)|⛔ **未重启任何服务、未投放任何版本**(共享层仍 0.4.0)|⛔ 未 commit·push|✅ 改动面 **恰 10 件**(`find "$PKG" -newermt "2026-09-25 14:10"`:`lib/host/mcn/{db,data,imports,index,workshop,store}.js`、`dsh.data.yaml`、2 份 skill 文档、`tests/mcn-store.test.mjs`)⇒ **`lib/client.js` 免跑移动端五条(A6)**|✅ 47 `/tmp` 残留 **0**|✅ 未建临时会话。 ⑨ 📄 **落地件** = `交付物/MCN数据面接入-阶段二-20260925.md`(判据表 A1–A8 + S0 九条读数 + 拆写常量说明 + 未达成理由 + 卫生 + 下一棒建议)|探针 = `tmp/p27/{s0-local.sh,s0-47.sh,s0-47b.sh,s2-backup-47.sh,scan-stmt.mjs,s1-local-align-check.mjs}` + `tmp/p27/{p1-hits.txt,stmt-scan.tsv}`。**下一棒(唯一)** = **第 28 棒 · 执行棒** = automation **`74aec2df-a750-4fc5-9d0b-5955eeb96b46`**(一次性 · `scheduledAt = 2026-09-25T14:40` · `cwds = E:/ProgramData/AIProject/aliyun-dsh-server`);接续点 = **写源档(`writeMode`,默认 `local`)+ 按「表」推进第一批全量切换(`account_persona` + `creative_log` 做端到端样板)**(见 §2 顶部)。⛔ 本线**同时只挂一个棒**。别线 `ACTIVE` 一律未动。 - ✅ **2026-09-25 13:53–14:0x|第 26 棒 · 规划棒(只出交接单 · 零落地)** —— ① ✅ **交接单已出**:`D:/github/dsh_shenxian/dsh-server-docs/05-交接单/插件投放与分库线-03MCN数据面接入改造-阶段二.md`(8 段齐全 + 附录 A 逐表改造口径/B 存量迁移口径/C `json` 列两案/D 删 `db.js` 收口判据;35,914 B / 281 行 · **行尾纯 LF(CR=0)** · `docs-audit.py` **rc=0**)。**单子范围 = 阶段二(全量迁移与收口)**:其余 9 张表(含拆表后的 `hot_account_ext`)读 / 写路径切平台取数口 + 存量迁移(6 张生成键表稳定键回填 · 先备份先出清单 · 幂等可重跑)+ `json` 列处置 + 🔴 **删 `lib/host/mcn/db.js` 使红线 22 → 0**。 ② 🔴 **本棒新增实测五条(后棒一律以现算为准,⛔ 别抄旧记数)**:㈠ **改造面双口径** —— 行口径 **222**(`.prepare(`/`.exec(` 行数,阶段一 P3)/**字面量口径 210**(本棒现算:`index.js`82/`imports.js`51/`db.js`37/`workshop.js`26/`data.js`13/`video-tools.js`1)⇒ 两口径**含义不同、⛔ 不许互相"纠正"**;㈡ 🔴 **代码 ↔ 声明表名脱钩**:`account_video_analysis` 代码仍 **22 处**(`data.js`3/`db.js`5/`imports.js`7/`index.js`5/`workshop.js`2)而声明已改 `video_analysis`(代码**零命中**)⇒ 不修则读写会打**不存在的表**;`hot_account_ext` **代码零引用** ⇒ 须**新建**读写路径;㈢ **`UPDATE`/`DELETE` 共 45 处**(`UPDATE` 26/`DELETE` 19),条件首列 = 自增 `id` **24** / 天然键 `aweme_id` **4** / 其余(业务列 / 复合 / 动态拼接)**17** ⇒ **≥41 处须「先 find 定位 → 再 PATCH/DELETE」两步改写**(平台 `PATCH`/`DELETE` **只按主键** ⇒ 本地自增 `id` 在平台侧不存在);㈣ `json` 声明列现算 **7 个**(阶段一记「6 个」⇒ **以现算为准**),其中 **3 列共 17 行**存量非法 JSON;㈤ 🔴 **平台无插件库 drop / 重建口** —— 数据面仅 **6 个端点**(`datastore/plan`·`datastore`·`datastore/migrate`·`datastores/reconcile`·`datastore/audit`·`datastores`)、`DROP DATABASE` 在 `src/` **零命中**(唯一命中 = 防注入单测注释)、`ForbiddenKind` = **5 类禁令**(`drop_column`/`alter_type`/`rename`/`notnull_no_default`/`drop_table`)。 ③ 🔴 **一条不可逆候选已如实上抛**:附录 C 两案 = **A 值清洗**(不删库 · 读侧兼容两形态)vs **B 改声明为 `text`**(语义干净但**必须删库重建**)⇒ B 案只能人工 `DROP DATABASE` ⇒ **须用户拍板**(规划侧判据:**迁移前做代价最低、迁移后做即丢真实数据**)。⛔ **未拍板前执行棒走 A 案**(已写进第 27 棒 prompt 的硬约束)。 ④ ✅ **待核实 4 条 + 待拍板 4 项**(均写进单内,⛔ 不在接续棒内自决):待核实 = 本地库精确列面(阶段一记 `hot_accounts` **24 列**/本棒静态读 **25 列** ⇒ **差异未定论**,一律以 `PRAGMA table_info` 现算为准)· `lib/client.js` 2 处调用形态 · 平台容量上限 · 实例凭据与窗口;待拍板 = **㈠ 47 实例重启窗口**(影响 1 个在线用户 `cce6d1cd-…`、约 1–3 分钟)· **㈡ 106 重启窗口**(与既有待重启面合批)· **㈢ 删库重建是否授权**(不可逆)· **㈣ `json` 列两案选型**。 ⑤ ✅ **卫生(本棒)**:⛔ 未 ssh / 未 scp / 未改码 / 未投放 / 未重启任何服务 / 未 commit·push;⛔ 未改 `D:/github/dsh_shenxian/src/**`(**只读** grep)|⛔ 未改 `D:/dshworkspace` 下任何文件(**只读**)|⛔ `im.ts` 零改动。 ⑥ 📄 **落地件** = `交付物/MCN数据面接入-阶段二-改造面清单-20260925.tsv`(**210 处**逐处 `file`/`line`/`ops`/`tables`/`where_cols`,供执行棒逐处接线)|探针脚本 = `tmp/p26/{scan_tables,scan_where,scan_precise}.py` + `tmp/p26/{table-map,where-map,stmt-precise}.tsv`。**下一棒(唯一)** = **第 27 棒 · 执行棒** = automation **`28780b2e-d1e7-4f35-aa58-0d80c68df537`**(一次性 · `scheduledAt = 2026-09-25T14:09` · `cwds = E:/ProgramData/AIProject/aliyun-dsh-server`);接续点 = **按交接单 `插件投放与分库线-03MCN数据面接入改造-阶段二.md` 执行阶段二**(见 §2 顶部)。🔧 同批已把本棒 automation `2714f440-…` 置 `PAUSED` ⇒ 本线**同时只挂一个**。⛔ 别线的 `ACTIVE` 一律未动。 - ✅ **2026-09-25 13:10–14:1x|第 25 棒 · 执行棒(阶段一纵向切片落地 —— A1–A4/A6 达成 · A5 按单内预期未达成 · 3 项未闭合)** —— ① ✅ **端到端打通(最容易翻车的一段)**:声明 11 表(`dsh.data.yaml` **7,391 B**/11 表 96 列 14 索引/纯 LF/sha256 `9425c953…`)→ 上传候选池 **0.4.0**(`HTTP 200`/`blocked:0`/`warnings:120`)→ `plan`(**25 项**)→ `datastore`(`outcome:"created"`,**159 ms**)→ **`migrate`(`state:"ready"`/`schemaVersion:1`/25 条 executed)** → 共享层 `action:"replaced"`(`fileCount:659`/`rollbackTgz:…/0.3.15.tgz`)。台账 `state=ready`/`plan_hash 58226942…`/`last_error:null`;`pg_database` 命中 **1 行**;对账 `clean:true`(`orphans:[]` `missing:[]`)。包 **1,962,024 B / 324 件**,sha256 **`96f67039dc8a16cd5f460f514d032560898fbedd19786e34a5b81a939e97b11c`**。 ② ✅ **A3 真机读**:`account_videos` 平台读回 **36 行 = 本地 SQLite 36 行**(前三 `aweme_id` `7628915676010423397/7629803455813243043/7631176055600527729` 逐字一致)+ **游标第二页严格递增无重复**;`account_persona` 洗净种子后 **2/2 读回**(列集合 8 列全对)。**A2** `available:true`、11 张表全在列。**A4** 五拒全 `bad-args`(写他人 `user_id`/缺主键列/带 `created_at`/未知表名/未知列)+ **0 行返回 `{ok:true,value:[]}`** + 缺身份头/非法包名 **400**;**归属列由内核强制注入**(读回行自带 `user_id`,请求体从未带)。 ③ 🔴 **三处实测修正(后棒一律以现算为准,⛔ 别抄旧记数)**:㈠ **物理表名 ≤ 20 字符**(`pluginId` 归一化后 **20 字符** + `p_` 前缀 + 下划线;**`validateDecl` 不查这条**,直到上传才 `HTTP 500 非法表名`)⇒ `account_video_analysis` 改名 **`video_analysis`**;㈡ 🔴 **`bigtext` 类型往返不守恒**(声明 `bigtext` ⇒ PG 落 `TEXT` ⇒ 回读归 `text` ⇒ 预演恒报 `alter_type` 禁令 ⇒ 状态**永久 `blocked`** ⇒ **该插件所有用户永远启不了**,**事故级**)⇒ 三处改 `text`(保留 `maxBytes:49152`,PG 类型同为 `TEXT`、上限意图不变);㈢ `plan`/`datastore`/`migrate` 的 body 参数名是 **`id`**(⛔ 非 `pluginId`,单内 §五 S7 样例有误)且 **`datastore` 只落 `created`、`migrate` 才是 `ready`**(易误判「建库即就绪」)。 ④ 🔴 **一条阶段二硬阻塞(本棒只报告 · ⛔ 未改 `src/**`)**:6 个 `json` 声明列中 **4 个有非法 JSON 存量共 17 行**(纯 Markdown 正文)—— `account_video_analysis.content_json` **12/12**、`account_persona.content_json` **4/4**、`account_analysis.content_json` **1/1** 非法(`account_video_source` 39 行、`script_review` 两张 json 各 2 行**全合法**)⇒ 实测 `{"ok":false,"reason":"disabled","detail":"22P02: invalid input syntax for type json"}`。⚠️ 因 `alter_type` 是平台禁令,改类型须**删库重建**(不可逆)⇒ **留待阶段二单内定口径**。⚠️ 另记:非法值返回的 `reason:"disabled"` **语义误导**(应为 `bad-args`/`invalid-value` 类)。 ⑤ ⏳ **未闭合 3 项**:㈠ **插件路由端到端未取读数** —— 实例仍载 `0.3.15`(实例启动 `11:50:39` **早于**共享层替换 `13:1x`)⇒ `/mcn/api/account/{videos,persona}` 跑旧码,不能用于证明新版适配层 ⇒ **待实例重启**;㈡ **106 共享层未同步**(节点侧首轮 `stale→pulled` 属预期)⇒ 待重启;㈢ **A5 红线仍 22**(阶段一预期,阶段二判据)。⛔ 本棒**未重启 106、未重启实例**(中断在线用户 ⇒ 红线 ⇒ 等窗口)。 ⑥ **卫生(A7 六条全清)**:⛔ 未改 `src/**`(实测 `src/` 20 个改动文件**最晚 mtime `2026-09-25 12:20:57`,早于本棒 13:10 开工**;`src/db/plugin-data/*`+`src/web/routes/plugin-data.ts` 是上游棒的 `??` 未跟踪件)|⛔ `im.ts` 零改动|⛔ 未手改用户 `home` profile/`.pnpm`(对用户库**只读** `mode=ro`)|⛔ 未重启 106|⛔ 未 commit·push|✅ **未迁存量**(平台侧种子为同值取证行,读回后**全删**:`VIDEO_DELETED=36`/`PERSONA_DELETED=2`/清理后 `n=0`)|✅ 临时 admin 会话 `10 → 删除 10 → 残留 0`|✅ 47 `/tmp/p25*` 残留 **0**。 ⑦ 📄 **落地件** = `交付物/MCN数据面接入-阶段一-20260925.md`(判据表 A1–A7 含命令/输出/退出码 + 三条平台侧缺陷 + json 列地雷面 + 踩坑记录)|探针 = `tmp/p25/s9b–s9e.sh`。**下一棒** = **第 26 棒 · 规划棒**(出阶段二交接单,见 §2)。 - ✅ **2026-09-25 12:5x–13:0x|第 24 棒 · 规划棒(只出交接单 · 零落地)** —— ① ✅ **交接单已出**:`D:/github/dsh_shenxian/dsh-server-docs/05-交接单/插件投放与分库线-02MCN数据面接入改造.md`(8 段齐全 + `〇` 对齐节 + 附录 A 阶段划分;占号 = `mkdir 05-交接单/.lock-02` 原子;自检 `docs-audit.py` **rc=0**、行尾纯 LF、21,787 B / 220 行)。**单子范围 = 阶段一(纵向切片)**:声明 11 张表(`hot_accounts` 拆 2)+ 建库 + 适配层 `lib/host/mcn/store.js` + **`account_videos` / `account_persona` 两张表的读路径**切平台取数口 + 真机读数;⛔ 阶段一**不删 `db.js`**(红线不回零)、**不迁存量数据**、⛔ 不改 UI。单内已给 §二 只读前置 6 条(命令 + 期望输出)、§四 决策点 6 已定 + 3 待核实、§五 步骤 S1–S10(每步自带验证)、§六 验收 A1–A7(含 A5 如实标"阶段一未达成")、§七 回滚(含"回滚素材不在位不许投放")、§八 回报格式。 ② 🔴 **三处实测修正(后棒一律以现算为准,⛔ 别抄旧记数)**:㈠ 入口旧记「MCN 包红线扫描 **21 处** SQLite 建表」⇒ **实测 22 命中**(**10 张表**的 `CREATE TABLE` + 第 49 行注释 1 + **11 条** `ALTER TABLE`);㈡ **真实改造面 = SQL 调用点 222 处**(`index.js` 94 / `imports.js` 51 / `db.js` 37 / `workshop.js` 26 / `data.js` 11 / `client.js` 2 / `video-tools.js` 1)—— **"21 处"是关键词命中数,不是改造面**;㈢ `hot_accounts` 实有 **24 列 > 平台 `MAX_COLUMNS=20`**(`src/db/plugin-data/schema.ts:75-76`,`MAX_TABLES=20` 同处)⇒ **该表按现状无法声明,必须拆表**(单内 D2-c);其余 9 表列数 3–17 合规。附带:包 `package.json` 现**无 `data` 键** ⇒ 状态 `none`、门禁放行;本地库落点 = `/.dsh/mcn-plugin.db`;声明可**本机离线**校验(`lib/db/plugin-data/schema.js` 导出 `validateDecl` / `parseDeclFromDir` / `pluginDbNameOf`)。 ③ ✅ **"本线唯一剩下的可自动接续、无需拍板项"判据复核通过** —— 待拍板项 **0 条**:拆表口径 / `scope` 全 `user` / 主键三分类 / `JOIN`·`OFFSET`·聚合的改写口径 **全属技术项 ⇒ 规划棒自决**;**存量数据处置**判为「先备份 + 先出受影响清单」的 **R8 自律项**(不删旧库 ⇒ 可回滚)⇒ 非拍板项。⚠️ 单内明写「⛔ 无回滚素材不许投放」=本棒前置门禁。 ④ **卫生**:⛔ 未 ssh / 未 scp / 未改码 / 未投放 / 未 commit / push;⛔ 未改 `D:/dshworkspace` 下任何文件(**只读**);⛔ 未改 `src/**`;⛔ 未重启 106;⛔ 未清 `.dsh-module-fallback`。登记 = `05-交接单/README.md §一` 新增本单一行。 ⑤ 🟡 **附带发现(⛔ 未动 · 只报告 · 属机制层)**:`07-scripts/handoff-guard.sh` 的 `_ANCHOR_SEGS`(shell:`… scripts skills 交接单`)与 `07-scripts/lock-guard-hook.py` 的 `_DOMAIN_SEGS`(python:`… 07-scripts 08-skills 05-交接单`)**内容不一致**;本棒 4 个目标路径经两侧算法**复算结果一致**(故本棒锁有效),⛔ 未修跨库脚本(不属本工作区)⇒ 留作单独排棒议题。 **下一棒(唯一)** = **第 25 棒 · 执行棒** = automation `129abaa5-937c-448b-811e-d804f35716be`(一次性 · `scheduledAt = 2026-09-25T13:09` · `cwds = E:/ProgramData/AIProject/aliyun-dsh-server`);接续点 = **按交接单 `插件投放与分库线-02MCN数据面接入改造.md` 执行阶段一(声明 + 建库 + 适配层 + 2 张表读路径 + 真机读数)**。🔧 同批已把本棒 automation `95874886-…` 置 `PAUSED` ⇒ 本线**同时只挂一个**。⛔ 别线的 `ACTIVE` 一律未动。 - ✅ **2026-09-25 12:1x–12:4x|第 23 棒 · 执行棒(三件全做完:判据 ①②③ 全绿 + 两项取证到手)** —— ① ✅ **§5-12 跨机错误消息截断放宽(200 → 1200)**:`src/supervisor/remote-spawner.ts` 新增导出常量 `AGENT_ERROR_TEXT_MAX = 1200`,两处分支(**4xx 具名抛** / **5xx `lastErr`**)改用之;单测 **+3 例**(T6 = 973 字符正文**逐字完整**进 `message`;T7 = 边界「**恰好 1200 一个不少**/多 1 字符即截到 1200」;T8 = 5xx 同上限且 **`calls===4`** 钉住"5xx 仍重试 4 次"这条既有语义**未被本次改动碰到**)。门禁:`npm run build` **rc=0**|`npm test` = **592 tests / 590 过 / 0 败 / 2 跳过**(第 21 棒 589/587 ⇒ **+3**)|`check:layering` **rc=0 ✅ 无新增违规**(未归类 1 条 `src/platform-paths.ts` 属既有基线)。**部署两机一件**:47 `/opt/dshs/lib/supervisor/remote-spawner.js` + 106 `/opt/dshs-cluster/lib/…`,**三方 md5 逐字一致 `e9ab05d9…`**(投前两机同为 `269d3d20…`/20564 B ⇒ 双机同版);备份 `/opt/dsh/backups/lib-p23/{iZrj99af19cibck1ge93tqZ,VM-0-8-opencloudos}-pre-p23-20260925-12{28}5{4,5}.tgz` + `.manifest` + 单文件副本;**47 `restart dshs` ⇒ active · NRestarts=0 · 门户 200 · journald `dshs listening on http://127.0.0.1:3080`**(停止阶段有一次旧进程 `Failed with result 'timeout'` = 优雅退出超时,新进程随即 Started,判为生效);⛔ **106 只投不重启**(待窗口)⇒ **106 待重启面 → 48 件**。`dshs-worker`(47) 未重启:该文件在 worker 模块图内,但 `RemoteSpawner` **唯一实例化点 = `lib/web/server.js:38`**(Manager 侧)⇒ 对 worker 零影响。⚠️ **无"长错误消息"真机读数**(需一次真失败的跨机调用)⇒ ⛔ **未宣称运行面验收通过**。 ② ✅ **§5-18「清理前必查」取证 → 本条从"待查"变"可拍板"**:三条链(`node_modules/{xlsx,react,react-dom}` → `.dsh-module-fallback` → 共享层)**确实在解析路径上** —— 实测 `require.resolve`(以实例 uid 114801 只解析不执行):`@dsh-local/business-plugins` / `@dsh-local/portal-entry` / `@deepseek-ai/dsh-client-ui-renderer` **三处落点全部经顶层兜底**(因它们的 `.pnpm//node_modules` **与**提升层 `.pnpm/node_modules` **都没有** react)⇒ **有真依赖**;但三处命中**全在浏览器半边**(`lib/client.js`,头部原文写明「以普通脚本形式执行,向 `window.__ModuleLoader__` 注册 factory」)⇒ `require("react")` 由**浏览器侧模块表**满足、**不读磁盘**;加反向证据 `grep -rln "__ModuleLoader__" /opt/dshs/lib` = **0 文件** ⇒ 服务端不解析 client bundle ⇒ **Node 运行期无消费者**,**结论句 = 有真依赖(3 处,均浏览器半边)/Node 运行期无消费者**。① 命中清单(4 类,含行号):`business-plugins/lib/client.js:56`、`portal-entry/lib/client.js:67`、`dsh-client-ui-renderer/lib/client.js:10` + 包自身内部(react/react-dom 的 `cjs/*`、`scheduler@0.23.2`、`use-sync-external-store@1.2.0`、`dsh-client-ui-primitives/lib/index.js:8-9`)——后者的消费者**自带** react、**不经兜底**。② 区分:**包自己声明的依赖** = 共享层 `dsh-plugin-mcn-suite/package.json:32-34`(`react`/`react-dom`/`xlsx`,自带)|**顶层兜底** = 上面那 3 处。③ **两处纠正 §5-18**:兜底目录内**只剩 3 条链**(另有 4 个**空目录** `@deepseek-ai`/`@shikijs`/`@types`/`@ungap` = 残留);三条链 mtime 实为 **09-25 10:14:57**(§5-18 记的 09-08 是**目录** mtime)。**4 条边界**(只核这一个 profile/未展开官方客户端宿主是否在服务端预解析/只对当前 profile 版本成立/未做删链 A/B)⇒ 交付件 §2-3。⛔ **未删任何软链、未改 profile、未动 `.pnpm`**。 ③ ✅ **§5-22 本机 `lib` ↔ 部署面 3+7 差异定性(结论:本线无活缺口)**:**7 件** = `im/gateway-token.{js,d.ts,js.map}`(**两机全缺**,且部署面 `grep gateway-token` = **0 命中** ⇒ **死件/无人引用**)+ `web/essential-plugins.{d.ts,js.map}` + `web/routes/plugin-data.{d.ts,js.map}`(四件均为**声明/映射边车**;其 `.js` 两机**都在**且引用者已解析 —— `routes/admin.js:10`/`routes/business-plugins.js:20`/`web/server.js:55` ⇒ **runtime 零影响**)。**3 件运行时 `.js` 两机逐字相同**(`12077`/`3342`/`36738`,mtime 均 `09-23 20:59:31` = **源构建时间**)vs 本机 `16241`/`4154`/`47262` ⇒ **本机更新**;`git log` **不可作判据**(IM 面整体落在同一 commit `e6207aa`)⇒ 判据 = 构建时间 + 体积。🔴 **新出耦合(后棒必看)**:本机 `lib/web/routes/im.js:42` 已 `import … from '../../im/gateway-token.js'` ⇒ **新版 `im.js` 与 `gateway-token.*` 必须同批投**(⛔ 单投 `im.js` ⇒ 实例启动期 `ERR_MODULE_NOT_FOUND`);两机现为「旧 `im.js` + 无 `gateway-token`」的**自洽态**,无人处于半投状态。⇒ **归类**:3 件运行时 `.js` 新版本 + `gateway-token.*` **属 IM 线投放面(⛔ 不归本线)**;4 件边车**可补但无收益**(两机无 TS 消费者、`.js.map` 只影响调试栈)⇒ **建议不投**。⛔ 未投任何件。 ④ **卫生**:⛔ 未 commit/push;⛔ `src/web/routes/im.ts` 零改动;⛔ 未手改用户 `home` 下 profile/`.pnpm`;⛔ **未重启 106**;⛔ 未清 `.dsh-module-fallback`(只取证);47 侧除目标文件外零改动;✅ 47 `/tmp` `p23-*` 残留 **0**(探针已 `trap` 清理并复核)。落地件 ⇒ `交付物/跨机错误消息放宽与两项只读取证-20260925.md`;探针 ⇒ `tmp/p23-item2-probe{,2,3,4,5}.sh` / `p23-item3-probe.sh` / `p23-deploy-backup.sh`。 **下一棒(唯一)** = **第 24 棒 · 规划棒** = automation `95874886-f28e-44d8-83a0-606cc27f8ef1`(一次性 · `scheduledAt = 2026-09-25T12:53` · `cwds = E:/ProgramData/AIProject/aliyun-dsh-server`);接续点 = **§5-9 MCN 数据面接入改造(出交接单)** —— 本线**唯一剩下的"可自动接续、无需拍板"项**(其余全卡在待拍板 / 待窗口 / 待自然触发,见 §5 后续)。🔧 同批已把本棒 automation `cea5b3d4-…` 置 `PAUSED` ⇒ 本线**同时只挂一个**。⛔ 别线的 `ACTIVE` 一律未动。 - ✅ **2026-09-25 11:58–12:1x|第 22 棒 · 执行棒(判据 ①②③ 全绿 —— 两机 `lib/` 代码面差异面归零)** —— ① ✅ **判据①(完整差异清单)**:`lib/**` **逐文件 md5** 全量对比 —— 47(`/opt/dshs`)= **389** 件 / 106(`/opt/dshs-cluster`)= **357** 件;逐字一致 **322**、内容不同 **23**、仅 47 有 **44**、仅 106 有 **12** ⇒ 原始差异面 **79 条**。🔑 **判据口径(本棒立 · 后棒沿用)**:两侧各自留有历史部署快照(`.s3bak-*` / `.s0bak-*` / `*.bak-*`)⇒ **只在代码面上要求归零** ⇒ **代码面差异 = 46 件**(21 件运行时 `.js` + 25 件 `.d.ts`/`.js.map`)+ 备份残留 33 条(47 侧 21 / 106 侧 12,**两侧时间戳本就不同** ⇒ ⛔ 不投不删)。 ② ✅ **判据②(补齐 → 归零)**:106 全量备份 `/opt/dsh/backups/lib-p22/106-pre-p22-20260925-120615.tgz`(sha256 `9612e4c0…`)+ 同目录**改前指纹清单**;47 侧按清单打包 **46 件**(`1ac3922cb3a3239a3f9d4cf59f7beb4f`)⇒ 106 收到后 md5 **一致** ⇒ `tar xzf` rc=0 ⇒ `lib` 件数 **357 → 380**(+23 缺件)、新建目录 `db/plugin-data`、`im/backends` 属主 **root:root 755** ⇒ 重采复核 **代码面 368 = 368、内容不同 0、单向缺失 0**(`p22-verify.py` **rc=0**)。**基准 = 47 生产部署面**(`dshs.service` 起于 `11:47:33` 的那份;抽样三件与本机构建产物逐字一致)⇒ §5-21 **闭合**。 ③ ✅ **判据③(待重启清单)**:106 **只有 `dshs-worker.service`**(`ExecMainStartTimestamp = 09-25 09:52:11`)+ relay、**⛔ 无 `dshs.service`**;入口 = `ExecStart = … /opt/dshs-cluster/lib/cli.js worker --port 19000 …`,而 `cli.js` **顶层静态 import** `./db/index.js`(→ `pg.js`/`sqlite.js`)与 `./web/server.js`(静态 import **14 条 routes**)⇒ 46 件**全在模块图内**、启动时已求值并缓存 ⇒ **改盘对运行中进程的行为影响 = 0**;**生效 = 下次重启** ⇒ 待重启 = **46 件全部**(运行时 `.js` 21 件)。⚠️ 与 §5-19 **同源不同事**(那条指 p20 的 `plugin-assembly.js`)。 ④ 🔴 **新发现(⛔ 未动 · 已排 §5-22)**:**本机构建产物 ≠ 生产部署面** —— `D:/github/dsh_shenxian/lib`(**09-25 11:44 新构建**)与两机部署面(IM 版 `09-23 20:59`)在 **3 件运行时 `.js`**(`im/backends/gateway.js` 16241↔12077、`im/connection-backend.js` 4154↔3342、`web/routes/im.js` 47262↔36738)+ **7 件本机独有**(`im/gateway-token.{js,d.ts,js.map}`、`web/essential-plugins.{d.ts,js.map}`、`web/routes/plugin-data.{d.ts,js.map}`)上不同,**且两机缺的是同一批**(不是 106 独有)⇒ 后棒 ⛔ **别拿本机 `lib` 当基准**;本棒⛔ 未据此改任何一侧(属 IM 线投放面)。另:106 的 `business-plugins.js` 落后 = **缺第 17/19/21 棒三轮改动**(`70d5c0f2…` → `3a752e01…`)。 ⑤ **卫生**:⛔ 未 commit/push;⛔ `src/web/routes/im.ts` 零改动;⛔ 未手改用户 `home` 下 profile / `.pnpm`;⛔ **未重启 106**(待窗口);**47 侧零改动**(只读)。落地件 ⇒ `交付物/两机lib差异面核对与106补齐-20260925.md`;清单与脚本 ⇒ `tmp/p22-lib47.tsv` / `p22-lib106-pre.tsv` / `p22-diff-pre.tsv`(79 条全量) / `p22-fill-list.txt`(46 件) / `p22-diff.py` / `p22-verify.py` / `p22-baseline.py`。 **下一棒(唯一)** = **第 23 棒 · 执行棒** = automation `cea5b3d4-fb27-49ae-a94d-db21c5fb2e60`(一次性 · `scheduledAt = 2026-09-25T12:18` · `cwds = E:/ProgramData/AIProject/aliyun-dsh-server`);接续点 = **§5-12 跨机错误消息截断放宽(200→1200)+ §5-18「清理前必查」只读取证 + §5-22 本机 lib↔部署面 3+7 差异定性**(见 §2 顶部)。🔧 同批已把本棒 automation `fd745e9d-…` 置 `PAUSED` ⇒ 本线**同时只挂一个**。⛔ 别线的 `ACTIVE` 一律未动。 - ✅ **2026-09-25 11:35–11:5x|第 21 棒 · 执行棒(判据 ①②③④ 全绿 —— 共享层台账两条语义缺陷已修)** —— ① ✅ **一条纪律 = 修法本身**:**要写进台账的值,必须从盘上那份实体现读**(⛔ 不信上游传下来的副本)—— 两条缺陷同源:㈠ `version` 取自候选池行、㈡ "回滚素材"落的是**新包自拷贝**。落地 = 三函数(`packageVersionAt` / `rollbackAssetPath` / `packSharedTree`)+ `doShare` 两处接线(`version` 改取 `incoming` 解出来的真值;**替换前**把**旧目录**打成 `<上一版真值版本>.tgz`,清单新增 `rollbackTgz` 字段 ⇒ 与 `backupTgz`「当前版本快照」**分成两个事实**)+ **新口 `POST /api/plugins/shared/reconcile`**(㈠ 的存量只能靠"拿盘上真值回写清单"收口,且必须是**显式动作** —— `/mine` 是用户面高频口,⛔ 不能把全量指纹现算摊给每个用户)。 ② ✅ **判据①(清单 ≡ 磁盘真值)**:47 上**制造真实脱钩**(`_dsh-local_storyforge` version 0.3.0→0.2.0)⇒ 干跑 `checked=4 fixed=1`(后值 = 盘上 0.3.0)⇒ 走**平台口** `RECONCILE_HTTP=200` 修回 ⇒ `GET /api/plugins/shared` 四个包全对(storyforge 0.3.0 / mcn-suite 0.3.15 / im-tabs 0.1.0 / softspark 2.0.0)。与备份逐字 `diff` ⇒ **唯一差异 = `updatedAt`**(**预期**:自愈也是一次写入,语义就是"该条目最后一次写入")。幂等复核:`fixed=0` 且清单 md5 前后**不变**(`73435d1f…`)。⚠️ **字面动作"跑一次 share"未做**(幂等分支一个字节都不动;替换分支要真发新版 ⇒ 会改线上共享层内容)⇒ 等价替代已足,如实声明。 ③ ✅ **判据②(节点连续两轮 current)**:在 106 上以节点身份触发两轮 ⇒ 双双 `{"missing":[],"stale":[],"extra":[],"pulled":[],"failed":[]}`(`asOf` 间隔 3.4 s)⇒ ⛔ 不再 stale/反复重拉;Manager 侧 `NODE_REPORT w-106 authoritative=4` 同步复核。 ④ ✅ **判据③(回滚素材名实)**:盘上既有素材 `mcn-suite/0.3.14.tgz` 的内容版本 **= 0.3.14**(名实相符);`packSharedTree` 在**真共享层包**上跑(产物只落 `/tmp`)⇒ 产物内容版本 `0.3.15`(= `SRC_TRUTH_VERSION`)、`ARCHIVE_NODE_MODULES_HITS=0`(⛔ 不夹带依赖树)、体积与池内 tgz 同量级。❗**完整替换路径未在真机跑通**(47 各包在备份面**已有同名素材** ⇒ 走"已存在则跳过" ⇒ 打包分支未被触发)⇒ 已排 §5-20。 ⑤ ✅ **判据④(单测)**:`test/shared-layer-sync.test.mjs` **+5 例**(真值口径 / 自愈三件 + 双向只报告 / 素材名实 + 不含 `node_modules` / 反证不许落进被归档目录)。门禁:`npm run build` rc=0|`npm test` = **589 tests / 587 过 / 0 败 / 2 跳过**(第 20 棒 584)|`check:layering` ✅ 无新增违规。 ⑥ ✅ **部署与卫生**:47 **一件**(`lib/web/routes/business-plugins.js`,md5 `3a752e01e4f16b4e6d6c47c305362a51` 与本地**逐字一致**)+ `restart dshs`(active · `NRestarts=0` · 门户 **200**);新口无凭据 ⇒ **401**(⇒ 已注册且门禁生效;404 才是没注册)。备份 `/opt/dsh/backups/lib-p21/`(`47-pre-p21-20260925-114554.tgz` + `manifest-pre-p21.json`)。47 `/tmp` 残留 **0**、临时 admin 会话**删 1 条(残留 0)**。⛔ 未 commit/push、⛔ `im.ts` 零改动、⛔ 未手改用户 `home` 下的 profile / `.pnpm`。 ⑦ 🔴 **新发现(⛔ 未动 · 已排 §5-21)**:**106 的 `lib/web/routes/business-plugins.js` 落后好几个版本**(106 `70d5c0f2…`/114041 B/mtime 09-25 00:28,47 投前 `4109ba34…`/124222 B)⇒ 本棒**只投 47**(改的是 Manager 侧台账逻辑,106 是 worker;⛔ 不擅自改别处部署面)。 **下一棒(唯一)** = **第 22 棒 · 执行棒** = automation `fd745e9d-d80b-4aca-aa09-5672b38ad228`(一次性 · `scheduledAt = 2026-09-25T11:58` · `cwds = E:/ProgramData/AIProject/aliyun-dsh-server`);接续点 = **47/106 两机 `lib/` 差异面核对与补齐(含 §5-19 待重启清单)**(见 §2 顶部)。🔧 同批已把本棒 automation `6e2e7e57-…` 置 `PAUSED` ⇒ 本线**同时只挂一个**。⛔ 别线的 `ACTIVE` 一律未动。 - ✅ **2026-09-25 11:0x–11:28|第 20 棒 · 执行棒(判据 ①②③ 全绿 —— pnpm `exit 255` 真根因已定且已修)** —— ① ✅ **真机真实报错原文拿到(判据①)**:`EPERM: operation not permitted, chmod '…/profiles/web/node_modules/xlsx/bin/xlsx.njs'`(pnpm `linkBin`)⇒ 🔴 **第 19 棒的 store 口径假设被真机证伪**:该 profile 的 `.modules.yaml` 记 `storeDir=/ws/.local/share/pnpm/store/v3`,与平台实际传的 `HOME=/ws` **逐字一致**(`plugin-assembly.ts:499` 那个 `homeDir` 形参,调用方传的是 `wsDirOf()`)⇒ ⛔ **别再按 store 方向改**(`ensure-biz-plugins.cjs` 那套口径属另一条链)。 ② ✅ **根因** = `node_modules/{xlsx,react,react-dom}` 是**未声明的 extraneous 软链**(→ `.dsh-module-fallback/node_modules/*` → 共享层实体,属主 **root**、硬链 `links=3`)⇒ 用户侧 pnpm 在 `linkBin` 里对 bin **无脑 `chmod 0755`**(不看当前是否已 755)⇒ 非属主 **EPERM**(**是 EPERM 不是 EACCES** ⇒ 与权限位无关,改 mode 无用)。三条旁路已真机/文档排除:chown 给用户(破 D2)/复制实体(破 D2 + 破模块同一性)/让 pnpm 跳过 bin(**pnpm 9.15.9 无此开关**,真机 `pnpm install --help` 实测无 bin 项;且共享层 4 个包**自身皆无 `bin` 字段**)。 ③ ✅ **修法**(`src/supervisor/plugin-assembly.ts` **一件文件**):`quarantineSharedLayerLinks`(只摘「顶层(含 `@scope/name`)∧ **未声明** ∧ **解到底落共享层**」的软链 ⇒ **改名**为 `.dsh-quarantine-*`;⛔ 不删、⛔ 不碰真目录/`.` 旁挂物/悬空链)+ `restoreQuarantinedLinks`(原名被占 ⇒ **具名返回**、⛔ 不覆盖、⛔ 不丢隐藏项)+ `withQuarantinedLinks`(包住 remove / install 两次 `runPnpm`,`finally` **无条件放回**)+ `pnpmDiagArgs` / `pnpmDiagRerun`(失败自动补一次 `--reporter default` 重跑,原因进 `message` + `pnpmDiag` 字段 + journald;⛔ 只重跑一次、⛔ 不递归)。 ④ ✅ **真机判据②**(47 · admin 存量 profile,`HOME=ws` 时代建的):平台装配路径 **`APPLY_OK`**(修前同参数 **255**)|`DEPS 4 MISSING []`(`dependencies ↔ node_modules` **双向一致**)|`QUARANTINE_LEFTOVER 0`|三条越权软链**原样在盘**;**对照组**(同 profile、**不经**修法手动复刻)**仍 255 且同一条 EPERM** ⇒ **修法是关键变量、非偶发**;20 min 内 `plugin-assembly` 日志 **0 条**;实例 `[rehydrate] probe OK … :20000`。 ⑤ ✅ **门禁**:`npm run build` rc=0|`npm test` = **584 / 582 过 / 0 败 / 2 跳过**(第 19 棒 577 ⇒ **+5 用例**)|`check:layering` ✅ 无新增违规。部署 47 一件(`/opt/dshs/lib/supervisor/plugin-assembly.js`,md5 `3c10048a…` 与本地逐字一致)+ `restart dshs dshs-worker`(双 active · NRestarts=0);**106 只投不重启**(`ExecMainStartTimestamp` 仍 `09:52:11` ⇒ 新代码**待下次重启生效**,零中断取向)⇒ **见 §5-19**。备份 `/opt/dsh/backups/lib-p20/47-pre-p20-20260925-111517.tgz` / `106-pre-p20-20260925-112626.tgz`。⛔ 未 commit/push、⛔ `im.ts` 零改动、⛔ 未手改用户 `home` 的 profile / `.pnpm`(诊断与验收均走**平台代码路径**或**平台命令复刻**)。 ⑥ 🔴 **新发现(⛔ 未动 · 已进 §5-18)**:那三条软链是**历史人工兜底遗留**(`dsh-module-fallback` 在**工作区代码里零命中** ⇒ 不是平台代码建的),而 pnpm 语义上本来会把它当 extraneous 清掉 ⇒ **是否清理待用户拍板**(清理会改实例的解析面);本修法只保证"平台装配不受影响",**⛔ 不等于这些软链该存在**。 **下一棒(唯一)** = **第 21 棒 · 执行棒** = automation `6e2e7e57-fffb-4ba4-87b7-841f3c0570c5`(一次性 · `scheduledAt = 2026-09-25T11:35` · `cwds = E:/ProgramData/AIProject/aliyun-dsh-server`);接续点 = **共享层台账两条语义缺陷(§5-10 ㈠㈡:清单与磁盘真值不同步 ⇒ 节点永远判 `stale`;`doShare` 回滚素材名实不符)**(见 §2 顶部)。🔧 同批已把本棒 automation `421a8bad-…` 置 `PAUSED` ⇒ 本线**同时只挂一个**。⛔ 别线的 `ACTIVE` 一律未动。 - ✅ **2026-09-25 10:2x–10:5x|第 19 棒 · 执行棒(第 1 件完成;第 2 件代码面全落 + 真机 2/4)** —— ① ✅ **回滚"缺失侧"补上(P0)**:`plugin-assembly.ts` 新增 `linkRefsOf()` / `rebuildMissingDepLinks()`(只补声明里的 `link:`、只补缺、目标不存在则**不建悬空链**、`node_modules` 不存在则跳过、属主 `chown` 尽力而为;纯 `symlinkSync` **不跑 pnpm**、不碰 `.pnpm`、不碰真目录)⇒ 接入 `restoreProfile()`(在 `pruneOrphanDepLinks` 之后)+ `applyProfileChanges()` 收尾。单测新增第 ⑨ 组 2 例(缺链重建 / 不造半安装态)+ 第 ⑩ 组 2 例(落盘拒绝 / 拒绝码同名)。 ② 🟡 **基础插件「默认开启 + 用户不可关闭」机制落地(§5-15 · 用户 09:3x 已拍板)**:**第 0 步真机确证** —— 47 上 `@deepseek-ai/dsh` **不声明任何插件清单**(无 `dsh.plugins`/`bundled`,只有 `configTrees`)、`@softspark` 在 dsh 包内**零命中** ⇒ "基础插件"**不是 dsh 的既有概念**、无"自带清单"可枚举;**唯一可比对的平台语义** = 门户 `admin.html`「运行环境」页的「平台必备 / 可移除」二分 ⇒ 技术自决 = **数据驱动**(台账 `essential` 标记,⛔ 不硬编码包名清单)。落地:`src/db/schema.ts` **v17**(`business_plugins.essential`;SQLite `INTEGER NOT NULL DEFAULT 0` / PG `BOOLEAN NOT NULL DEFAULT FALSE`;真机 `max(version)=17` 已落、池内**全 `f`** ⇒ 存量行为零变化)+ `types/repo/pg/sqlite/adapter` 五件(列 + `setBusinessPluginEssential`)+ **两处门禁**(路由 `/mine/apply` 对基础插件的 `enabled:false` 回 **409 具名** `essential_plugin` + 写 `disable_denied_essential` 审计;装配层结构性跳过并回 `refused[]`)+ **三条播种路径**(审批钩子 `/admin/users/:id/approve` / `essential/sweep` / `/mine/apply`)+ **门户 UI 不给关闭入口**(`poc/business-plugins/lib/client.js`:勾选框 `disabled`、状态徽章改渲染「平台基础插件 · 不可关闭」+ `title`;版本 `0.3.25→0.3.26`)。跨机:`remote-spawner` 请求体带 `essential`、`worker/agent.ts` 严格解析 bool。 ③ 🟡 **真机判据(47,临时 admin 会话,残留 0)**:**④ 关闭请求 ⇒ 409 具名拒绝且状态不变 ✅**(拒绝前后 `/mine` 完全一致)|**② 台账标记生效 ✅**(admin 列表 `essential=True`)|🔴 **① 默认播种 ⇒ `enabled`:未通过**(sweep 撞 `pnpm_failed`,`/mine` 仍 `enabled=False`)|🔴 **③ 门户 UI:未投放、未取证**(0.3.26 tgz 已 pack,⛔ 未铺)⇒ **⛔ 未宣称验收通过**。 ④ 🔴 **pnpm `exit 255` 根因首次成形(⛔ 未修 · 已写进 §5-16 + 第 20 棒)**:真机 `stderr=- stdout=-`(`--reporter silent` **零输出**)、12 s 内**连续 4 次** ⇒ **不是"偶发",在该 profile 上稳定复现**;**已排除**权限(共享层链 `755` 全程、uid `114801` 对新旧包目录 A/B 双读**均通过**)与残留(apply 失败后 `dependencies` **已回滚干净**、无半安装态;`/mine` 前后一致)。**新证据** = `.modules.yaml` 记 `storeDir=/ws/.local/share/pnpm/store/v3`(**HOME=ws 时代**)而平台以 `HOME=/home` 跑 pnpm(`src/supervisor/plugin-assembly.ts:499`)、`/.local/share/pnpm/store/v3` **不存在** ⇒ 高度指向 **ERR_PNPM_UNEXPECTED_STORE(立即拒绝特征 ≈2 s)**;🔴 **同仓 `scripts/ensure-biz-plugins.cjs:existingStoreDir()` 早在解同一个问题**(注释写明该错误码 / 档案 57)⇒ 正解 = **平台路径复刻它**(读 `.modules.yaml` 的 `storeDir` ⇒ 传 `--store-dir`)。 ⑤ 🟡 **门禁与部署**:`npm test` = **577 过 / 0 败 / 2 跳过**(第 18 棒 575 ⇒ **+2 新用例**)|`check:layering` ✅ 无新增违规|⚠️ `npm run verify` 尾段 `verify-platform-admin-section.mjs` **ERR_AMBIGUOUS_MODULE_SYNTAX = 既有缺陷**(单独跑同样失败,与本棒无关;本棒相关四项全绿)。47 部署**十一件**、106 **两件** md5 与本地**逐字一致**,备份 `/opt/dsh/backups/lib-p19/47-pre-p19-20260925-104848.tgz` / `106-pre-p19-20260925-105012.tgz`;47 `restart dshs` ⇒ active · NRestarts=0 · 门户 200;**106 worker 未重启**(有在线实例)。⚠️ 部署途中曾把 `lib/` 内容误落 `/opt/dshs` 根(散进 `supervisor/ web/ db/ worker/`)⇒ **已逐一 `rm` + `rmdir` 复原(四目录均系本次误建)后改按 `-C /opt/dshs/lib` 重投**;期间 47 **未用新代码重启**(无"带伤运行")。⛔ 未 commit/push、⛔ `im.ts` 零改动、⛔ 未手改用户 `home`。 ⑥ ⚠️ **副作用(已报 · 待用户表态)**:`@softspark/dsh-file-preview` 已**真发布到共享层**(881 文件;回滚素材 `/opt/dsh/backups/plugins/_softspark_dsh-file-preview/2.0.0.tgz`)⇒ 按 D1「共享层已发布 ∪ 我已启用」,**所有登录用户已可见并可自助启用**;若用户**先启用再被标 `essential`**,装配层将拒掉之后所有 `enabled:false` ⇒ **用户无法自行关闭**(撤回口 = `POST /api/plugins/business/:id/essential {essential:false}`)。 **下一棒(唯一)** = **第 20 棒 · 执行棒** = automation `421a8bad-699c-42d3-8458-058b0de42101`(一次性 · `scheduledAt = 2026-09-25T11:08` · `cwds = E:/ProgramData/AIProject/aliyun-dsh-server`);接续点 = **pnpm `exit 255` 可见化 + 根因修复 → 基础插件判据 ①③ 补齐(含门户 UI 投放取证)**(见 §2 顶部)。🔧 同批已把本棒 automation `0fdf7101-…` 置 `PAUSED` ⇒ 本线**同时只挂一个**。⛔ 别线的 `ACTIVE` 一律未动。 - ✅ **2026-09-25 09:4x–10:2x|第 18 棒 · 执行棒(三件全做完;第 3 件 5/5 全绿)** —— ① ✅ **共享层自足性转正(判据 ①②③ 全绿)**:正式修法进铺装流程 —— ③ 层 `plugin-assembly.ts` 新增 `resolveDshPackageRoot` / `resolveHostDshScopeDir` / `ensureSharedScopeLink` / `healSharedLayerSelfSufficiency`;`doShare` 目录就位后调用(**刻意放在 `hashTree` 之后** ⇒ 指纹不含软链、不引发重拉),worker `syncSharedLayerOnce` **每轮**幂等自愈;① 层 `dsh-install.ts` 原有一份 dsh 根解析副本**下沉到 ③ 层复用**。软链落点 = **包目录内** `/node_modules/@deepseek-ai`(⛔ 不落共享层根,否则对账面 `onDisk` 把它当 `extra` 上报)。真机:**删掉人工探针链**后重新 share ⇒ `linked=['_dsh-local_im-conversation-tabs','_dsh-local_storyforge','dsh-plugin-mcn-suite']`、`scope=/usr/local/lib/node_modules/@deepseek-ai/dsh/node_modules/@deepseek-ai`;47 三包 scope LINK 可解(240 项)、**106 三包 LINK → `/usr/lib/node_modules/...`**(两机 `npm root -g` 不同 ⇒ 必须各机本地解析,⛔ 不能由 Manager 算好随包分发);`off` 任务 `a524537251ff570a` / `on` 任务 `28dd88e4c391cad6` **双 success**、`rejected:[]`、探活通过、journald **`[mcn-suite] loaded v0.3.15(host 面 2 个:mcn, voxemw-cloud)`** + `skills ensured: 装 mcn-short-video@0.3.15(升级自 0.3.14)`、`/api/dsh/status` = `running:true port:20000 restarts:0`。 ② ✅ **回滚残留(P1)**:`restoreProfile()` 末尾接入 `pruneOrphanDepLinks()`(`declaredNamesIn` = `dependencies` ∪ `dsh.profile.bundles`,解析失败取空集**宁可不删**;只删**软链**、只删**未声明**、跳过 `.` 开头与真目录)+ 单测钉四条不变量。🔴 **另发现"缺失侧"未覆盖(已上报 · ⛔ 未修)**:本棒 `off` 撞 pnpm 偶发失败 ⇒ **声明还在、软链被删掉** ⇒ 实例启动崩 `Error: dsh: cannot resolve profile bundle "dsh-plugin-mcn-suite"` ×5 → `crash-loop-circuit-open`(冷却 600 s)⇒ **走平台正规口 `off`→`on` 恢复**(两次 success,`pnpm install` 自动重建软链,⛔ 未手工 `ln -s`、未手改 `package.json`/`.pnpm`)。建议下一棒:对声明中的 `link:` 依赖做确定性重建(纯 `symlinkSync`,**不需要跑 pnpm**)⇒ 两个方向都收敛。 ③ ✅ **移动端第 ④ 条修掉 + 五条重跑 5/5**:`mcn-mobile` 段扩到插件**全部**表单控件(`.v1rksel`、`.dvAcc_*` + `mcnNav_panel|page`、`dvAcc_page` 兜底,`!important` —— 因 `.dvAcc_*` 用 `font:var(--dsw-font-s-14)` **简写**且**按需注入**)⇒ 版本 **0.3.14→0.3.15**(两处真源)⇒ 以池内 0.3.14 tgz 为基底**确定性重打**(322 文件、变化面**恰 3 文件**、基底 ≡ 线上 `d387089b…`)⇒ share `action:"replaced"` `treeSha256 939206c76abb…` ⇒ 47/106 共享层双双 0.3.15(106 首轮 `stale→pulled`)⇒ 375×667 真机读数:**独占 ✅**(面板 `{x:56,y:0,w:319,h:667}`、会话区 `display:none`)|**可退出 ✅**(二次点 `.mcnNav_trigger` ⇒ `#/mcn`→`/`、`navCount` 1→0;现版 UI **不渲染** `.mcnNav_close` ⇒ 用等价判据)|**溢出 0 ✅**(竖 375=375、横 667=667)|**`SELECT.v1rksel` = 16px ✅(修前 13.33px)**|**横屏 ✅**(面板 611×375、溢出 0)。零点 = `style[data-plugin-css="mcn-mobile"]` 计数 **1**。 ④ 🔴 **新发现(⛔ 未修 · 已写进 §5 后续)**:`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|祖先目录 `/var/lib/dshs`、`/var/lib/dshs/users` 是 **711 ⇒ 该 uid 确无 `r` 位**但实测不影响 pnpm|磁盘 23 GB / inode 11% 充足)⇒ 高度指向**并发/时序**;**下次复现的正解动作** = 给失败路径补一次 `--reporter default` 重跑(属 `plugin-assembly.ts` 小改)。 ⑤ **门禁与卫生**:`npm run build` rc=0|`npm test` = **575 tests / 573 过 / 0 败 / 2 跳过**(基线 449/447/2;新增 6 用例 = 第 ⑦⑧ 组)|`check-layering` **✅ 无新增违规**。部署 47 四件、106 两件 **md5 逐字一致**,备份 `/opt/dsh/backups/lib-p18/`;插件回滚素材 `/opt/dsh/backups/plugins/dsh-plugin-mcn-suite/0.3.15.tgz`。清理:临时 admin 会话**残留 0**(仅余他人 3 条真实会话)、两机 `/tmp/p18*` **残留 0**。⛔ 未 commit/push、⛔ `im.ts` 零改动、⛔ 未手改用户 `home` 下的 profile / `.pnpm`。落地件 ⇒ `交付物/移动端字号扩面与共享层自足性转正-20260925.md`;截图 ⇒ `tmp/p18-shot-*.png`。 **下一棒(唯一)** = **第 19 棒 · 执行棒** = automation `0fdf7101-d6d9-4279-bce2-0ca89479a44b`(一次性 · `scheduledAt = 2026-09-25T10:26` · `cwds = E:/ProgramData/AIProject/aliyun-dsh-server`);接续点 = **回滚残留缺失侧(P0 · 小改)→ 基础插件「默认开启 + 用户不可关闭」机制(§5-15,用户已拍板)**(见 §2 顶部)。🔧 同批已把本棒 automation `ed2b5e61-…` 置 `PAUSED` ⇒ 本线**同时只挂一个**。⛔ 别线的 `ACTIVE` 一律未动。 - 🔴 **2026-09-25 09:3x|用户拍板(本线口径新增):dsh 自带的基础插件「默认开启 + 用户不可关闭」** —— 原话「这些 dsh 自带的基础插件 应该是 默认开启无法关闭的」。⇒ **§5 第 14 条那组"要不要发到共享层"的 A/B 两案一并作废**:用户要的不是"发到市场让用户自己去装",而是**「本来就该开着、且关不掉」**。落地形态 + 已做的最小取证 ⇒ **§5 新增第 15 条(待排棒)**。本轮为短程补录:抢锁 → 写入口 → 追加日志 → 放锁,⛔ 未开工做改造(第 18 棒已排定,避免抢锁打断链条)。 - ✅ **2026-09-25 08:5x–09:2x|第 17 棒 · 执行棒(第 1 件完成;第 2 件通过;第 3 件 4/5)** —— ① ✅ **可观测性补上并立刻见效**:`plugin-assembly` 新增 `pnpmExecError()`(stderr/stdout 尾巴拼进 message + 结构化字段 + 落 journald)、worker agent `/plugins/apply` 失败落日志(**worker 是独立单元,日志与 Manager 不通 —— 第 16 棒只查了 `dshs` 故漏掉全部证据**)、任务对象新增 `detail`。**三条根因全是它指出的**,不再是"52 s 后失败、原因不明"。 ② ✅ **第 2 件通过**:admin 实例依赖已是 `link:/var/lib/dshs/bundled-plugins/dsh-plugin-mcn-suite`,实例 **`[mcn-suite] loaded v0.3.14`(自 0.3.13 升上来)** ⇒ **共享层更新对已启用用户生效**(=本线 P2 目标)。修掉两条真 P0:**㈠ `-w` 无条件传**(平台自己铺的 profile 没有 `pnpm-workspace.yaml` ⇒ pnpm 报 `--workspace-root may only be used inside a workspace` 立即退出 ⇒ 门户启停插件全断;改前 52 s failed → 改后 **5 s success**)**㈡ `/restart-probe` 吃 10 s 默认客户端预算**(对端要真重启实例、预算 60 s ⇒ 必超时并重试 4 次,每次重试又重启一次实例;实测一条任务连打 8 次探活、100 s 后 `aborted due to timeout`)⇒ 独立预算 90 s。**㈢ 共享层不自足**(`node_modules` 零个 `@deepseek-ai` ⇒ `Cannot find package '@deepseek-ai/dsh-llm'` ⇒ 实例 crash-restart)⇒ **用人工软链探针验证修法有效**(240 个包),⛔ 探针**不是最终修法**,转正 = 第 18 棒第 1 件。已上线 4 个模块(md5 与本地逐字一致),备份 `/opt/dsh/backups/lib-p17/`。 ③ 🟡 **第 3 件 375×667 真机五条 = 4/5**:独占 ✅(会话区 computed `display:none`)|可退出 ✅(单面板**不渲染** `.mcnNav_close` ⇒ 用等价判据:二次点入口 ⇒ URL `#/mcn`→`/`、`.mcnNav_split` 1→0、会话区复原)|无横向溢出 ✅(竖 375=375、横 667=667,溢出 0)|横屏 ✅ |🔴 **聚焦不缩放 ❌** —— 工作台内唯一可见表单控件 `SELECT.v1rksel`(「全部赛道」)= **13px**,移动端规则只覆盖 `.mcnNav_input/select/textarea` ⇒ 覆盖缺口。零点:`style[data-plugin-css="mcn-mobile"]` 计数 **1** ⇒ 0.3.14 确在浏览器里生效。取证脚本 = `tmp/p17-visual.mjs`(零依赖 CDP)。 ④ 未修并上报:共享层自足性(P0,转正入第 18 棒)|移动端覆盖缺口(P1)|回滚不清理 `node_modules`(P1)|跨机错误消息截 200 字符(P2)|`preflight-lock.sh` 对工作区根文件判【D】⇒ 本棒按 guard 的锚点算法定域后开工(跨库文件,只报告)|⏸️ `@softspark/dsh-file-preview` 被复现动作一并关掉且从用户面清单消失(**待你拍板**,见 §5)。落地件 ⇒ `交付物/P0修通与移动端真机验收-20260925.md`;截图 ⇒ `tmp/p17-shot-*.png`。临时 admin 会话已删(残留 0)、47 `/tmp` 零残留。⛔ 未 commit/push、⛔ `im.ts` 零改动、⛔ 装配协议零改动。 **下一棒(唯一)** = **第 18 棒 · 执行棒** = automation `ed2b5e61-25b9-4b97-849c-1e0b204b669e`(一次性 · `scheduledAt = 2026-09-25T09:28` · `cwds = E:/ProgramData/AIProject/aliyun-dsh-server`);接续点 = **共享层自足性转正 → 回滚残留 → 移动端第 ④ 条**(见 §2 顶部)。🔧 同批已把第 17 棒自己的 automation 置 `PAUSED` ⇒ 本线**同时只挂一个**。⛔ 别线的 `ACTIVE` 一律未动。 - ❌ **2026-09-25 08:1x–08:4x|第 16 棒 · 执行棒(第 1、2 件完成并上线;第 3 件未通过 —— 被一条新抓出的 P0 挡住)** —— ① ✅ **迁包+改版+重投 47/106 全做完**:`mcn-mobile` 样式段从**从未投放**的 `dsh-plugin-mcn` v0.1.0 平移进**线上真身** `dsh-plugin-mcn-suite`(38 个引用类名齐备 0 缺;只改包名一处)⇒ 版本 **0.3.13 → 0.3.14**(两处真源)⇒ 以池内 tgz 为基底重打(**322 文件、只差 3 个文件、零增删**,sha256 `3ca65153…`)⇒ `POST /api/plugins/business`(http=200,`trustedOverride:false`)⇒ 两段式 share(`action:"replaced"`,`treeSha256 f414df79…`)⇒ **47 + 106 共享层双双 0.3.14**(106 走节点拉取,一轮 `stale→pulled` 收敛;两机 `client.js` md5 = `d387089b…`)。**改前副本 md5 与线上逐字相同**(无隐藏漂移)。⚠️ 行尾两次归一(源段 CRLF→LF;Windows 写文本默认 CRLF ⇒ 已回滚重做)。 ② 🔴 **抓出 P0(本轮最重要产出,⛔ 未擅自修)**:**共享层更新对「已启用」用户不生效,且平台无可用补救路径** —— admin 实例的依赖是 `file:…/.dsh-stage/dsh-plugin-mcn-suite.tgz`(**旧副本**,与共享层无关);正规补救 = 关掉再开,而**全量「关闭」稳定失败**(`0df1ea39ed3026c4` / `3eb4b72b73373e07`,各约 52 s 后 `failed`「安装失败,请重试或联系管理员」,`/mine` 复核仍 `enabled:true`)。已排除"暂存包缺失"。**附带第二条缺陷**:`journalctl -u dshs` 只打 HTTP 请求行,**失败原因不落任何日志** ⇒ 根因未能定位。⇒ 直接命中本线目标 **P2「插件更新要有定义」**。 ③ ❌ **第 3 件(375×667 真机五条)未取到读数** ⇒ **⛔ 未宣称通过**。但「**能不能真渲染**」这件事本棒已做成判定且结论 = **能**:47 侧**无任何浏览器**;**本机有真 Chrome**;`https://ai1net.com/u//dsh/` 带临时 admin `sid` 实测 **200 且平台自动下发实例凭据**;实例地址由 `/api/dsh/status` 直出。⇒ **路径已通,只差"实例上跑的是新版本"**。 ④ 🟡 **另两条台账缺陷**:㈠ 共享层清单记 `version 0.3.9`/`treeSha256 cb1c73fb…`,而**盘上是 0.3.13 内容**(不同步 ⇒ 节点对账会永远判 `stale`;本棒 put 新版后自愈,来源未查明)㈡ share 补落的"回滚素材"用**新**版本号命名且内容是**新**包 ⇒ 不构成"回到上一版";真正的上一版躺在 `0.3.9.tgz` 里(名字与内容不符)。 ⑤ **清理**:临时 admin 会话已删(残留守 **0**)|47 `/tmp` 零残留|**无半状态**(两次失败均未落盘,profile 依赖原样)|⛔ 未 commit/push、⛔ `im.ts` 零改动、⛔ 装配协议零改动、⛔ 未开任何新可见面。落地件 ⇒ `交付物/移动端修复迁包与投放-20260925.md`。 **下一棒(唯一)** = **第 17 棒 · 执行棒** = automation `68818629-c256-4cf4-8f15-3f86909e16db`(一次性 · `scheduledAt = 2026-09-25T08:46` · `cwds = E:/ProgramData/AIProject/aliyun-dsh-server`);接续点 = **补装配可观测性 → 修通重装配 → 补做真机五条视觉验收**(见 §2 顶部)。🔧 同批已把本棒(第 16 棒)的 automation `51e621ca-…` 置 `PAUSED`(once 型已触发但仍是 ACTIVE,被调度器重扫会重复开工)⇒ 本线**同时只挂一个**。⛔ 别线的 `ACTIVE` 一律未动。 - ✅ **2026-09-25 07:4x–08:0x|第 15 棒 · 执行棒完成(部署 47 + 数据面取数口真机验收 7/7 + 抓出并修复一条 P0 接线缺陷;第 2 件未通过:前提不成立)** —— ① **部署**:`lib/web/server.js`(与 47 原版**只差 7 行**,逐行 diff 确认)+ `lib/db/plugin-data/{runtime,registry}.js` + `lib/web/routes/plugin-data.js` + `sdk/im-plugin-client/` ⇒ **5/5 md5 逐字一致** + `restart dshs`(active · NRestarts=0 · 门户 200);备份 `/opt/dsh/backups/lib-p15/47-pre-p15-20260925-074345.tgz`。**投放前先做漂移比对**:`business-plugins.js`/`worker/agent.js`/`leased-spawner.js`/`config.js` 四件与本地**逐字一致** ⇒ 只投必要项。⚠️ 106 未部署(取数口是 Manager 侧路由)。 ② **真机验收(47 · 调用者 guest)= 7/7 PASS**:四拒全中(写他人 `user_id`/room 表缺 `room_id`/缺主键列/带 `created_at` 各得 `ok:false, reason:bad-args` + 具名 detail)|非成员读 room 表 = **0 行**(`value:[]`)|`insert`→`find` **回读一致**。**归属注入直连核**:`user_id` 全部 = 调用者本人;`probe_room` 0 行。**迁移回显首次在真机上看见** `PRIMARY KEY ("user_id","probe_key")` ⇒ **`id` = 声明第一列** 口径成立。负例:给无声明真插件取数 ⇒ `409 no-declaration`(不是 500、不是假装成功)。 ③ 🔴 **抓出 P0 接线缺陷(已修 + 已复验)**:库已 `ready`、鉴权通过,取数仍一律 `409 no-declaration`。根因 = **两个名字空间被混用** —— 池键/台账键是**原始包名**(`dsh-plugin-pdprobe`/`@dsh-local/storyforge`),而取数口把**归一化 id**(`pluginIdOf()` → `dsh_plugin_pdprobe`)交给了 registry,registry 内部拿它**精确查** `business_plugins` / `plugin_datastores` ⇒ **永远查不到** ⇒ **任何插件**取数都只会 `no-declaration`。**单测抓不到**(用假执行器直接构造声明,绕过路由层归一化)。修法(最小)= `src/web/routes/plugin-data.ts` 两处改传原始包名(status 路由对外仍回归一化 `pluginId`,响应契约不变)。复验:build rc=0 + 相关单测 **24/24** + 重投重启后 **7/7 PASS**。⛔ 未 commit / push。 ④ 🔴 **第 2 件(MCN 移动端视觉验收)未通过 —— 前提不成立**:线上**真身**是 `dsh-plugin-mcn-suite` **v0.3.13**(池 + 共享层),它的 `lib/client.js` 里 `mcn-mobile` 命中 **0**(`mcnNav_split` = 4);本地 suite(0.3.13)同样 **0**;而修复改在了 **`dsh-plugin-mcn` v0.1.0** —— 一个**从未投放**的包 ⇒ "重投 + 实例重启"**无从生效**。**故不做视觉验收、也不宣称通过**。✅ **可移植性已核对**:`mcn-mobile` 段是运行时注入的 `