# MCN 数据面接入改造 · 阶段一(纵向切片)— 执行回报 - **执行棒**:插件投放与分库线 · 第 25 棒(执行棒)|会话 `插件投放与分库线-第25棒`|witness 时间窗 **2026-09-25 13:10 – 14:1x** - **唯一执行单**:`D:/github/dsh_shenxian/dsh-server-docs/05-交接单/插件投放与分库线-02MCN数据面接入改造.md`(8 段齐全) - **入口**:工作区根 `接续入口_插件投放与分库线_20260922.md` §2(第 25 棒条目) - **判据口径**:全部读数取自**真机**(47 `bt-server` / 106 `test106`);每条附命令、输出原文、退出码。 --- ## 一、结论 **阶段一部分完成(A1–A4、A6 达成;A5 按单内预期记为「未达成」;A7 卫生六条全清)。** 未达成项只有一条且是**单内预先排除**的(红线归零属阶段二),另有 **3 项未闭合**(1 项待窗口、2 项属平台侧缺陷只报告)。 - ✅ **端到端最容易翻车的一段已打通**:声明(11 表)→ 上传 → `plan` → `CREATE DATABASE` → `migrate` → `state=ready` → 取数口可读可写可删。 - ✅ **两张表读路径切平台**:`account_videos` 平台侧读回 **36 行 = 本地 36 行**,游标翻页正确;`account_persona` 读回 **2 行**(洗净种子)。 - ✅ **`available:true`**,11 张表全在列;归属列由内核强制注入(返回值带 `user_id`,无需插件自己写)。 - ⚠️ **发现一条真实的阶段二阻塞**:6 个 `json` 声明列中有 **17 行存量值不是合法 JSON**(纯 Markdown)⇒ 声明成 `json` 时无法 verbatim 写入。详见 §五-未闭合项-1。 --- ## 二、判据表(命令 + 输出原文 + 退出码) ### 前置门禁(§二 只读前置 P1–P6 + §四 4.2 三条) | # | 判据 | 命令 | 输出原文(摘) | 结果 | |---|---|---|---|---| | P1 | 包内原无 `data` 键 | 读 `package.json` | `"dsh":{"bundle":…,"client":…}`;`files` 不含 `dsh.data.yaml` | ✅ | | P2 | 红线命中数 | `grep -rEc 'CREATE TABLE\|ALTER TABLE' lib/*.js lib/**/*.js` | 命中 **22**(10 张表 `CREATE TABLE` + 1 处注释 + 11 条 `ALTER TABLE`)= 与单内一致 | ✅ | | P3 | 真实改造面 | 同上扫描 | **SQL 调用点 222**(`index.js` 94/`imports.js` 51/`db.js` 37/`workshop.js` 26/`data.js` 11/`client.js` 2/`video-tools.js` 1) | ✅ | | P4 | 本地库落点 | 读 `lib/host/mcn/config.js` | `DSH_DIR = process.env.DSH_HOME ? join(DSH_HOME,'.dsh') : join(homedir(),'.dsh')`;`DB_PATH = join(DSH_DIR,'mcn-plugin.db')` | ✅ | | P5 | 取数口未就绪态 | `curl … /api/im/plugin-data/status`(无凭据⇒401;带身份) | `{"ok":false…}` 401 / `{"ok":true,"pluginId":"dsh_plugin_mcn_suite","available":false,"reason":"no-declaration",…}` | ✅ | | P6 | 平台校验函数在位 | 读 `lib/db/plugin-data/schema.js` | `validateDecl` / `parseDeclFromDir` / `pluginDbNameOf` 三者皆在 | ✅ | | 4.2-1 | 列级上限 | 读 `schema.ts` | `json` 列必填 `maxBytes`;行上限 `maxRowBytes=64 KiB`(自决 30720/49152 合规) | ✅ | | 4.2-2 | **回滚素材在位** | `ls /opt/dsh/backups/plugins/dsh-plugin-mcn-suite/` | `0.3.9.tgz` / `0.3.14.tgz` / **`0.3.15.tgz`** | ✅ | | 4.2-3 | 台账原状态 | `select … from plugin_datastores` | 原为空 `[]`;对账 `clean:true` | ✅ | > ⚠️ **P1 门禁前置已满足**:`state=none` ⇒ 门禁放行,故可先投放声明再建库。**但"先建库再投放"的顺序仍按 §五 S7→S8 执行**(见 §七 硬约束)。 ### 实施(S1–S8) | 步 | 判据 | 输出原文(摘) | rc | |---|---|---|---| | **S2** | 声明离线校验 | `level=ok` `tables=11` `columns=96` `indexes=14` `declarationDdl=25`;全部 `physicalName` 匹配 `/^p_[a-z][a-z0-9_]{0,40}$/`;全表 `scope=user`;文件纯 LF | 0 | | **S3** | 适配层单测 | `node --test tests/mcn-store.test.mjs` ⇒ **13 tests / 13 pass / 0 fail** | **0** | | **S4** | 三件语法 + 红线复核 | `node --check` ×3 全过;红线 **仍 22**(未删 `db.js`)⇒ **A5 如实记「未达成」** | 0 | | **S5** | 出包与差集 | 与 0.3.15 改前副本比对:**新增** 5 件(`dsh.data.yaml`/`lib/host/mcn/store.js`/`lib/im-plugin-client/index.mjs`/`tests/mcn-store.test.mjs`/`tests/smoke.mjs`);**内容不同** 3 件(`lib/host/mcn/data.js`/`lib/host/mcn/index.js`/`package.json`);`onlyBase=[]`(**无丢件**);包内 **324 件** | 0 | | **S6** | 上传候选池 | `HTTP=200`;池内 `version:"0.4.0"`;`compat.blocked:0`;`warnings:120` | 0 | | **S7** | 建库两段式 | ① `plan` ⇒ `state=pending`,**25 项**待做;② `datastore` ⇒ `outcome:"created"` **159 ms**,`state:"created"`;③ `migrate` ⇒ `state:"ready"`,`schemaVersion:1`,**25 条 executed**;④ 复演 ⇒ `{"state":"ready","items":0,"forbidden":0}`;⑤ 台账 `state=ready`;⑥ 对账 `clean:true`(`databases`=`ledgered`=`["dshs_pl_dsh_plugin_mcn_suite"]`,`orphans:[]` `missing:[]`) | 0 | | **S8** | 发布共享层 | `action:"replaced"`/`version:"0.4.0"`/`fileCount:659`/`backupTgz:…/0.4.0.tgz`/`rollbackTgz:…/0.3.15.tgz` | 0 | ### 验收 A1–A7 | # | 判据 | 命令 | 输出原文 | 判定 | |---|---|---|---|---| | **A1** | 库与台账 | ① `select count(*) from pg_database where datname='dshs_pl_dsh_plugin_mcn_suite'` ② `select row_to_json(x) from plugin_datastores x where plugin_id='dsh-plugin-mcn-suite'` | ① `A1_PG_DB_ROWS=1` ② `{"plugin_id":"dsh-plugin-mcn-suite","db_name":"dshs_pl_dsh_plugin_mcn_suite","state":"ready","schema_version":1,"plan_hash":"58226942d82290388e3090ed53e29f60bd71bd77d7b4454cece6f342b9e9bd6f","last_error":null,"created_at":1790313849773,"updated_at":1790314091010}` | ✅ **库存在 1 行;台账 `state=ready`、`schema_version=1`、`last_error=null`** | | **A2** | 取数口就绪 | `curl … /api/im/plugin-data/status -H "x-dsh-im-plugin-id: dsh-plugin-mcn-suite"` | `{"ok":true,"pluginId":"dsh_plugin_mcn_suite","available":true,"reason":null,"detail":null,"dbName":"dshs_pl_dsh_plugin_mcn_suite","tables":["hot_accounts","hot_account_ext","account_videos","video_analysis","account_video_source","account_persona","account_analysis","creative_log","rewrite_log","storyboard_log","script_review"]}` | ✅ **11 张表全在列** | | **A3** | 真机读(2 张表) | 见下 §三 命令块 | `COUNT_VIDEOS={"ok":true,"reason":"ok","value":{"n":36}}`(本地 SQLite 同账号 = **36**);首页 `limit=3&order=asc` ⇒ `7628915676010423397 / 7629803455813243043 / 7631176055600527729`(与本地前三逐字一致);游标第二页 `after=7631176055600527729` ⇒ `7631955574851074228…`(**严格递增,无重复**);`COUNT_PERSONA={"ok":true,…,"value":{"n":0}}` → 洗净种子后 `n=2`,读回 `2` 行且 `persona_id` 与投放值逐字一致 | ✅ **集合与本地一致**(详见 §五 说明:本次取证为「投放同值种子 → 平台读回 → 比对 → 删除」,⛔ 未迁存量) | | **A4** | 四拒 + 一条 0 行 | 见下 §四 命令块 | ① 写他人 `user_id` ⇒ `{"ok":false,"reason":"bad-args","detail":"不得写入他人的 user_id(归属列由内核注入)"}`;② `insert` 缺主键列 ⇒ `{"ok":false,"reason":"bad-args","detail":"缺主键列『persona_id』(它是本表的行标识)"}`;③ 带 `created_at` ⇒ `{"ok":false,"reason":"bad-args","detail":"审计列『created_at』由内核维护,⛔ 不可自写"}`;④ 未知表名 ⇒ `bad-args「未知表『nosuch_table』」`;⑤ 未知列 ⇒ `bad-args「未知列『nosuchcol』(表『account_videos』)」`;**⑥ 无匹配 ⇒ `{"ok":true,"value":[]}`(0 行)**;⑦ 缺插件身份头 ⇒ **HTTP 400**;⑧ 非法包名(`Bad Name`)⇒ **HTTP 400** | ✅ **五拒全 `bad-args`/400,0 行用例返回空数组而非报错** | | **A4-补** | **归属隔离(额外实测)** | 同 `POST`/`GET` 全部由内核注入 `user_id` | 读回行**含** `"user_id":"cce6d1cd-b376-4304-80f0-0e1c58c9ffde"`(请求体**从未带**该字段)⇒ 归属列恒定注入生效 | ✅ | | **A5** | 红线归零 | §二 P2 同一命令 | **22(未变)** | ⚠️ **未达成(按单内预期)** ⇒ 阶段二判据,⛔ 不作「已达成」 | | **A6** | 移动端五条 | — | 本棒改动文件清单 = `package.json` / `dsh.data.yaml`(新) / `lib/host/mcn/{store.js(新),data.js,index.js}` / `lib/im-plugin-client/index.mjs`(新) / `tests/*` ⇒ **不含 `lib/client.js`、`web/**`、`lib/host/mcn/index.js` 里的 UI 段**(改动点仅 L1394/1416/1554/2989 四处取数调用)⇒ **未改 UI** | ✅ **免跑**(依据 = 上述文件清单) | | **A7** | 卫生 | 见 §六 | 六条逐条点名 | ✅ | | **S9-附** | 插件路由真机 | `GET /mcn/api/account/persona?accountId=1629`、`/mcn/api/account/videos?…` | ⚠️ **实例仍载 0.3.15**(journald `[mcn-suite] skills ensured: 跳过 mcn-short-video@0.3.15(版本一致)`;实例启动于 `11:50:39`,早于共享层替换)⇒ **本项待实例重启后生效,本棒未取得读数** | ⏳ **未达成 → 见 §五-未闭合项-2** | --- ## 三、A3 命令块(可复现) ```bash # 前置:临时 admin 会话(用完即删,实测残留 0) SID=$(node /opt/dshs/mksess.cjs | tail -1) C="Cookie: sid=$SID"; H='Host: ai1net.com'; P='x-dsh-im-plugin-id: dsh-plugin-mcn-suite' U=http://127.0.0.1:3080 # 计数(平台侧) curl -s -H "$H" -H "$C" -H "$P" "$U/api/im/data/account_videos/count?eq.account_id=1629" # → {"ok":true,"reason":"ok","value":{"n":36}} # 首页(升序,limit=3) curl -s -H "$H" -H "$C" -H "$P" "$U/api/im/data/account_videos?eq.account_id=1629&limit=3&order=asc" # → {"ok":true,"value":[{"user_id":"cce6d1cd-…","aweme_id":"7628915676010423397","account_id":"1629",…}, …]} # 游标第二页(after = 首页末条主键值) curl -s -H "$H" -H "$C" -H "$P" "$U/api/im/data/account_videos?eq.account_id=1629&limit=3&order=asc&after=7631176055600527729" # → 首条 aweme_id=7631955574851074228(> 7631176055600527729,严格递增) # 人设读回(洗净种子后) curl -s -H "$H" -H "$C" -H "$P" "$U/api/im/data/account_persona?eq.account_id=1629&limit=5&order=desc" # → {"ok":true,"value":[{"user_id":"cce6d1cd-…","persona_id":"850003e3-…","account_id":"1629",…}]} # 列集合 = ["user_id","persona_id","account_id","analysis_time","content_json","summary","created_at","updated_at"] ``` **本地侧对照**(源库 `/var/lib/dshs/users/cce6d1cd-b376-4304-80f0-0e1c58c9ffde/home/.dsh/mcn-plugin.db`,`mode=ro`): ``` LOCAL_VIDEOS=36 LOCAL_PERSONA=2 LOCAL_AWEME_SAMPLE=['7628915676010423397','7629803455813243043','7631176055600527729'] VIDEO_COLS=['account_id','aweme_id','collect_count','collected_time','comment_count','duration', 'like_count','like_display','play_count','publish_time','share_count','source_dir', 'tags','topic','video_title','video_url'] # 16 列,与声明一致 平台侧:VIDEOS_INSERTED=36 FAILED=0 → 读回 n=36 → 删除 VIDEO_DELETED=36 FAILED=0 → n=0 ``` --- ## 四、A4 命令块(可复现) ```bash H='Host: ai1net.com'; P='x-dsh-im-plugin-id: dsh-plugin-mcn-suite'; U=http://127.0.0.1:3080 curl -s -X POST -H "$H" -H "$C" -H 'content-type: application/json' -H "$P" \ -d '{"persona_id":"1111…","account_id":"1629","user_id":"someone-else"}' "$U/api/im/data/account_persona" # → {"ok":false,"reason":"bad-args","detail":"不得写入他人的 user_id(归属列由内核注入)"} curl -s -X POST … -d '{"account_id":"1629","analysis_time":"2026-09-25"}' "$U/api/im/data/account_persona" # → {"ok":false,"reason":"bad-args","detail":"缺主键列『persona_id』(它是本表的行标识)"} curl -s -X POST … -d '{"persona_id":"2222…","account_id":"1629","created_at":1}' "$U/api/im/data/account_persona" # → {"ok":false,"reason":"bad-args","detail":"审计列『created_at』由内核维护,⛔ 不可自写"} curl -s -X POST … -d '{"x":"1"}' "$U/api/im/data/nosuch_table" # → {"ok":false,"reason":"bad-args","detail":"未知表『nosuch_table』"} curl -s -H "$H" -H "$C" -H "$P" "$U/api/im/data/account_videos?eq.nosuchcol=1" # → {"ok":false,"reason":"bad-args","detail":"未知列『nosuchcol』(表『account_videos』)"} curl -s -H "$H" -H "$C" -H "$P" "$U/api/im/data/account_videos?eq.account_id=999999" # → {"ok":true,"reason":"ok","value":[]} ← 0 行 curl -s -o /dev/null -w '%{http_code}\n' -H "$H" -H "$C" "$U/api/im/data/account_videos" # → 400(缺身份头) curl -s -o /dev/null -w '%{http_code}\n' -H "$H" -H "$C" -H 'x-dsh-im-plugin-id: Bad Name' "$U/api/im/data/account_videos" # → 400(非法包名) ``` > ⚠️ **与单内 §六 A4 的口径差异(须记录)**:单内第 ⑤ 条是「**非成员读 room 表 ⇒ 0 行**」,但本插件声明 **11 张表全部 `scope: user`,无 room 表** ⇒ 该条**在本插件不适用**;本棒以「**无匹配 ⇒ 0 行**」+ 追加「**未知表名 / 未知列 / 缺身份头 / 非法包名**」四条等价替代,覆盖面只多不少。 --- ## 五、产物指纹 | 项 | 值 | |---|---| | 包 | `dsh-plugin-mcn-suite@0.4.0` | | 出包 | `dsh-plugin-mcn-suite-0.4.0.tgz` · **1,962,024 B** · **324 件** | | sha256 | `96f67039dc8a16cd5f460f514d032560898fbedd19786e34a5b81a939e97b11c` | | 声明 | `dsh.data.yaml` · **7,391 B** · 纯 LF(CR=0) · **sha256 `9425c953f79fbec2b438e93488b294418f6798a3f93a3652e0c2b27a1297db78`** | | 声明结构 | `schemaVersion: 1`|**11 表 / 96 列 / 14 索引**|`declarationDdl` 25 条|全 `scope: user` | | 改前副本(比对基线) | `dsh-plugin-mcn-suite-0.3.15.tgz` sha256 `ede02821c56d7d1ad802e16a3e0d46b1764bfdf0f75c329d4dc1503af2d9d0b3` | | 服务器回滚素材 | `/opt/dsh/backups/plugins/dsh-plugin-mcn-suite/0.3.15.tgz`(**本棒 share 自动落盘**)+ 既有 `0.3.9/0.3.14/0.3.15` | | 声明快照(服务器) | `/var/lib/dshs/bundled-plugins/dsh-plugin-mcn-suite/dsh.data.yaml` = **7,391 B** 在位 | | 台账 `plan_hash` | `58226942d82290388e3090ed53e29f60bd71bd77d7b4454cece6f342b9e9bd6f` | **两机差集**: | 机 | 共享层版本 | 结论 | |---|---|---| | 47 `bt-server` | `/var/lib/dshs/bundled-plugins/dsh-plugin-mcn-suite/package.json` ⇒ `0.4.0`,`dsh.data.yaml` 在位 | ✅ 已同步 | | 106 `test106` | **尚未同步** | ⏳ ⛔ 本棒未重启 106(待窗口);节点侧首轮 `stale→pulled` 属预期 ⇒ **待重启面记入 §五-未闭合项-3** | --- ## 六、未闭合项 | # | 现象 | 归属 | 下一步 | |---|---|---|---| | **1** | 🔴 **`json` 声明列 ⇄ 存量值不守恒**:6 个 `json` 列中 **4 个有非法 JSON 存量** —— `account_video_analysis.content_json` **12/12 非法**、`account_persona.content_json` **4/4 非法**、`account_analysis.content_json` **1/1 非法**(`account_video_source.content_json` 39 行、`script_review.dims_json/issues_json` 各 2 行 **全合法**)。实测报错:`{"ok":false,"reason":"disabled","detail":"22P02: invalid input syntax for type json"}`。存量值实为**纯 Markdown 文本**(如 `'# 飞科F8剃须刀视频拆解分析…'`)。⇒ **阶段二迁移这些列前必须先做值清洗,或把声明改为 `text`**;⚠️ 因 `alter_type` 是平台禁令(只增不减),改类型须**删库重建** ⇒ 属不可逆操作,**留待阶段二单内拍板** | **插件侧选型(本棒)+ 平台侧语义** | 阶段二必办第 1 条 | | **2** | **插件侧路由端到端未取读数**:实例仍载 `0.3.15`(实例启动 11:50:39 早于共享层替换 13:1x)⇒ `/mcn/api/account/{videos,persona}` 跑的是旧代码,不能用于证明新版适配层 | 部署面(**待实例重启**) | 需**中断该在线用户**才可重启 ⇒ **属红线门禁,等用户给窗口**;⛔ 本棒未重启 | | **3** | **106 共享层未同步 / 未重启**:106 上 `dshs-worker` 无 `dshs.service`,节点侧拉取需触发;本棒按纪律**只投不重启 106** | 部署面 | **待窗口**(与既有「106 待重启面」合批) | ### 🔴 三条平台侧缺陷(⛔ **本棒未改 `src/**` 一行,只报告**) | # | 缺陷 | 证据 | 影响 | |---|---|---|---| | **①** | **物理表名长度上限未被声明校验覆盖**:`PLUGIN_DB_RE = /^p_[a-z][a-z0-9_]{0,40}$/` 只在校验阶段「不查」,直到建 DDL/上传才 500。`validateDecl`(`src/db/plugin-data/schema.ts`)**放行**非法长度 ⇒ 上传时 `HTTP 500「内部错误:非法表名 p_dsh_plugin_mcn_suite_account_video_analysis」`。根因:`pluginId` 归一化后 **20 字符** + 分隔符 ⇒ 声明表名可用 **≤ 20 字符** | 本棒实测:`account_video_analysis`(22 字符)被拦 ⇒ 改名 `video_analysis`(14)后通过 | **开发期反复试错**(须先建库失败才知);建议把长度判据并入 `validateDecl` | | **②** | 🔴 **`bigtext` 类型往返不守恒**:声明 `type: bigtext` ⇒ PG 落 `TEXT` ⇒ 回读归 `text` ⇒ 预演恒报 `alter_type` 禁令 ⇒ 状态**永久 `blocked`** ⇒ **该插件所有用户永远无法启用** | 本棒实测:迁移后复演 `state=blocked`,禁令原文 = `alter_type: bigtext → text`(`rewrite_log.script_text` / `storyboard_log.script_text` / `script_review.content`) | **事故级**:任何用 `bigtext` 的插件都启不了 ⇒ 建议平台侧修「中性类型回读归一」或将 `bigtext` 从 `NEUTRAL_TYPES` 移除 | | **③** | **单内命令样例与实装不符+缺一步**:§五 S7 用 `-d '{"pluginId":"…"}'`,实装参数名是 **`id`**(否则 `400 invalid_plugin_id`);且 §五 未写明 **`migrate` 是 `ready` 的必需步骤**(`datastore` 只 `CREATE DATABASE`,落 `state=created`)⇒ 易误判「建库即就绪」 | 本棒实测:改 `{"id":…}` 后通过;`state=created` 时门禁拦下投放,补 `migrate` 后 `ready` | 交接单/规范文档回改(属文档面) | **本棒的修法(已落,⛔ 不改平台源码)**:①② 通过在 `dsh.data.yaml` 头部写**两条硬约束注释**兜住(表名 ≤ 20 字符 + `bigtext` 往返缺陷禁用),并改名为 `video_analysis`、把三处 `bigtext` 改 `text`(**保留 `maxBytes: 49152`,PG 类型同为 `TEXT`、上限意图不变**)。 --- ## 七、硬约束与阶段一范围自查 | 硬约束 | 落地情况 | |---|---| | **必须先建库、再投放** | ✅ 顺序 = S6 上传 → **S7 建库(`ready`)** → S8 共享层替换;⛔ 未换序 | | ⛔ 不迁存量数据 | ✅ 平台侧种子为**同值取证行**,读回后**全部删除**(`VIDEO_DELETED=36 FAILED=0`、`PERSONA_DELETED=2 FAILED=0`、`AFTER_CLEAN_*` 均为 `n=0`);本地 `mcn-plugin.db` **只读**(`mode=ro`),⛔ 未写一行 | | ⛔ 不删 `db.js` | ✅ 红线仍 **22**(A5 如实记「未达成」) | | ⛔ 不改 UI | ✅ 改动点仅 4 处取数调用(`data.js:113/129`、`index.js:1394/1416/1554/2989`) | | ⛔ 不动其余 7 张表读写代码 | ✅ 未改(mtime 未变) | | ⛔ 不改 `src/**` | ✅ **本棒窗口(13:10 起)内 `src/` 零改动**(实测 `src/` 20 个改动文件最晚 mtime = `2026-09-25 12:20:57`,**早于开工时刻**) | | ⛔ 不重启 106 | ✅ 未重启 | | ⛔ 不 commit / push | ✅ 未 commit / 未 push | --- ## 八、卫生声明(A7 六条逐条点名) 1. ⛔ **未改 `src/**`** —— `git -C D:/github/dsh_shenxian status --short -- src` 共 20 个 `M`,**最晚 mtime `2026-09-25 12:20:57`(早于本棒 13:10 开工)**;`src/db/plugin-data/*` 与 `src/web/routes/plugin-data.ts` 为 `??`(**上游棒新增的未跟踪文件,非本棒产物**);本棒窗口内改动数 = **0**。 2. ⛔ **`im.ts` 零改动** —— 同上(未出现在改动面)。 3. ⛔ **未手改用户 `home` 下 profile / `.pnpm`** —— 本棒对用户目录**只读**(`sqlite open mode=ro` / `find` / `ls`)。 4. ⛔ **未重启 106** —— 106 仅只读盘点,⛔ 未重启任何服务。 5. ⛔ **未 commit / 未 push** —— 工作区与文档库均无本棒提交。 6. ✅ **清理完成** —— ① 临时 admin 会话 `poc-curl2`:`TEMP_SESSIONS_BEFORE=10 → DELETED=10 → AFTER=0`(走平台自身 `pg` 驱动,⛔ 未回显凭据);② 47 `/tmp/p25*` 残留 **0**;③ 本机中间物已收敛至 `tmp/p25/`(脚本 + 改前副本)。 7. 🟡 ⛔ **未清 `.dsh-module-fallback`**(属待拍板项)|⛔ **未动 `D:/github/dsh_shenxian/src/**` 任何文件**。 --- ## 九、附录 · 本棒踩坑记录(供后棒免重蹈) 1. **`preflight-lock.sh` 对工作区根文件判【D】未归类** ⇒ 按任务单既有惯例用 guard 锚点算法定域(`aliyun-dsh-server/<文件名>`)开工;⛔ 未改跨库脚本。 2. **`npm pack --pack-destination dist` 需先 `mkdir -p dist`**(否则 ENOENT)。 3. **Python 重写文本默认 CRLF** ⇒ 每次改完必须把 `\r\n` 归一并按字节复核 `CR=0`,否则进包携带 CRLF。 4. **`pkill -f "<库名>"` 会杀掉自己** —— `ssh '…' pkill -f "dshs_pl_…"'` 里该模式匹配到远端 bash 自身命令行 ⇒ 自杀、无输出、rc=1。✅ 正确姿势:模式写成不与自身命令行重叠的片段,或先 `ps` 取 pid 再 kill。 5. **`plan`/`datastore` 的 body 参数名是 `id`**(⛔ 不是 `pluginId`)。 6. **实例健康快照**:pid `1173881`、端口 `20001`、`restarts:0`、实例 URL `https://admin.ai1net.com/?token=…`;本地库计数 `account_videos=362 / account_persona=4 / hot_accounts=5 / rewrite_log=13`。 --- **变更记录**:2026-09-25 14:1x 本棒落档(第 25 棒 · 执行棒)。判据链可被第三方按 §三/§四 命令块复现。