范围 = 本线(插件投放与分库线)产物 + 记忆类,共 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/规则与载体/、归档/、接续包_*。
25 KiB
MCN 数据面接入 · 阶段二 · A 案落地与取证(第 33 棒 · 执行棒)
执行会话:插件投放与分库线-第 33 棒(2026-09-25 19:5x–20:2x)|依据:入口 §0「用户拍板 —— A 案」行 + 唯一执行单
05-交接单/插件投放与分库线-04MCN数据面接入改造-阶段二第三批.md上游拍板:A 案 = ① 给「账号」「视频」两类记录在平台侧补稳定主标识 ②account_video_source正文列声明上限 30 KB ⇒ ≥100 KB 变量:PKG=D:/dshworkspace/plugin_package/dsh-plugin-mcn-suite|WS=E:/ProgramData/AIProject/aliyun-dsh-server|DOC=D:/github/dsh_shenxian/dsh-server-docs
一 结论
A 案的两项已在「声明面 + 插件代码面 + 单测」全部落地,并在 47 真机取到硬证据;🔴 实测推翻该案的「只能删库重建」代价 —— 本案不需要删库,只需两条 ADD COLUMN(零数据损失、无需重灌)。
同时拿到三条必须让用户知道的事实:
- 🔴 「删库重建」不必要(原拍板代价可省)。真机预演:新声明 ⇒
forbidden=0、items=2×add_column;maxBytes改动零 diff 项(平台diff.ts只比类型、不比maxBytes)。⇒ 本案落地 = 一条迁移(加列),不是删库 + 10~20 分钟重灌。 - 🔴 A 案后半段有效但不彻底:
account_video_source可上台行数 21 → 27(+6);剩 12 行仍被「平台单行 64 KiB 上限」拦住 —— 瓶颈已从「列上限」转移为「平台配额maxRowBytes默认 64 KiB,且plugin-data.ts构造时未传quota⇒ 无配置口」。⇒ 这 12 行要么永远留在本地,要么动平台侧(⛔ 本棒禁改src/**)。 - ⏸️ 本棒未做出包 / 上池 / 加列迁移 / 切档(理由见 §六)—— 这三步都要求动生产(门户候选池 + 平台库),且第 1 条已改变原授权所设想的动作形态(「重建」⇒「加列」)⇒ 依 R8 与「影响面」判据列为待拍板,⛔ 未自决。
未达成项:S3 剩 1 处、S4、S5、S6、S7 全部未开工(逐条见 §七)|A2/A5/A8 需生产窗口或未投放 ⇒ 未达成|A1 的新口径读数已取(见 §四),但平台侧只有 27 行(存量 21 行上台 + 本案新增 6 行的可行性已证,尚未执行)。
二 A 案两项落在哪(逐项 · 可复核)
2.1 声明面 dsh.data.yaml
| # | 改动 | 精确内容 |
|---|---|---|
| ① | hot_accounts 加列 |
- { name: local_id, type: text }(可空 —— 平台禁 notNull 无 default;⛔ 不作主键,首列仍是 account_name) |
| ② | account_videos 加列 |
- { name: local_id, type: text }(同上) |
| ③ | account_video_source.content_json |
maxBytes: 30720 ⇒ 128000 |
| ④ | account_video_source.analysis_json |
maxBytes: 30720 ⇒ 128000 |
⚠️ 上限取 128000 而非 100000:实测最大正文 94,889 B,留 ~35% 余量。
⚠️ store.js 的 COLUMN_MAX_BYTES 同步改同值(⛔ 不改会让插件侧仍按 30 KB 拒收而平台已放行 ⇒ 两侧口径分叉)。其它任何表的 maxBytes 一列未动(video_analysis / account_persona 仍 49152)—— 防顺手扩大。
🔴 补正(20:2x):初稿声称 ③④ 已落 —— 复核时发现 dsh.data.yaml 实际仍是 maxBytes: 30720(① ② 两处 local_id 加列是真落了)。已按 A 案原授权补齐为 128000(+ 3 行说明注释),并用 PyYAML 实测解析通过(11 表 / json 列 maxBytes 全为整数),指纹见 §九。⚠️ 本条属「把已拍板的事做实」,未引入任何新决策;local_id 加列与代码面(§2.2)当轮即已落盘,不受影响。
2.2 代码面(store.js · 唯一落点)
| 机制 | 落点 | 口径 |
|---|---|---|
| 取号口径(唯一) | 新增 localIdValueOf(table, row) |
显式 local_id 优先,否则本地 id 折算 ⇒ 纯函数、无副作用,折算与回填共用它 |
| 写侧折算 | normalizeRow() 循环之后统一折算 |
本地自增 id ⇒ local_id(String());id 仍进 dropped(⛔ 不写平台);coerced 记 id->local_id。⚠️ 放在循环后 ⇒ 行里显式给的 local_id 天然优先 |
| 读侧还原 | 新增 restoreLocalId(table, row),由 rowsOf(result, table) 统一调用 |
local_id ⇒ 数字 id;已有 id 不覆盖;非数字原样透出(⛔ 不丢不猜) |
| 新行取号 | insertRow() 前挂 attachLocalId() ⇒ allocLocalId() |
懒初始化 = 扫该表取 max(local_id)+1;进程内单调、号只增不减(删除不回收 ⇒ ⛔ 不会把"新人"认成"老人") |
| 列不存在 ⇒ 具名降级 | localIdSupported()(取一行看键,进程内缓存) |
声明已升级但库未迁移 ⇒ 返回 null ⇒ 不写该列、⛔ 不中断写入 |
| 存量自愈回填 | upsertByPk()「已存在」分支 ⇒ backfillLocalId() |
🔴 只补空、⛔ 不覆盖非空。理由:存量行是加列之前灌进去的 ⇒ 重跑迁移只会 skip、永远补不上 local_id ⇒ 前端 row.id 会一直缺席 |
| 拆表 | splitHotAccount() + HOT_MAIN_COLUMNS |
id ⇒ 主表 local_id(⛔ 不能丢 —— 丢了前端账号详情没 id);扩展表 ⛔ 不带该列(它没声明) |
为什么这样一来调用方零改动:normalizeRow / rowsOf 是所有平台档读写的唯一必经之路(insertRow / patchRow / upsertByPk / writeRows / findRows / locate / findVideos 全走它)⇒ 折算与还原在一处生效,⛔ 不需要去 89 处调用点逐个改。
2.3 迁移工具 tools/migrate-local-to-platform.mjs
toPlatformRow()保留本地id原值(由normalizeRow折算 ⇒ 存量行的local_id= 本地 id,不吃号);- 新增
backfilled计数(表级 +parts[]+SUMMARY),CLI 每行多打印 「回填承载列 n」。
三 判据表 A1–A9(命令 + 输出原文 + 退出码)
| # | 判据 | 命令 | 输出原文(关键行) | rc |
|---|---|---|---|---|
| A1 | 存量迁移一致性 | node --experimental-sqlite /tmp/p33/dry33.mjs(47 真机 · 只读 dry-run) |
DRY [account_video_source] 本地=39 计划=27 坏行=12 重复键=5 清洗列=0DRY [account_videos] 本地=362 计划=362 坏行=0 重复键=0 清洗列=362(清洗列集合 ["id->local_id"])DRY [hot_accounts] 本地=5 计划=5 坏行=0 重复键=0 清洗列=5(同上) |
0 |
| A1-附 | 重复键逐组具名(⛔ 不许"最后一行赢") | 同上 | DUP {"pk":"7658997937279683855","localIds":[20,40]}DUP {"pk":"7648947073131625905","localIds":[27,28]}DUP {"pk":"7667477088350044069","localIds":[29,44]}DUP {"pk":"7631176055600527729","localIds":[31,42]}DUP {"pk":"7629803455813243043","localIds":[32,46]} |
0 |
| A2 | 三表读写 E2E | — | ⛔ 未达成:实例内读数需 47 实例重启(待拍板㈠);本棒⛔ 不承诺窗口依赖项 | — |
| A3 | 结构改写面处置完毕 | ① grep -rniE "\bJOIN\b" lib | grep -v "\.join(" ② grep -rn "LIMIT ? OFFSET ?" lib ③ grep -rnE "ROW_NUMBER|OVER\s*\(" lib |
① 148(全库粗扫,含注释与非本批表)② 8(data.js 3/index.js 3/store.js 1/workshop.js 1)③ 9(index.js 6/store.js 3)🔴 涉及本批三张表的处置数 = 0(S4/S5 未开工 ⇒ 见 §七) |
0 |
| A4 | 表名对齐 | grep -rE "account_video_analysis" . | grep -v /node_modules/ | wc -l;grep -c "hot_account_ext" lib/host/mcn/store.js |
前者 0 ✅;后者 6 ✅(>0) | 0 |
| A5 | 三表已切 platform 档 |
— | ⏸️ 未达成(未切档;且 local_id 列未就位 ⇒ 切了会让新行没 id) |
— |
| A6 | 移动端五条 | 08 §4-补 ④ |
免跑(本棒改动恰 4 件:dsh.data.yaml/lib/host/mcn/store.js/tools/migrate-local-to-platform.mjs/tests/mcn-store.test.mjs;lib/client.js 零改动 ⇒ 不动 UI) |
— |
| A7 | 卫生(逐条) | 见 §九 | 全过 | 0 |
| A8 | 两机一致 | 两机共享层 package.json version + md5 |
⏸️ 未达成:本棒未出包、未投放 ⇒ 代码面/运行面都还是旧的(47 现役 = 0.4.0,tgz 1,962,024 B · 09-25 13:28)⇒ ⛔ 不得写"一致" |
— |
| A9 | 红线零新增 | grep -rEn "CREATE TABLE|ALTER TABLE|DROP TABLE|better-sqlite3|from 'pg'|require\('pg'\)" . | grep -v /node_modules/ | wc -l |
23 ✅(与第 27/28/29/31/32 棒同值) | 0 |
| A-单测 | 离线判据 | node --test tests/mcn-store.test.mjs |
# tests 58 / # pass 58 / # fail 0(原 51 + 本棒新增 7:㊺–㊿ + 51) |
0 |
四 真机读数(47 · 只读 · 零写请求)
4.1 声明迁移预演(plan33.mjs · readCurrentSchema + diffDecl)
### 1) 库现状(只读)
EXISTS=true schemaVersion=1 tables=12
--- OLD origin=yaml-file level=ok findings=[]
dbName=dshs_pl_dsh_plugin_mcn_suite tables=11 schemaVersion=1
PLAN items=0 forbidden=0 summary={"tables":0,"columns":0,"indexes":0} unchanged={"tables":11,"columns":96}
--- NEW origin=yaml-file level=ok findings=[]
DECL hot_accounts cols=14 ← 13 ⇒ 14(+local_id)
DECL account_videos cols=17 ← 16 ⇒ 17(+local_id)
PLAN items=2 forbidden=0 summary={"tables":0,"columns":2,"indexes":0} unchanged={"tables":11,"columns":96}
ITEM add_column p_dsh_plugin_mcn_suite_hot_accounts.local_id | ALTER TABLE "…hot_accounts" ADD COLUMN IF NOT EXISTS "local_id" TEXT;
ITEM add_column p_dsh_plugin_mcn_suite_account_videos.local_id | ALTER TABLE "…account_videos" ADD COLUMN IF NOT EXISTS "local_id" TEXT;
🔴 读数含义(1–3 为预演结论,4 为 20:2x 复核补正):
- OLD 与库现值完全一致(
items=0/unchanged 11 表 96 列)⇒ 现役 0.4.0 声明与真机库是同步的 ⇒ 预演基线可信; - NEW 只多两条
add_column、forbidden=0⇒ 状态 =drift(可迁移),⛔ 不是blocked⇒ 不需要删库重建; maxBytes改动零 diff 项(unchanged.columns仍 96)⇒ 与源码级判定一致(diff.ts:316-334只比normalizeCurrentType(c.dataType)vsd.type,完全不读maxBytes)。- 🔴 补正(20:2x 复核得出 · 与本棒初稿口径不同):平台的列级
maxBytes全程没有任何强制点 ——schema.ts:500-515只做「必须是正整数」+「json列必填」的声明期校验;runtime.ts写入期查的是行级quota.maxRowBytes = 64 KiB(:316-317)+ 行数配额(:319checkRowQuota),从不按列读maxBytes(全仓grep -rn 'maxBytes' src/db/plugin-data/除schema.ts外零命中)。 ⇒ 列上限的唯一实际把关人 = 插件侧store.js的COLUMN_MAX_BYTES。故 A 案 ② 的功能效果由代码面落地;声明面改的是「口径一致 + 文档」,不是功能开关。这也解释了为什么声明面漏改不会让 A 案失效(但会让两侧口径分叉,属必须补齐的自洽项)。
4.2 A 案后半段效果现算(account_video_source 逐行 · 39 行)
| 行号 | localId | content_json | analysis_json | 整行(新口径) | ①原 30 KB 列上限 | ②新 128 KB 列上限 | ③平台单行 64 KiB |
|---|---|---|---|---|---|---|---|
| 0 | 1 | 78,682 | 4 | 79,867 | ❌ 超 | 过 | ❌ 超 |
| 2 | 3 | 73,720 | 4 | 74,658 | ❌ 超 | 过 | ❌ 超 |
| 4 | 5 | 5,105 | 32,436 | 38,014 | ❌ 超 | 过 | 过 |
| 5 | 7 | 78,153 | 4 | 79,209 | ❌ 超 | 过 | ❌ 超 |
| 9 | 13 | 18,305 | 56,742 | 75,542 | ❌ 超 | 过 | ❌ 超 |
| 10 | 14 | 8,666 | 57,453 | 66,717 | ❌ 超 | 过 | ❌ 超 |
| 11 | 15 | 7,014 | 44,509 | 52,044 | ❌ 超 | 过 | 过 |
| 14 | 21 | 25,361 | 94,889 | 120,641 | ❌ 超 | 过 | ❌ 超 |
| 15 | 22 | 13,502 | 55,847 | 69,708 | ❌ 超 | 过 | ❌ 超 |
| 21 | 29 | 10,219 | 59,898 | 70,457 | ❌ 超 | 过 | ❌ 超 |
| 22 | 30 | 13,973 | 59,898 | 74,217 | ❌ 超 | 过 | ❌ 超 |
| 24 | 32 | 7,627 | 33,863 | 41,812 | ❌ 超 | 过 | 过 |
| 26 | 34 | 8,701 | 34,732 | 43,760 | ❌ 超 | 过 | 过 |
| 30 | 38 | 10,542 | 59,698 | 70,849 | ❌ 超 | 过 | ❌ 超 |
| 31 | 39 | 9,175 | 63,803 | 73,602 | ❌ 超 | 过 | ❌ 超 |
| 33 | 41 | 7,339 | 32,221 | 40,099 | ❌ 超 | 过 | 过 |
| 36 | 44 | 10,223 | 59,898 | 70,465 | ❌ 超 | 过 | ❌ 超 |
| 38 | 46 | 7,627 | 33,863 | 41,812 | ❌ 超 | 过 | 过 |
小计:总 39|过原 30 KB 上限 21|过新 128 KB 列上限 39|列上限+单行上限都过 27
⇒ 🔴 A 案后半段把可上台行数从 21 提到 27(+6 行);剩 12 行的瓶颈不在列上限(全过 128 KB),而在平台单行 64 KiB:runtime.ts:66 maxRowBytes = 64*1024(DEFAULT_QUOTA)⇒ 而 src/web/routes/plugin-data.ts:86 构造 PluginDataRegistry 时未传 quota ⇒ 恒取默认、无配置口(47 上 /etc/dshs.env 与 drop-in 均无 quota 项,实测 grep 零命中)。
4.3 新口径 dry-run(三表 · 零平台写请求)
DRY_WRITE_CALLS=0(探针的假 fetch 会抛异常 ⇒ 若发请求会立刻报错)
⇒ 三表 dry-run 一个平台请求都没发(apply:false 分支在 writeRows 之前 return)✅
五 「不需要删库重建」的判定(本棒最重要的正向结论)
| 判据面 | 依据 | 结论 |
|---|---|---|
| 源码 | diff.ts:49 PlanKind 含 add_column;:52 ForbiddenKind = drop_column / alter_type / rename / notnull_no_default / drop_table |
加列 = 合法计划项(可自动 ALTER TABLE … ADD COLUMN IF NOT EXISTS);改类型才是禁令 |
| 源码 | diff.ts:316-334 只比「归一化类型」,不读 maxBytes |
改上限 ⇒ 零 DDL 差异 |
| 真机 | §4.1 预演:items=2×add_column · forbidden=0 · status=drift |
可迁移,⛔ 不 blocked |
⇒ 原拍板告知的代价(不可逆删库 + 数据不可用 10~20 分钟 + 重灌)在本案不发生。本案落地收敛为:
出包(含新声明/新代码) → 上传门户 ⇒ 检测(预演) ⇒ 执行迁移(= 2 条 ADD COLUMN,秒级)
→ 重跑存量迁移(幂等 skip + **回填 local_id**)
风险面(替代原代价,须一并知道):上传后到迁移完成之间的分钟级窗口内,若该用户「停用→重新启用」插件,会因声明 ≠ 库现值(drift)而启用被拒。⇒ 三步必须紧邻执行;回滚 = 重新上传旧包(business-plugins/dsh-plugin-mcn-suite.tgz 现有 0.4.0,1,962,024 B)+ ALTER TABLE … DROP COLUMN local_id(或直接接受多一列空列,无害)。
六 R8 自律:清单 + 备份 + 影响面(本棒只出件,未执行)
6.1 动作清单(有序)
| # | 动作 | 面 | 可逆性 |
|---|---|---|---|
| 1 | PKG 出包(版本号自决,⚠️ 建议临时版本号避免与收口的 0.5.0 混淆) |
本机产物 | 可逆(删产物) |
| 2 | 上传门户候选池(POST /api/plugins/business,admin 临时会话,R4 用完即删) |
生产池 | 可逆(重传旧包) |
| 3 | 检测/预演(期望 add_column ×2、forbidden=0) |
只读 | — |
| 4 | 执行迁移(2 条 ADD COLUMN) |
生产库 | 可逆(列可留空/可删) |
| 5 | 重跑存量迁移(幂等 skip + 回填 local_id) |
生产库 | 可逆(幂等) |
| 6 | 复验:account_videos 362 行 / hot_accounts 5 行每行 local_id 非空;account_video_source 27 行上台 |
只读 | — |
6.2 备份现状
| 面 | 现状 |
|---|---|
旧库 mcn-plugin.db |
在位(第 27 棒备份 cce6d1cd-…-mcn-plugin-db-20260925-141436.tar.gz,809,732 B;sha256 b37f8aab51a90f74…)⇒ ⛔ 本批不重做备份、⛔ 不删旧库 |
平台库 dshs_pl_dsh_plugin_mcn_suite |
本棒不删、不重建(§五)⇒ 只需"加列",无数据损失面 |
| 现役插件包 | /var/lib/dshs/business-plugins/dsh-plugin-mcn-suite.tgz(0.4.0 · 1,962,024 B · 09-25 13:28)+ 共享层 bundled-plugins/dsh-plugin-mcn-suite/ ⇒ 回滚素材 |
6.3 影响面
| 谁受影响 | 多久 | 代价 |
|---|---|---|
47 上该插件用户(cce6d1cd-…) |
分钟级窗口(上传 → 迁移完成之间) | 该窗口内「停用→重新启用」会被拒(drift);已在运行的实例不受影响(加载的是共享层 bundle,非新包);计划任务/其它功能不受影响 |
| 其它用户 / 其它插件 | 0 | 加列只作用于 dshs_pl_dsh_plugin_mcn_suite 一个库 |
| 106 | 0 | 本步不涉及(106 属 S9–S11 收口) |
七 未闭合项(⛔ 逐条不宣称通过)
| # | 项 | 现象 | 归属 | 下一步 |
|---|---|---|---|---|
| ㈠ | S3 剩 1 处 | index.js 那条三表混写 SQL(account_videos + account_video_source + SUM() OVER)未改 |
插件侧 | 先改它 → 再 S4/S5(04 单 §五 已定序) |
| ㈡ | S4 未开工 | account_videos 全量切换(游标 2 处 / 窗口函数 2 处 / IN 逐主键 4 处 / account_id 类型对齐)一处未改 |
插件侧 | 落点 = 现算 89 处(data.js 9/imports.js 36/index.js 30/video-tools.js 1/workshop.js 13)⇒ 读数 tmp/p33/batch3-live-33.txt |
| ㈢ | S5 未开工 | hot_accounts + hot_account_ext 拆表切换(4 写点 + 11 JOIN)一处未改;首件事仍是核实 5 处写点能否都拿到 account_name |
插件侧 | 同上 |
| ㈣ | local_id 列未就位 |
真机库仍是 13/16 列(无承载列)⇒ 即使接线完成也不能切档(切了新行没 id ⇒ 前端 6 入口失效) | 部署面 | §六 的 1–5 步(待拍板) |
| ㈤ | account_video_source 12 行仍上不去 |
瓶颈 = 平台单行 64 KiB(DEFAULT_QUOTA 未覆盖、无配置口);⚠️ 本棒⛔ 不改 src/** |
平台侧 | 待拍板(§八-②) |
| ㈥ | A2 / A5 / A8 未达成 | A2 需 47 实例重启窗口|A5 未切档|A8 未出包未投放 | 部署面 | 待拍板(§八-①) |
| ㈦ | D16 映射新鲜度缺口(第 31/32 棒遗留) | hotAccountMetaMap / videoLocalIdMap 只能来自本地 ⇒ 切档后新建账号会缺席 |
插件侧 | 🔴 本棒部分缓解:local_id 就位后,映射可直接从平台行取���restoreLocalId 已让 row.id 在平台档可用)⇒ 下一棒可据此把映射改为"优先平台" |
| ㈧ | 第 30 棒读数文件被覆盖(如实记账) | 本棒用 tmp/p30/scan-batch3-live.py 现算第三批落点时,该脚本写死落盘到 tmp/p30/batch3-live.txt ⇒ 第 30 棒那份被现算结果覆盖 |
本机 | 已把现算副本留 tmp/p33/batch3-live-33.txt;⚠️ 脚本落盘路径不属本棒域锁(tmp/ 不入库)⇒ ⛔ 未改脚本 |
八 待拍板(两项 · 一轮一问 · 逐条自包含)
⛔ 本棒未登记下一棒(用户 2026-09-17 明令:要用户拍板的等拍了再新建接续会话)。
① 要不要现在动生产,把 A 案的两项真正落到 47(出包 → 上池 → 加列迁移 → 回填)? 为什么需要你定:这一步会改动生产环境——具体是:把插件新版本传到门户(替换候选池里的包),随后给该插件在数据库里加两列。影响面:该插件的使用者(47 上那一个用户)在"上传完成到加列完成"的几分钟窗口内,如果停用再启用插件会被系统拒绝;已经在运行的实例不受影响。数据不会丢(不是删库,只是加列),旧数据备份已经在位。
- A 案:现在就做(优点:本棒付出的代码/声明/取证立刻变成可用状态,下一棒可直接接线+切档;缺点:需要几分钟的生产窗口,且窗口内该用户若正好停用再启用会被拒)。
- B 案:等下一棒把 S3/S4/S5 接线也做完,再一次性动生产(优点:只动一次生产、窗口只开一次;缺点:接线期间写好的平台档代码暂时无法在真机验证,"有没有接对"要靠离线单测兜)。
- 我的倾向:B 案(可推翻)。理由:切档本来就必须等接线全部完成(一处未改完就切 = 写到没人读的地方),所以"现在就迁移"并不能提前拿到任何真机收益,却要单独开一次生产窗口。
② account_video_source 那 12 行超 64 KiB 的记录,怎么处置?
为什么需要你定:这 12 行是用户真实数据(AI 解析正文较大)。现在它们上传不到平台(平台单行上限 64 KiB 是硬编码默认值,且平台侧构造时没有留出可配置的接口 ⇒ 要提高必须改平台程序)。不处置的话,这 12 行永远是"平台档下读不到"的状态——切档后这 12 个视频的解析结果会看不见。
- A 案:接受现状(优点:不动平台程序、不影响其它插件;缺点:这 12 个视频的解析结果切档后读不到,属功能净缺失)。
- B 案:改平台程序,把单行上限提上去(优点:12 行也能上台、彻底;缺点:改动的是平台公共组件,影响所有插件,需要单独评估与测试,且与本棒"不改平台"的约束冲突 ⇒ 必须另立单子)。
- C 案:插件侧把这 12 行的大正文拆成多行存放(优点:不动平台、不丢数据;缺点:要改插件表结构 + 读取逻辑,属新增改造面)。
- 我的倾向:A 案(可推翻)。理由:先让 27/39 行可用,把 12 行如实记为"未上台",等平台侧配额这件事单独拍板;⛔ 不拿"顺手改平台"当默认。
九 卫生声明(A7 逐条)
⛔ 未改 D:/github/dsh_shenxian/src/**(只读 grep + 只读 import)|⛔ 未改 im.ts|⛔ 未改 D:/dshworkspace 以外任何别的工作区文件|⛔ 未重启任何服务|⛔ 未投放任何版本|⛔ 未出包|⛔ 未 commit·push|⛔ 未删任何库(旧库 mcn-plugin.db 在位)|⛔ 未删 db.js|⛔ 未改跨库脚本(handoff-guard.sh / preflight-lock.sh 一字未动)|✅ 真机探针 /tmp/p33/ 已整体删除(实测 ls 报 No such file or directory)
改动面恰 4 件(全纯 LF · CR=0,逐字节验过);⚠️ dsh.data.yaml 是 20:2x 补正之后的版本(补正内容与理由见 §2.1 的「补正」段),其余三件与 20:1x 收口时逐字节一致:
| 文件 | sha256(前 16) | 字节 | CR/LF |
|---|---|---|---|
dsh.data.yaml |
d6627f1c647a8c27 |
8,351 | 0 / 183 |
lib/host/mcn/store.js |
ed5f25741476f0d9 |
105,150 | 0 / 2,239 |
tools/migrate-local-to-platform.mjs |
060bbf855f624fdd |
12,886 | 0 / 254 |
tests/mcn-store.test.mjs |
befaef4aaae43e43 |
53,587 | 0 / 1,040 |
十 域锁
--claim-exec "p33-exec-plugin-dataline" --domains <11 键> ⇒ rc=0;复读 .locks/p33-exec-plugin-dataline/{OWNER,DOMAINS} 断言归属(开始 09-25 19:57;会话 ee21bf7d-0f0e-4c42-9abc-29e8e15bb774)。
释放(收口时):ME="p33-exec-plugin-dataline" bash $DOC/07-scripts/handoff-guard.sh --release-exec —— 🔴 必须带 ME= 前缀(不带 ⇒ ME 空 ⇒ 会删掉 .locks/* 下所有人的域锁,第 32 棒 09-25 19:5x 实测事故)。
第 1 步门禁如实记账:preflight-lock.sh 对本棒 11 个目标路径全判【D】未归类 ⇒ 按字面 rc=1;依第 26–32 棒既有惯例开工并报告,⛔ 未改那个跨库脚本。
🔴 补正周期(20:2x)再抢一次域锁:--claim-exec "p33-exec-plugin-dataline" --domains "dsh-plugin-mcn-suite/dsh.data.yaml" "aliyun-dsh-server/交付物" ⇒ rc=0;复读 DOMAINS(2 键)+ OWNER 首行 = 本人,断言归属通过(20:22)。
🔴 释放读数(如实记账 · ⛔ 未作任何人工处置):跑 ME="p33-exec-plugin-dataline" … --release-exec 输出 · 无可释放的锁(或非本人持有)(rc=0);实测 .locks/ 仅剩 .migrations(== 无任何域锁目录)、.exec-lock 不存在 ⇒ 本棒域锁均已不在。⚠️ 只读源码 handoff-guard.sh:249-271:$_who 非空时删除条件是 [ "$o" = "$_who" ](OWNER 首行相等)⇒ 带 ME= 的释放按定义不会碰别人的锁。⛔ 全程未删锁、未接管(R9)。
附 本棒探针与读数($WS/tmp/p33/)
| 文件 | 用途 |
|---|---|
plan33.mjs |
声明迁移预演(47 · 只读)⇒ §4.1 读数 |
dry33.mjs |
逐行字节现算 + 新口径 dry-run(47 · 只读)⇒ §4.2 / §4.3 |
new/{dsh.data.yaml,package.json} |
新声明夹具(parseDeclFromDir 要求 package.json 指路 ⇒ ⛔ 不自动探测 yaml) |
batch3-live-33.txt |
第三批落点现算 89 处(替换第 30 棒读数,见 §七-㈧) |
scan.txt |
现算汇总(data.js 9/imports.js 36/index.js 30/video-tools.js 1/workshop.js 13) |
out/ |
预留(本棒未产生额外落盘) |