Files
dsh_ai1net_server/交付物/MCN数据面接入-阶段一-20260925.md
T
admin 318430c9e9 chore(工作区): 插件投放与分库线收口入库(第 35 棒 + 22:44 拍板落盘)
范围 = 本线(插件投放与分库线)产物 + 记忆类,共 27 件:
- 接续入口_插件投放与分库线(1 件):§0 新增 22:44 拍板行;§2 第 36 棒范围改 5 步、条件步转无条件
- 交付物(22 件):MCN 数据面接入阶段一/阶段二系列(含 B 案落地与 shadow 读数)、
  P0 修通与移动端真机验收、pnpm-EPERM、两机 lib 差异、共享层台账语义、
  基础插件身份与回滚、插件接入验收、插件数据面取数口、移动端迁包与字号扩面、跨机错误消息
- 记忆(4 件):2026-09-24 / 2026-09-25 日志、MEMORY.md、本棒 automation 执行记录

⛔ 未含他线在途改动(只报告、不代提交):机制层 CODEBUDDY.md / state.py /
.codebuddy/rules/server-ops.md、接续入口_IM线、接续入口_StoryForge验收线、
其余 29 个 automation 目录、docs/规则与载体/、归档/、接续包_*。
2026-09-25 23:06:29 +08:00

213 lines
21 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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 棒 · 执行棒)。判据链可被第三方按 §三/§四 命令块复现。