Files
dsh_ai1net_server/交付物/MCN数据面接入-阶段二-B案落地与shadow读数-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

378 lines
30 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 数据面接入 · 阶段二 · **B 案落地与 shadow 读数**(第 35 棒 · 执行棒)
> **会话**:`p35-exec-plugin-dataline`(2026-09-25 21:47 开工 → 22:5x 收口)
> **工作区**:`E:/ProgramData/AIProject/aliyun-dsh-server`(本件落 `交付物/`)
> **上游**:入口 `接续入口_插件投放与分库线_20260922.md` §0 最新行 + §2「⏭️ 本轮动作(第 35 棒)」
> **唯一执行单**:`$DOC/05-交接单/插件投放与分库线-04MCN数据面接入改造-阶段二第三批.md`(§五 S0–S8 / §六 A1–A9)
> **依据件**:`$WS/交付物/MCN数据面接入-阶段二-第三批接线完成-20260925.md`(第 34 棒)
> **变量**:`PKG=D:/dshworkspace/plugin_package/dsh-plugin-mcn-suite`|`WS=E:/ProgramData/AIProject/aliyun-dsh-server`|`DOC=D:/github/dsh_shenxian/dsh-server-docs`
---
## 〇 结论(3 行)
1. **① – ⑤ 全部达成**(处方 (a) 落码 → 出包 `0.5.0` → 上池 → 加列迁移 2 条 → 回填),**A2(取数口面)达成**、**A1 两表达成**、A3/A4/A6/A7/A9 达成。
2. 🔴 **⑥ 未全绿** ⇒ 按执行单 §五 S6/S7 与任务书铁律,**停住不切档(⛔ 不带病切换)**,**⑦ 整条留给下一棒**;**A5 未达成**、**A1 的 `account_video_source` 一支具名未达成**、**A8 如实记「两机不一致」**。
3. 🔴 **根因两条已钉死**(**均非数据丢失**,均**非本棒引入**):**甲**=本地 5 组重复 `aweme_id` ⇒ 计数 39 ≠ 34(平台主键唯一 ⇒ 必然折叠);**乙**=平台 `jsonb` **重排对象键序** × 插件 `canonicalJsonText()` **保留序** ⇒ 逐键比对**假阴性**(换成「与序无关」比对 ⇒ **34/34 全等**)。
> **归属**:本棒**已动生产**(47 共享层投放 + 平台库 2 条 `ADD COLUMN` + 存量回填),**未动实例本地库、未重启任何服务、未切任何档**。
> 🔴 **本棒不 commit / 不 push** —— 用户 2026-09-25 22:44 **已拍板「不纳入版本管理」**(§5-②)⇒ 本条由"待定"转为**长期有效**;源码保底改为定期打包到 `E:/dsh-backups/plugin-src/`(见 §5-②、§六)。
> ✅ **两条待拍板已于 22:44 全部到手**(① ⇒ A 案删本地 5 条旧副本;② ⇒ B 案不纳管 + 定期打包备份,机制已落地)⇒ **第 36 棒 ⑥ 可闭环、⑦ 可执行**。
---
## 一 本棒动作逐条状态(按序 · 与任务书 ①②③④⑤⑥⑦ 一一对应)
| # | 动作 | 状态 | 一句话读数 |
|---|---|---|---|
| ① | 落 (a) 处方代码(正文列 gzip 编解码落 `store.js` 的 `normalizeRow`/`rowsOf`) | ✅ 达成 | 新增 `GZIP_COLUMNS`/`GZIP_PREFIX="gz1:"`/`gzipToJsonText`/`gunzipIfPrefixed`/`restoreGzipText`/`restoreGzipColumns`/`packOversizedRow` + `jsonbLike()` 补正;**编解码只在插件侧**,调用方零改动。单测 **66/66**(`# pass 66 / # fail 0`)、`node --check` 9 文件全 `rc=0` |
| ② | 出包 `0.4.0` → `0.5.0` | ✅ 达成 | `dist/dsh-plugin-mcn-suite-0.5.0.tgz` **2,017,667 B**/条目 **324**/md5 **`5cbccac1348e5dcb8d11bf400a000498`** |
| ③ | 上池 → 发布共享层 | ✅ 达成 | `POST /api/plugins/business` →200(`compat.level=ok`)⇒ 迁移到 `state=ready` ⇒ `POST /api/plugins/business/share` →200 `action:"replaced"` `version 0.5.0` |
| ④ | 加列迁移 2 条 `ADD COLUMN`(`hot_accounts.local_id` / `account_videos.local_id`,可空) | ✅ 达成 | `elapsedMs:16`、`backup:"taken"`(`/opt/dsh/backups/plugin-db/dsh-plugin-mcn-suite/v1-2026-09-25T14-06-56-401.sql`);库读数 `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_video_source` ins=16/skip=23、`account_videos` skip=362/**backfill=362**、`hot_accounts` skip=10/**backfill=5**、失败 0;幂等重跑 ins=0;本地库 sha256 `b37f8aab…` 前后逐字一致、mtime 未变 |
| ⑥ | 三表 `shadow` 读数(全 `match=true` 才允下一步) | 🔴 **未全绿** | `account_videos` **match=true**(362/362)/`hot_accounts` **match=true**(5/5)/**`account_video_source` match=false**(platform **34** / local **39**)⇒ **停住不切档** |
| ⑦ | 切档 + 重启验收(三表 → `platform`;47 实例 + 106 合并窗口;A2 E2E + A5 真机读数) | ⛔ **未执行**(整条留下一棒) | 前置 ⑥ 未全绿 ⇒ ⛔ 未切任何表、⛔ 未重启 47、⛔ 未触碰 106;实例 `data-plane.json` 仍 **4 表 `platform`**(`account_persona`/`creative_log`/`account_analysis`/`video_analysis`),三表**仍 `local`** |
> ⚠️ **本棒未按任务书原意走到 ⑦** —— 依据是任务书自己写的判据:「⛔ **若 ⑥ 未全绿 ⇒ 停住不切档**(⛔ 不带病切换),如实上抛,⑦ 整条留给下一棒。」
---
## 二 判据表 A1–A9(**命令 + 输出原文 + 退出码** · 可被第三方复现)
> 探针环境:`ssh [email protected]`;node = `/usr/local/bin/node`(`--experimental-sqlite`);探针包 `/tmp/p35/pkg`(指纹逐字等于本机与共享层)。
> 🔴 **R10 说明**:实例 uid(`dsh-cce6d1cdb376430480f0`)**被平台网络隔离**(实测连 `127.0.0.1:3080` 都 `ECONNREFUSED`,而 root `curl` = 200)⇒ 探针**只能以 root 跑**,故一律 `DSH_HOME` 重定向到 `/tmp/p35/dshhome`(**实例 home 零写入**)+ 本地库 `readOnly` 打开。
### A1 存量迁移一致性
```bash
# [真机 47] root + DSH_HOME 重定向;探针 run-p35.mjs §4
node --experimental-sqlite /tmp/p35/run-p35.mjs # 退出码 0(RUN_RC=0)
```
```
A1 [account_video_source] 本地键=39 平台行=34 onlyLocal=[] onlyPlatform=[] truncated=false match=true
A1 [account_videos] 本地键=362 平台行=362 onlyLocal=[] onlyPlatform=[] truncated=false match=true
A1 [hot_accounts] 本地键=5 平台行=5 onlyLocal=[] onlyPlatform=[] truncated=false match=true
```
> ☝️ 这是**探针自算**的「主键集合」判据(`onlyLocal`/`onlyPlatform` 皆空 ⇒ 唯一键集合**逐字一致**)。
```bash
# [真机 47] 插件自身的影子读口径(compareRows:计数 + 主键集合)
node --experimental-sqlite /tmp/p35/run-p35.mjs # 退出码 0
```
```
[mcn-store] shadow what=probe35.account_video_source platform=34 local=39 match=false onlyPlatform=[] onlyLocal=[]
[mcn-store] shadow what=probe35.account_videos platform=362 local=362 match=true
[mcn-store] shadow what=probe35.hot_accounts platform=5 local=5 match=true
```
**判定**:🔴 **部分达成** —— 两表 `本地 n == 平台 n` ✅;`account_video_source` **本地 39 ≠ 平台 34** ⇒ **未达成(具名:5 组重复 `aweme_id`,见 §三-甲)**。
**`badRows`**:三表 **全 0**(⛔ 无空键、无坏行);空 `aweme_id` 行数 **0**(执行单 §附录 C-2 预计"非零" ⇒ **实测为零**,具名纠正)。
### A2 三表读写 E2E(平台取数口)
```bash
# [真机 47] 临时会话(mksess.cjs 直插,TTL 600s)+ Host/Cookie 垫片
bash -s < tmp/p35/remote-09-a2.sh # 退出码 0(A2_DONE)
```
```
### 0) 取数口就绪态
{"ok":true,"pluginId":"dsh_plugin_mcn_suite","available":true,"reason":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"]}
=== ① hot_accounts(PK=account_name) === 7 步 全 HTTP=200(读空→建→读回→改→读回→删→读空)
=== ② account_videos(PK=aweme_id) === 7 步 全 HTTP=200
=== ③ account_video_source(PK=aweme_id,两个 json 列) === 7 步 全 HTTP=200
3 读回 ⇒ {"content_json":"正文 E2E","analysis_json":"分析 E2E", ...} # jsonb 脱一层引号,存储形态假设成立
=== ④ 负例(应全 bad-args) ===
N1 写他人 user_id HTTP=200 {"ok":false,"reason":"bad-args","detail":"不得写入他人的 user_id(归属列由内核注入)"}
N2 缺主键列 HTTP=200 {"ok":false,"reason":"bad-args","detail":"缺少主键列「account_name」(它是本表的行标识)"}
N3 带内核审计列 HTTP=200 {"ok":false,"reason":"bad-args","detail":"审计列「created_at」由内核维护,⛔ 不可自写"}
N4 未知表 HTTP=200 {"ok":false,"reason":"bad-args","detail":"未知表「nosuch_table」"}
N5 未知列(过滤) HTTP=200 {"ok":false,"reason":"bad-args","detail":"未知列「nosuchcol」(表「account_videos」)"}
N6 未知列(写入) HTTP=200 {"ok":false,"reason":"bad-args","detail":"未知列「nosuchcol」"}
```
> ⚠️ 该脚本**尾段**(PG 残留核查)首轮因内嵌 `node -e` 的引号/反引号被 bash 吃掉而**报语法错误未执行**(`bash: line 79: syntax error near unexpected token ')'`)—— 已按「把 node 脚本抽成独立 `.mjs` 上传执行」重做,**本轮补齐**(见 A2-附)。
**A2-附 PG 侧残留核查**(补齐 · 退 `0`)
```bash
node --experimental-sqlite /tmp/p35/diag-src3.mjs # 退出码 0(DIAG3_RC=0)
```
```
### J) A2 收尾:PG 侧残留核查(应全 0)
PG_RESIDUE hot_accounts (cols=account_name,local_id) = 0
PG_RESIDUE account_videos (cols=aweme_id,local_id,account_id) = 0
PG_RESIDUE account_video_source(cols=aweme_id,video_id) = 0
PG_TOTALS={"a":"5","b":"362","c":"34"}
```
**判定**:✅ **达成(平台取数口面)** —— 三表 7 步读写 **全 200**、6 个负例**全 `bad-args`**、0 行返回 `{ok:true,value:[]}`、写入残留 **0**。
⚠️ **「实例内」那一半仍未达成**(执行单 A2 原注:「实例内读数需 47 重启 ⇒ 未重启则该条未达成」)⇒ **随 ⑦ 留给下一棒**。
### A3 结构改写面处置完毕
```bash
grep -rniE "\bJOIN\b" lib | grep -v "\.join(" # 残留全落在 local() 回调 / 本地专属分支 / 第四批主表 rewrite_log(具名豁免)
grep -rn "LIMIT ? OFFSET ?" lib
grep -rnE "ROW_NUMBER|OVER\s*\(" lib
```
**判定**:✅ 达成(第 34 棒已验并复扫;**平台档路径零命中**;本棒**未改任何调用点** ⇒ 读数逐字不变)。
### A4 表名对齐彻底
```bash
cd "$PKG"
grep -rE "account_video_analysis" . | grep -v /node_modules/ | wc -l # → 0
grep -c "hot_account_ext" lib/host/mcn/store.js # → 10
```
**判定**:✅ 达成(**0** / **10 > 0**)。
### A5 已按表切档(三张表)
```bash
# [真机 47] 读该用户 <home>/.dsh/data-plane.json
cat /var/lib/dshs/users/cce6d1cd-b376-4304-80f0-0e1c58c9ffde/home/.dsh/data-plane.json
```
```
INSTANCE_DP_UNCHANGED={"tables":{"account_persona":{"writeMode":"platform","mode":"platform"},
"creative_log":{"writeMode":"platform","mode":"platform"},
"account_analysis":{"writeMode":"platform","mode":"platform"},
"video_analysis":{"writeMode":"platform","mode":"platform"}},"updatedAt":"2026-09-25T09:07:28.440Z"}
PLANE={"tables":{"account_video_source":{"mode":"shadow"},"account_videos":{"mode":"shadow"},
"hot_accounts":{"mode":"shadow"}},"updatedAt":"2026-09-25T14:12:06.586Z"}
```
> ☝️ 第一个是**实例真档**(仍 4 表);第二个是**探针档**(`DSH_HOME` 重定向到 `/tmp`,只用来取 shadow 读数)—— ⛔ 探针档**不是**实例档,两处别混。
**判定**:🔴 **未达成** —— 三表**仍 `local`**(总数仍 4 表 `platform`,非 A5 要求的 7 表)。**原因 = ⑥ 未全绿(依铁律停住)**。
### A6 旧库备份在位 + 零数据损失可回滚
```bash
# [真机 47]
sha256sum "$HOME/.dsh/mcn-plugin.db" | cut -d' ' -f1 # 迁移前后各取一次
tar -tzf /opt/dsh/backups/plugin-data/dsh-plugin-mcn-suite/cce6d1cd-…-mcn-plugin-db-20260925-141436.tar.gz
```
```
b37f8aab51a90f7481eb321e50456ca347635bcf2bfce567ea4200d88c18277a # 迁移前
b37f8aab51a90f7481eb321e50456ca347635bcf2bfce567ea4200d88c18277a # 迁移后(逐字一致)
DB owner=dsh-cce6d1cdb376430480f0 mtime=2026-09-20 23:05:05.178301565 +0800 # mtime 未变
```
**判定**:✅ 达成 —— 备份在位可解出;本地旧库**未被删、未被改**(sha256 + mtime 双证)。
🔴 **本棒新增一处 R10 修复(必须记)**:`<home>/.dsh/data-plane.json` 属主为 **`root:root`**(**第 32 棒 root 探针遗留**)⇒ 切档前必须先修,否则**实例 uid 写不动档位**。已 `find "$UHOME" -user root -exec chown "$UOWNER:$UOWNER" {} +` ⇒
```
ROOT_OWNED /var/lib/dshs/users/…/home/.dsh/data-plane.json root:root 644
ROOT_OWNED_AFTER_CHOWN=0
… data-plane.json owner=dsh-cce6d1cdb376430480f0:dsh-cce6d1cdb376430480f0 mode=644
ROOT_OWNED_FINAL=0
```
### A7 移动端五条
**判定**:✅ **免跑** —— 本棒**未改任何 UI 代码**(`lib/client.js` 零改动;改动面恰 **6 件**:`store.js`/`package.json`/`tests/mcn-store.test.mjs`/(出包物)`dist/*.tgz`;调用方 **0 处**)。
### A8 两机一致(代码面 / 运行面分开记)
```bash
# [47]
grep -m1 '"version"' /var/lib/dshs/bundled-plugins/dsh-plugin-mcn-suite/package.json
md5sum /var/lib/dshs/bundled-plugins/dsh-plugin-mcn-suite/lib/host/mcn/store.js | cut -d' ' -f1
grep -c "gz1:" /var/lib/dshs/bundled-plugins/dsh-plugin-mcn-suite/lib/host/mcn/store.js
```
```
"version": "0.5.0",
bc02c23c080eee58d8873508badc1c5b # 与本地 PKG 逐字一致
4 # 处方 (a) 已进共享层
```
```bash
# [47] 节点登记表(查 106 端点)
SELECT * FROM dsh_hosts;
```
```
HOST {"id":"w-47","endpoint":"http://127.0.0.1:19100",…,"status":"up","via":"local"}
HOST {"id":"w-106","endpoint":"http://127.0.0.1:19000",…,"status":"up","via":"relay"}
```
**判定**:🔴 **不一致(如实记,⛔ 不得写「一致」)** —— **47 共享层 = `0.5.0`(本棒投放,已取证)**;**106 本棒未触碰、未取证**(`w-106` 仅经中继可见、端点是回环隧道,本棒无 106 作业面)⇒ 106 侧同步**随重启窗口在下一棒做**。
### A9 红线零新增
```bash
cd "$PKG"
grep -rEn "CREATE TABLE|ALTER TABLE|DROP TABLE|better-sqlite3|from 'pg'|require\('pg'\)" . | grep -v /node_modules/ | wc -l
```
```
23
```
**判定**:✅ 达成(**23**,与第 27/28/29/33/34 棒同值;处方 (a) 只加**运行时编解码**,**零新增 DDL / 驱动**)。
### 产物指纹(`report §八-6` 要求)
| 件 | 指纹 |
|---|---|
| `lib/host/mcn/store.js` | md5 **`bc02c23c080eee58d8873508badc1c5b`** / 117,808 B / 纯 LF |
| `tests/mcn-store.test.mjs` | 64,299 B / 纯 LF / `# tests 66 / # pass 66 / # fail 0` |
| `dist/dsh-plugin-mcn-suite-0.5.0.tgz` | **2,017,667 B** / 324 条目 / md5 **`5cbccac1348e5dcb8d11bf400a000498`** / `npm shasum 4e025aba87a4c3edf6cb36e389c6f7ca1bcf873e` |
| `package.json` | `"version": "0.5.0"`(仅 `version` + `description` 两字段与 `0.4.0` 桶不同) |
| 共享层(47) | `…/bundled-plugins/dsh-plugin-mcn-suite/` `version 0.5.0`/`store.js` md5 同本机 |
---
## 三 🔴 ⑥ 未全绿的根因(两条 · 逐条取证 · **均非数据丢失**)
### 甲|计数 39 ≠ 34 = 本地 5 组重复 `aweme_id`(平台主键唯一 ⇒ 必然折叠)
```bash
node --experimental-sqlite /tmp/p35/diag-src.mjs # 退出码 2(尾段平台读撞会话过期;甲段已跑完)
```
```
LOCAL_TOTAL=39
DUPKEY aweme_id=7658997937279683855 rows=2 ids=[20,40] 正文全同=false 分析全同=true
DUPKEY aweme_id=7648947073131625905 rows=2 ids=[27,28] 正文全同=false 分析全同=true
DUPKEY aweme_id=7667477088350044069 rows=2 ids=[29,44] 正文全同=true 分析全同=true
DUPKEY aweme_id=7631176055600527729 rows=2 ids=[31,42] 正文全同=true 分析全同=true
DUPKEY aweme_id=7629803455813243043 rows=2 ids=[32,46] 正文全同=true 分析全同=true
DUP_SUMMARY 组数=5 冗余行数=5 组内完全一致=3/5 唯一键数=34
```
- 本地 `account_video_source` **39 行 = 34 个唯一 `aweme_id` + 5 个历史副本**;平台主键 `(user_id, aweme_id)` **唯一** ⇒ **平台只能有 34 行**。
- 5 组里 **3 组两个副本逐字全同**;**2 组**(`…9683855`、`…625905`)**只有正文差 2 字节**(分析列全同)。
- 🔴 **本表按 `id` 累积历史版本、读路径取最新**(`store.js` 注释与本地 SQL 一致:`… WHERE video_id=? OR (aweme_id IS NOT NULL AND aweme_id=?) ORDER BY id DESC LIMIT 1`;另有两处是 `IN` 集合索引/`COUNT(*)>0` 布尔)⇒ 旧副本**对读取不可见**;平台档下 `findVideoSources` 亦按主键取单行 ⇒ **行为等价**。
- ⚠️ **但这不等于可以自决删数据**:要与 A1 判据(`本地 n == 平台 n`)对齐,只能**删本地 5 行冗余副本**(**不可逆**)或**改判据**(须回规划棒)⇒ 见 §5-①(**待拍板**)。
### 乙|逐键内容 23/34 不等 = `jsonb` 键序重排 × `canonicalJsonText()` 保留序(**假阴性**)
```bash
node --experimental-sqlite /tmp/p35/diag-src3.mjs # 退出码 0(DIAG3_RC=0)
```
```
### H) 与序无关的深度相等(本地该键 **最新一行** vs 平台读回)
### H_SUMMARY 唯一键=34 正文+分析 序无关全等(最新一行)=32 序无关全等(任一本地副本)=34 仍有差异=0
### I) 交叉表:`gz1` 形态 vs 与序无关相等
FM aweme_id=7642280101581146850 PG_content[type=string gz1=true ] 序无关相等=true PG_analysis[type=null …]
FM aweme_id=7648968851887115563 PG_content[type=object gz1=false] 序无关相等=true PG_analysis[type=null …]
FM aweme_id=7628138993386034792 PG_content[type=object gz1=false] 序无关相等=true PG_analysis[type=object …]
```
```bash
# [真机 47] PG 地面真值:jsonb 值类型分布
SELECT jsonb_typeof(content_json), jsonb_typeof(analysis_json), count(*) FROM p_…_account_video_source GROUP BY 1,2;
```
```
PG_TYPE content=string analysis=string rows=6
PG_TYPE content=object analysis=null rows=3
PG_TYPE content=string analysis=null rows=3
PG_TYPE content=object analysis=object rows=20
PG_TYPE content=string analysis=object rows=2
```
- **`gz1=true`(落成 jsonb `string`)的行 ⇒ 逐键 `match`;`gz1=false`(落成 jsonb **对象**)的行 ⇒ 逐键 `match=false`**,**11 / 23 两侧完全对得上**(`jsonb_typeof=string` 的行数恰为 11)。
- 换成**与序无关**的深度比对 ⇒ **34/34 全等、仍有差异 = 0** ⇒ **平台侧内容与本地逐字段完全一致,不存在内容丢失**。
- 🔴 **机制**:PostgreSQL `jsonb` **会按"键长度+字母序"重排对象键**;而插件 `canonicalJsonText()` 是 `JSON.stringify`(**保留键序**)⇒ 只要值以 **jsonb 对象**形态落库,读回后 canon **必然**与本地文本不等。
- 🔴 **这是真缺陷(不是验收口径问题)**:`canonicalJsonText()` 还被
- 迁移幂等 / 稳定键生成、
- `imports.js` 的**「同视频+同内容 ⇒ 去重」**判据(`… AND content_json=?` 的平台档对应实现)、
- `imports.js` 的**预览判据**(`sr.ok` 分支)
共用 ⇒ **切到 `platform` 档后,同内容会被判成不同内容**(倾向于**重复写入**,R11 净变差)⇒ **必须在切档前修**(修法 = 递归按键排序后再序列化;**技术项 ⇒ 自决**)。
### 丙|附带纠正与澄清(防止后棒按错记开工)
| 项 | 旧记 | 实测纠正 |
|---|---|---|
| `account_video_source` 空 `aweme_id` 行数 | 执行单 §附录 C-2「**预计非零**」 | **0**(`badRows` 三表全 0) |
| `CONTENT … 同=12 异=27`(第 35 棒中途读数) | 疑似内容丢失 | **假阴性** —— 该读数的 `analysis` 长度打印串是 `String(obj)` ⇒ 恒为 `"[object Object]"`(15 字),**是探针自身的量测坑**,⛔ 后棒别拿它当结论(已在本件用 PG 地面真值 + 与序无关比对推翻) |
| 会话 TTL | — | `mksess.cjs` 的临时会话 **TTL = 600 s** ⇒ 长探针**必须现场新建**,否则处处 401(本次撞过一次,报 `not-registered / unauthorized`) |
---
## 四 未闭合项(逐条「现象 / 归属 / 下一步」)
| # | 现象 | 归属 | 下一步 |
|---|---|---|---|
| 1 | `account_video_source` `shadow match=false`(34 vs 39,5 组重复键) | **规划面判据 + 本地数据**(⛔ 非平台侧、⛔ 非插件逻辑) | **待拍板**(§5-①)⇒ 定后由下一棒执行 |
| 2 | `canonicalJsonText()` 键序敏感 ⇒ 平台档判据假阴性 | **插件侧**(`store.js`) | **技术项 · 自决** ⇒ 下一棒修 + 出包 `0.5.1` + 重投 + 重取影子读数 |
| 3 | ⑦ 切档 + 重启验收**整条未执行**(47 实例 + 106 合并窗口) | 本棒(**有意停住**) | 下一棒(窗口**已获用户批准**,见 §0 21:33 行) |
| 4 | A2「实例内」那一半未达成 · A5 未达成 | 同 3 | 同 3 |
| 5 | 106 共享层仍为旧版(两机不一致) | 部署面 | 下一棒随重启窗口同步 |
| 6 | `hot_accounts` / `account_videos` 的 `local_id` **回填已到位**,但「映射改优先平台」未做 | 插件侧(第 31/32 棒遗留 ㈦ 的部分缓解) | 下一棒(`restoreLocalId` 已让 `row.id` 在平台档可用) |
| 7 | 插件包源码**仍未纳入版本管理**(`??` 未跟踪) | **用户拍板项 ㈣** | **待拍板**(§5-②) |
| 8 | 共享锁目录曾出现**非本线遗留物**(`.exec-lock/OWNER = __st_other_1879`;`.locks/修域锁释放缺陷-…`) | 他人 lane | 按 **R9** ⛔ 不删锁、不接管;本棒收口时 `.exec-lock` **已自然消失**(本会话已复核) |
---
## 五 待拍板(**自包含** · 一轮一问 · 每条带优缺点 · ⛔ 不在接续棒内自决)
> ✅ **两条均已于 2026-09-25 22:44 由用户拍板**(原提问与优缺点保留在下方,供追溯):
> - **① ⇒ A 案**:**删掉本地那 5 条旧副本**(不可逆 · 已授权)。执行要求(已写入第 36 棒):先原样导出留档(`tmp/p36/`)+ 打印清单 → 再删 → 删后本地 `account_video_source` 应 = **34 行**;⛔ 只删行、⛔ 绝不删本地库文件 `mcn-plugin.db`、⛔ 不删 `db.js`、⛔ 不重建库。
> - **② ⇒ B 案**:**不纳入版本管理**,改为定期打压缩包备份到固定目录。**已由第 35 棒会话落地**:固定目录 `E:/dsh-backups/plugin-src/`(`backup.sh` + 基线包 `mcn-suite-src-20260925-2246-v0.5.0.tgz`/**6,065,179 B**/**330 文件**/md5 `247dd1456597e5496a695f4e91d695dc`/内容摘要 `9c38cdb4…`)+ 每周自动备份 automation **`1e3c9070-a75d-4ae4-b81e-06e2eb20a5b4`**(周日 03:30 · 内容摘要去重「未变则跳」· 保留最近 26 份)。⚠️ **该机制只防误删,不防同一块盘损坏**(要防盘坏需再复制一份到别的机器/盘)。
> ⇒ **第 36 棒起按已定执行,⛔ 不再重问这两条。**
### ① 平台上「一个视频只能存一条来源记录」,本地却存了 5 组重复(同一视频两条)。要不要**删掉本地那 5 条旧副本**,好让两边数量对齐?
**为什么得由你定**:这一步要么**动实例里的真实数据(删 5 行,不可逆)**,要么**放宽这条验收判据**。两条路都对「这台机器上那位用户的资料」有实际影响,且**没有客观更优的一方** ⇒ 属边界外。**现在不决定也可以** —— 现象已具名,平台侧内容 **34/34 与本地逐字段一致**(已实测),**没有数据丢失**;只是「切换开关」这一步卡住不动,第三批就到不了终点。
- **A 案|删本地 5 条旧副本**(保留每条视频最新那一份)
优点:一次做完、验收判据如实达成;对读用户**无可见影响**(读路径本来就只取最新那一份,多出来的旧副本从来读不到)。
缺点:**不可逆**(只能靠备份回滚);其中 2 组的旧副本正文与最新副本**差 2 个字符**,删了就没了。
- **B 案|不删数据,改判据为「同一视频只算一次 + 逐条内容比对」**
优点:**零数据变更**,风险最低。
缺点:改的是规划棒定死的判据(`本地 n == 平台 n`),按规矩要**回规划棒**再改一次单子;且今后每台机器都得靠这条新口径解释。
- **C 案|保持现状不动**(继续停在 `local` 档)
优点:今天什么风险都不担。
缺点:第三批**永远到不了终点**,本地库与平台库长期并存、两处结论会越走越远。
**我的倾向**:**A 案**(可推翻)—— 5 条旧副本对读取不可见、删除前有备份 + 我会先出逐条清单;⚠️ 但它**不可逆**,所以**必须你点头**我才动。
### ② 这次改的插件源码(近一个月、十几个文件)要不要纳入版本管理?
**为什么得由你定**:这决定「以后回滚靠什么」—— 是**靠 git 记录**,还是**只能靠备份包**。属影响面偏好,不是技术优劣。
- **A 案|现在就纳入**(把该目录加进仓库并提交)
优点:改动有版本留痕、可逐笔回滚、后续多会话协作可对账。
缺点:要动仓库与提交动作;该包目前形态(大文件+出包产物)需先定「哪些进、哪些不进」。
- **B 案|暂不纳入**(继续只靠 sha256 + 交付件留痕)
优点:不动仓库,零额外动作。
缺点:**近一个月改动零版本留痕**;一旦误删/误改,只能靠备份整包回退,**无法定位到某一笔改动**。
**我的倾向**:**A 案**(可推翻)—— 本线已连续 8 棒在同一批文件上改,留痕收益已超过成本。
> ⚠️ 其余存量待拍板项(㈠ 47 实例重启窗口 ✅ 已于 21:33 批准;㈡ 106 重启窗口 ✅ 已批准「可以合并那是 worker」;㈢ `json` 列是否改声明 —— **本批不需要**,留档)**本棒一项未新增、未改动**。
---
## 六 卫生声明
- ⛔ **未改** `D:/github/dsh_shenxian/src/**`(平台侧**零改动**)· ⛔ 未改 `im.ts` · ⛔ 未改装配协议。
- ⛔ **未删** 本地旧库 `mcn-plugin.db`(sha256 + mtime 双证未变)· ⛔ 未删 `db.js` · ⛔ **平台库未删未重建**。
- ⛔ **未重启任何服务**(47 实例 / 106 全未触碰)· ⛔ **未逐表切档**。
- ⛔ **未 commit / 未 push**(见 §5-②)。
- ⛔ **未改** `D:/dshworkspace/plugin_package/dsh-plugin-mcn-suite/` 以外任何工作区文件(本棒另动的三处均为**本线自有文档**:本工作区交付物 + 本线入口 + 文档库 `05-交接单/README.md`)。
- ✅ **R4 卫生**:临时会话已清理(`DELETE FROM sessions WHERE user_agent='poc-curl2'` ⇒ `SESSIONS_BEFORE=7 / SESSIONS_DELETED=7 / SESSIONS_AFTER=0`)。
- ✅ **真机产物已清**:`/tmp/p35*`、`/tmp/mksess-guest.cjs` 全删(`TMP_P35_CLEAN`);本机证据与探针脚本留在 `$WS/tmp/p35/`(tmp 保留期 7 天)。
- ✅ **R10 修复**:实例 home 下 `root:root` 属主配置已 chown 回实例 uid(`ROOT_OWNED_FINAL=0`)。
### 域锁
- 抢锁:`bash $DOC/07-scripts/handoff-guard.sh --claim-exec "p35-exec-plugin-dataline" --domains <9 键>` ⇒ `rc=0`;复读断言:
```
OWNER: p35-exec-plugin-dataline / 开始:09-25 21:47 / ME:p35-exec-plugin-dataline
DOMAINS: mcn/store.js, dsh-plugin-mcn-suite/package.json, tests/mcn-store.test.mjs,
dsh-plugin-mcn-suite/dist, mcn/index.js,
aliyun-dsh-server/接续入口_插件投放与分库线_20260922.md,
aliyun-dsh-server/交付物, dsh-server-docs/05-交接单, aliyun-dsh-server/.workbuddy
```
- 开工门禁如实记账:`preflight-lock.sh` 对本棒 **8 个目标路径全判【D】未归类** ⇒ 按字面 `rc=1`;依第 26–34 棒**既有惯例**开工并报告,⛔ **未改那个跨库脚本**。
- 释放:**22:38 已带会话名释放**(`ME="p35-exec-plugin-dataline" … --release-exec` ⇒ `RC=0`;复核 `.locks/p35-exec-plugin-dataline`/`.exec-lock` 均已移除、`find -name OWNER` 零命中、`.locks/` 仅余空容器 `.migrations/`)。随后**为落两条拍板 22:44 二次抢锁**(3 域:入口/交付物/`.workbuddy`,`rc=0`,OWNER 归属已断言),**改完即再次带会话名释放**。
- 备份(拍板②):**固定目录 `E:/dsh-backups/plugin-src/`** 已建(脚本 `backup.sh` + `pkgs/` + `backup.log` + `.last.sha256`);基线包 `mcn-suite-src-20260925-2246-v0.5.0.tgz` **6,065,179 B**/**330 文件**/md5 `247dd1456597e5496a695f4e91d695dc`;幂等实测:二次运行输出 `RESULT=SKIP_UNCHANGED` 且包数仍为 1(未新增)。每周日 03:30 自动执行(automation `1e3c9070-a75d-4ae4-b81e-06e2eb20a5b4`)。⚠️ 同盘备份**只防误删,不防盘坏**。
---
## 七 下一棒(第 36 棒)范围(已按上表登记)
> ✅ **本节已于 2026-09-25 22:44 按用户拍板改写**(原为条件式;现两条拍板到手 ⇒ **条件步转无条件**,范围见入口 §2「⏭️ 本轮动作(第 36 棒)」)。
1. 🔴 **修 `canonicalJsonText()` 键序敏感性**(技术项 · 自决;修法 = 递归按键排序后序列化)+ 补一条单测(**jsonb 键序被打乱后仍判相等**);`node --test tests/mcn-store.test.mjs` 必须全过。
2. 出包 **`0.5.1`** → 上池 →(结构迁移到 `state=ready`)→ 发布共享层(记条目 / 字节 / md5)。
3. **删本地 5 条旧副本**(执行拍板①):**先**原样导出留档(`tmp/p36/`)+ 打印清单(表名/主键值/保留哪份/删哪份)→ **再**删 → 删后本地 `account_video_source` 应 = **34 行**。⛔ 只删行、⛔ 绝不删本地库文件 `mcn-plugin.db`、⛔ 不删 `db.js`、⛔ 不重建库。
4. 重取 `account_video_source` 影子读数 ⇒ 应转 **两面对齐 34 = 34 + 内容判据全绿**。
5. **闭环 ⑥ + 执行 ⑦**:三表逐表切 `platform` → **47 实例 + 106 合并同一次窗口**重启(窗口已批)→ 三表 E2E(A2 实例内面)+ 真机读数验 A5/A8。
6. 收口:释放域锁(`ME=`)· automation 置 PAUSED + 排期(⑤ 完成 ⇒ 不登记下一棒)· 入口 `§0`/`§2` · `05-交接单/README.md §一` · 当日日志 · 交付件(A1–A9 附命令 + 输出 + 退出码)。⛔ 仍不 commit / 不 push。
**变更记录**:2026-09-25 22:5x 第 35 棒(执行棒 · 会话 `p35-exec-plugin-dataline`)落盘;纯 LF。