# 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 root@47.77.182.89`;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] 读该用户 /.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 修复(必须记)**:`/.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。