范围 = 本线(插件投放与分库线)产物 + 记忆类,共 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/规则与载体/、归档/、接续包_*。
10 KiB
插件数据面取数口 · 47 真机验收 + 抓出接线缺陷(2026-09-25 · 第 15 棒)
一句话:把第 14 棒落地的「运行时取数口」部署到 47 并真机跑通(四拒一空 + 回读 7/7 PASS), 过程中抓出一条会让取数口对全部插件失效的接线缺陷(已修 + 已复验); 第 2 件(MCN 移动端视觉验收)未通过 —— 前提不成立:移动端修复落在未投放的包里。 归属线:插件投放与分库线|会话:插件投放与分库线-第15棒|交接单:无(本棒为执行棒)
一、本轮做了什么
| # | 事项 | 结果 |
|---|---|---|
| 0 | 开工门禁 + 域锁 | preflight 判【D】2 个 → 归类后抢锁成功(域 aliyun-dsh-server/接续入口_插件投放与分库线_20260922.md + aliyun-dsh-server/.workbuddy) |
| 1 | 部署取数口到 47 | ✅ 5/5 md5 逐字一致 + restart dshs + 服务 active、门户 200 |
| 2 | 数据面取数真机验收 | ✅ 7/7 PASS(四拒 + 一空 + insert→find 回读;归属列真值直连核) |
| 3 | 抓出并修复接线缺陷 | ✅ 已修 src/web/routes/plugin-data.ts + 重投 + 复验 |
| 4 | MCN 移动端视觉验收 | 🔴 未通过 —— 前提不成立(修复不在已投放的包里,详见 §五) |
| 5 | 夹具清理 | ✅ 零残留(池 5 / 插件库 0 / 台账 0 / 会话 0 / tgz 已删) |
二、部署面证据(47 · /opt/dshs)
| 文件 | 47 与本地 md5 | 说明 |
|---|---|---|
lib/web/server.js |
fcbbb9b3476499ba2d97841f0e6ebaed |
与 47 原版只差 7 行(就是 pluginDataRoutes 的 import + register +注释)—— 已逐行 diff 确认 |
lib/db/plugin-data/runtime.js |
45daf7c5b2b6163e3d1e1fd110dca36d |
新增 |
lib/db/plugin-data/registry.js |
44d3b5c424f7bc27a930454a945f6f06 |
新增 |
lib/web/routes/plugin-data.js |
a03394088f4ad90e571d4b5ede7a5f95 → 修复后 fac8f4dfe0d5710705de6d614275c77e |
新增 → 修复 |
sdk/im-plugin-client/index.mjs |
4a1a160156a90cc07696332fcc99d0cd |
新增(落 /opt/dshs/sdk/) |
- 备份:
/opt/dsh/backups/lib-p15/47-pre-p15-20260925-074345.tgz(投放前)。 - 重启:
systemctl restart dshs→active、NRestarts=0、门户200。 - 部署前先做了漂移比对(避免把别的棒的东西带上线):
business-plugins.js/worker/agent.js/leased-spawner.js/config.js四件与本地逐字一致 ⇒ 只投必要项。 - ⚠️ 106 未部署(本轮范围 = 47;取数口是 Manager 侧路由 ⇒ 106 不影响本轮验收)。
三、真机验收读数(47 · 调用者 = guest,插件身份头 = 包名)
夹具 = 一次性探测包 dsh-plugin-pdprobe(user 域 + room 域各一表;声明过平台真校验器 → level=ok)。
| 断言 | 期望 | 实测(HTTP 200 + 契约体) |
|---|---|---|
① 写他人 user_id |
拒 | ok:false, reason:bad-args,detail=不得写入他人的 user_id(归属列由内核注入) |
② room 表缺 room_id |
拒 | ok:false, reason:bad-args,detail=房间维度表必须带 room_id |
| ③ 缺主键列 | 拒 | ok:false, reason:bad-args,detail=缺少主键列「probe_key」(它是本表的行标识) |
④ 带 created_at |
拒 | ok:false, reason:bad-args,detail=审计列「created_at」由内核维护,⛔ 不可自写 |
| ⑤ 非成员读 room 表 | 0 行 | ok:true,value:[] ⇒ 0 行 |
⑥ insert 正常行 |
ok | ok:true |
⑦ find 回读一致 |
1 行且字段一致 | ok:true,value 1 行,note='v1'、payload={a:1} |
归属注入直连核(不靠响应自证):p_dsh_plugin_pdprobe_probe_user.user_id 全部等于
调用者本人 4092b965-…;probe_room 行数 = 0。
DDL 侧证(迁移回显):PRIMARY KEY ("user_id", "probe_key") ⇒ id = 声明第一列、归属列 + 第一列 复合键
(无代理自增 id)—— 与 DB-03 §五 口径一致,首次在真机上被看见。
对照(负例):给无声明的真插件取数 ⇒ 409 plugin-data-unavailable / no-declaration(不是 500,不是"假装成功")。
四、🔴 抓出的接线缺陷(P0 级 · 已修 + 已复验)
现象:库建好(state=ready)、插件身份头合法、鉴权通过 —— 但取数一律
409 no-declaration:插件「dsh_plugin_pdprobe」不在候选池或包内无数据面声明。
根因(两个名字空间被混用):
| 名字空间 | 取值 | 谁在用 |
|---|---|---|
| 池键 / 台账键 | 原始包名:dsh-plugin-pdprobe、@dsh-local/storyforge |
business_plugins.id、plugin_datastores.plugin_id(建库口、门户都用它) |
pluginId(归一化) |
去 scope + 下划线化:dsh_plugin_pdprobe |
pluginIdOf();用于算库名/表名前缀 |
取数口把归一化后的 id 交给了 PluginDataRegistry,而 registry 内部拿这个值去
findBusinessPlugin / findPluginDatastore 精确查这两张表 ⇒ 永远查不到 ⇒
任何插件调取数口都只会得到 no-declaration(不是这一个夹具的问题)。
为什么单测没抓到:test/plugin-data-runtime.test.mjs 用假执行器,直接构造声明对象,
绕过了路由层的 id 归一化 ⇒ 这条路径只有在真机上才会被走到。
修法(最小):src/web/routes/plugin-data.ts 两处
resolvePluginPort:pluginId: pluginIdOf(pkgName)→pluginId: pkgName(原始包名)/plugin-data/status:查表用原始包名,对外仍回归一化pluginId(响应契约不变)
复验:npm run build rc=0|node --test test/plugin-data-runtime.test.mjs test/im-plugin-client.test.mjs → 24/24 过
|重投 + 重启 → 7/7 PASS(同 §三)。
⛔ 未 commit / 未 push(遵守本棒约束);改动留在
D:/github/dsh_shenxian工作树(plugin-data.ts)。
五、🔴 第 2 件(MCN 移动端视觉验收)—— 未通过:前提不成立
验收目标(入口 §2 原文):D:/dshworkspace/plugin_package/dsh-plugin-mcn/lib/client.js 的 mcn-mobile
样式段 → 375×667 下工作台独占、能退出、无横向溢出、聚焦不缩放、横屏可用。
真机取证(为什么做不了):
| 检查 | 结果 |
|---|---|
| 线上真身是哪个包 | 池与共享层里的都是 dsh-plugin-mcn-suite v0.3.13(/var/lib/dshs/bundled-plugins/dsh-plugin-mcn-suite) |
| 该真身含修复吗 | 否 —— lib/client.js 里 mcn-mobile 命中数 = 0(mcnNav_split = 4) |
| 本地 suite 含修复吗 | 否 —— 本地 dsh-plugin-mcn-suite/lib/client.js(0.3.13 · mtime 09-20 22:12)mcn-mobile = 0 |
| 修复改在哪个包 | dsh-plugin-mcn v0.1.0(本地 lib/client.js mtime 09-25 05:18)—— 该包从未投放 |
⇒ 移动端修复改在了「不是线上真身」的包里。所以"重投插件 + 实例重启"无从生效: 线上跑的一直是 suite,它里面没有这段样式。故本件不做视觉验收,也不宣称通过(无真机读数不得宣称)。
整改路径(可机械执行 —— 已核对可移植性):
mcn-mobile 段是一段运行时注入的 <style>(document.head.appendChild,data-plugin-css="mcn-mobile"),
选择器全部是 .mcnNav_* / vvModal 族。已逐个核对它在 suite 里的存在性:
.mcnNav_split |
.mcnNav_panel |
.mcnNav_resizer |
.mcnNav_close |
.mcnNav_btn |
.mcnNav_mask |
.mcnNav_modal |
.mcnNav_stats |
.mcnNav_input |
vvModal |
|---|---|---|---|---|---|---|---|---|---|
| 4 | 3 | 3 | 2 | 12 | 3 | 10 | 2 | 2 | 2 |
⇒ 类名齐备 ⇒ 可整段平移(把同一段注入进 suite 的 lib/client.js,dataset.plugin 改成 suite 名)。
⚠️ 重投前必须先验本地 suite 与线上 suite 逐字一致(否则会把别的漂移一起发出去);
⚠️ 版本要自增(0.3.13 → 0.3.14)并同步 lib/skills-installer.js 的 SUITE_VERSION 与 skills/.manifest.json(该包「版本号唯一真源」口径)。
六、第 3 件(可选 · MCN 数据面接入改造)—— 未做
超本轮范围(lib/db.js 红线扫描 21 处、且须「先建库再投放」)。按入口 §2 的口径留给后续棒。
七、清理与零残留(夹具全清)
| 项 | 清理后读数 |
|---|---|
| 候选池 | 5(回到原值,夹具行 0) |
插件库 dshs_pl_* |
0 |
plugin_datastores |
0 |
plugin_data_audit(夹具) |
0 |
临时会话(poc-curl2) |
0 |
| 夹具 tgz / 备份目录 | 已删 |
| 服务 | active |
⚠️ 夹具在池内存活约 2 分钟(07:56–07:59 量级),期间登录用户理论上可在「功能管理」看到它 ——
已删除,无残留。这是本轮唯一一处对线上可见面的临时触碰,主动记录。
八、未闭环 / 下一棒
- 🔴 MCN 移动端修复的「迁包 + 重投 + 375×667 视觉验收」(见 §五)—— 本线唯一排定的下一棒。
- 跨节点形态:worker 上的实例 → Manager 侧数据面仍缺「实例→控制面」身份通道
(本轮验收是同机/本机形态)⇒ 属既有缺口(
DB-03 §六自认),未动。 - 106 未部署本轮产物(Manager 侧路由,不影响本轮验收)。
九、可复现命令
# 部署(三件产物 + server.js)
scp lib/web/server.js bt-server:/opt/dshs/lib/web/server.js
scp lib/db/plugin-data/{runtime,registry}.js bt-server:/opt/dshs/lib/db/plugin-data/
scp lib/web/routes/plugin-data.js bt-server:/opt/dshs/lib/web/routes/
ssh bt-server 'systemctl restart dshs'
# 验收(夹具搭建 + 7 条断言 + 零残留清理)
scp tmp/15棒-夹具/dsh-plugin-pdprobe-0.0.1.tgz bt-server:/var/lib/dshs/business-plugins/
scp tmp/15棒-夹具/pd-probe-47b.mjs bt-server:/tmp/ # 或第 15 棒工作区 tmp/15棒-夹具/
ssh bt-server '/usr/local/bin/node /tmp/pd-probe-47b.mjs'
ssh bt-server 'bash -s' < tmp/15棒-夹具/remote-cleanup.sh