Files
dsh_ai1net_server/接续入口_插件投放与分库线_20260922.md
T

54 KiB
Raw Blame History

接续入口 · 插件投放与分库线(2026-09-22 开线)

工作区:E:/ProgramData/AIProject/aliyun-dsh-server(本入口只此一份) 开线棒:会话 aliyun-dsh-server 2026-09-22 13:2x(用户当场提要求后收口) 代码基线:D:\github\dsh_shenxian = 生产口径 dshs;⚠️ 另有一份开源导出物 dsh_ai1net(/opt/dsh_ai1net · /etc/dsh_ai1net.env · DSH_AI1NET_*)是另一套命名、另一个部署,⛔ 两者不要混用(本会话已实测踩过这个坑)。 上游上下文:同工作区 .workbuddy/memory/2026-09-22.md 的 10:0x 节(carbon 线"两处平台侧阻塞"判定)+ 07:5x 节(一插件一库拍板)。


§0 状态(最新一行 = 执行依据)

  • 2026-09-22 13:2x|开线 —— 规划棒①收口(出交接单 ①共享只读包库与插件数据面.md)。
  • ✅ 2026-09-22 17:0x|执行棒①完成(S1/S2 全绿) —— 共享只读包库已在 47+106 上线并真机取证;实投 mcn-suite + storyforge。S3/S4/S5/S6 未做。
  • 🔴 2026-09-22 18:1x–21:1x|编码与设计(受 IM 线锁占用,只出件不动服务器) —— 用户追加口径「admin 全流程自闭环」(上传 → 检测通过 → 点建库按钮 → 建库成功才能开启 → 更新按钮 → 更新也检测 → 告知数据库改动 → 确认执行按钮)。产出设计件 交付物/数据面管理面-建库与更新流程-20260922.md(7 态状态机 / 检测第 3 项 / dsh.data 声明 / API 契约 / 两段式预演-确认 / 门户改动清单)。
  • ✅ 2026-09-22 19:3x|用户拍板建库权限 = D,产出 D 档 Runbook。
  • 🔴 2026-09-23 05:2x|D 档实测被证伪(重大反转) —— PG 显式禁止从函数内执行 CREATE DATABASE(cannot be executed from a function;另一层是「不能在事务块内」,而 PL/pgSQL 函数体恒在事务内)。同姿势下 CREATE SCHEMA / CREATE ROLE 均成功 ⇒ 语句级内核限制。旁路 dblink / postgres_fdw 均不可用。已回滚,服务器零残留。判定件 ⇒ 交付物/建库权限-D档被证伪与修正判定-20260923.md。
  • ✅ 2026-09-23 05:4x|用户拍板建库权限 = B,并已在 47 落地 —— ALTER ROLE dshs CREATEDB; + 11 条验收全过(核心断言:权限精确 false|true|false,未要 CREATEROLE / 未要超管)。🔴 B 档真实缺口:PG 无「只允许 dshs_pl_* 前缀」的原生机制(实测建 evil_probe 成功)⇒ 白名单只能靠平台代码兜住三点(库名单点生成 + 正则复校 + 每次建库落台账审计)。落地件 ⇒ 交付物/插件建库权限-B档落地-20260923.md。
  • 🔴 2026-09-23 05:5x|排期调整:本线代码面现在开工 —— 原拍板「A = 等 IM 线整阶段收官后一次性跑完」的前提已变化:IM 线因 E 单 UI 落点待拍板而停下(A–D 四单已落地、E 单未开工、未登记接续棒 ⇒ 整阶段收官无时点),本线继续空等已不成立。故改为:不依赖拍板的部分(设计件 §八 除建库口以外的全部)即刻推进;建库口与三份文档回写另行安排。 下一棒(唯一) = 第 3 棒 · 执行棒 = automation fe16b14a-cd7e-424d-99b0-37a21972365a(一次性 · scheduledAt = 2026-09-23T05:55)。 ⛔ 拍板项(不得自动跨过):无新增;建库通路已定 = B 档。
  • ✅ 2026-09-23 06:0x|第 3 棒 · 执行棒完成(设计件 §八 除建库口外全部落地) —— 7 项全做完:src/db/plugin-data/schema.ts(新建 · 声明解析校验 9 中性类型 · pluginDbNameOf/assertPluginDbName)|src/db/plugin-data/diff.ts(新建 · diffDecl/planHashOf/forbidden 只增不减)|v15 迁移 plugin_datastores × plugin_data_audit(台账 = B 档白名单第三点兜底)|检测第 3 项「数据面声明校验」|三处门禁(enableDenialOf / 检测不过不许建库 / planHash 未确认不许迁移)|门户「数据面」列 + 7 态徽章 + 预演清单面板 + 状态驱动按钮|版本回读校验(只认内容指纹,漂移写 plugin_version_drift 审计)。 验证:npm run build rc=0 | npm test = 391/389 过 / 0 败 / 2 跳过(基线 366/364 过 / 2 跳过,本棒 +25 条)|check-layering ✅ 无新增违规(added: 0)。⛔ 未做(属下一棒):建库口接线 / 三份文档回写 / S3·S5-a·S6 / 跨节点内容分发。 下一棒(唯一) = 第 4 棒 · 执行棒 = automation 6e8cedcd-f426-4390-9460-937573d93f7d(一次性 · scheduledAt = 2026-09-23T06:27,约 8 分钟后);接续点 = 建库口接线(调 B 档 CREATE DATABASE、单语句不走事务、落台账 + 审计)。
  • ✅ 2026-09-23 07:0x|第 4 棒 · 执行棒完成(建库口接线 · B 档全部兑现) —— §5 的 5 项全做完:src/db/plugin-data/datastore.ts(新建 479 行 · CREATE DATABASE 单语句非事务 / readCurrentSchema 真读 information_schema + p_meta_schema / executePlan 建库项非事务+表级 DDL 同事务 / reconcilePluginDatabases 对账 / 错误码映射 PG_CREATEDB_MISSING·invalid_plugin_db_name)|currentSchemaOf 去占位|路由 POST /datastore(建库)+ GET /datastores/reconcile(对账告警)+ /datastore/plan 的 executable 真判据 + /datastore/migrate 去 503 真执行|门户「建库」按钮接真实路由(原"尚未接线"禁态已去掉)。 验证:npm run build rc=0 | npm test = 426/424 过 / 0 败 / 2 跳过(基线 391 过 ⇒ 本棒 +35)|check-layering ✅ 无新增违规(added: 0)。47 真机取证通过(真实编译产物走建库口):建库 created 71 ms/幂等 exists/非法名 invalid_plugin_db_name 未下发 PG/库内台账真读 exists:true, schemaVersion:7/planHashPreserved:true(COALESCE 语义)/对账抓出 orphan dshs_pl_borphan4。清理后零残留(pluginDbs:[] · 控制面表回到 13 张 · dshs active)。落地件 ⇒ 交付物/建库口接线-B档落地-20260923.md。 ⚠️ 本棒记的已知缺口(⛔ 未假装做了):① 迁移前结构备份(设计件 §七-3 的 pg_dump)属运维面,归 S5-a 棒;② 47 控制面仍停在迁移 v13 ⇒ v15 两张台账表要平台启动时 runPgMigrations 才落,部署本棒代码时随 restart dshs 自动补。
  • ✅ 2026-09-23 07:2x|第 5 棒 · 执行棒完成(三份文档回写 · 纯文档零代码) —— 共享文件持锁成批做,全部落笔: ① DEPLOY-本部署.md 新增 §6.4 数据库初始化(PG13 / 47 本机进程):建角色建库 + 🔴 ALTER ROLE dshs CREATEDB;(标用途 = 插件建库,⛔ 未给 CREATEROLE/SUPERUSER)+ 回滚句 + 权限校验判据(f|t|f)+ 白名单只能靠代码 + v15 迁移表随 restart dshs 自落。 ② 交接单/…①共享只读包库与插件数据面.md:§五 S4-2 改写为 admin 显式按钮(上传只检测 ⇒ pending ⇒ 点 [建库] ⇒ 成功才能开启;两道后端门禁;单语句非事务;两重防线;错误码)|§十-D1 表格行同步改并标注原写法作废。 ③ 数据库/DB-03-插件数据面规范.md:执行状态块整块重写(🔴 受阻 ⇒ ✅ 已落地;仅剩"跨节点身份通道为 0"一条真缺口)|§三 新增 「三·补 谁建库、什么时候建」|§四 执行时机改两步(建库 / 迁移都是插件级一次性)+ 表级同事务 / 建库不可回滚 / planHash COALESCE 语义|§六-2 补两张台账表结构(plugin_datastores / plugin_data_audit)。 ④ 顺带核对 交付物/数据面管理面-建库与更新流程-20260922.md §十一:D 档段已作废(头部 + §十一 末尾均已有更新说明)⇒ ⛔ 未改动其历史结论文本,核对通过。 验证:npm run build rc=0 | npm test = 426 tests / 424 过 / 0 败 / 2 跳过(与基线一致,纯文档零变动)|git diff --stat 本次改动 = DEPLOY-本部署.md +21(唯一 tracked 的直接证据;另两份在未跟踪路径下:交接单/ 被 .gitignore:45 排除、数据库/ 整个目录仍是 ?? 未跟踪 ⇒ 改动落地但不出现在 tracked 清单,属既有仓库形态,⛔ 与本棒无关)。锁已 --release-exec。 下一棒(唯一) = 第 6 棒 · 执行棒;接续点 = S3 跨机装配(POST /plugins/apply + remote-spawner.applyPlugins + mine/apply 改走台账裁决)。
  • ✅ 2026-09-23 08:1x|第 6 棒 · 执行棒完成(S3 跨机装配 · 真机 S3-E 全过) —— §5 的 4 项全做完:新建 src/supervisor/plugin-assembly.ts(约 400 行,两台机共用的唯一实现)|worker/agent.ts 新增 POST /plugins/apply(同族于 /restart-probe,⛔ 不新增入站口)|remote-spawner.applyPlugins(按 hostIdFor 定位机器)|mine/apply 改只做清单裁决(⛔ 不再本机 runPnpmAs)。 验证:npm run build rc=0 | npm test = 439/437 过 / 0 败 / 2 跳过(基线 426/424 ⇒ +13)|check-layering ✅ 无新增违规(added: 0)。 S3-E 真机取证(106 · 非 Manager worker · 用户 4092b965 uid 100002 · 包 @dsh-local/storyforge 0.1.0)①–⑦ 全过:applyOk:true|file:/var/lib/dshs/bundled-plugins/_dsh-local_storyforge|node_modules/<pkg> 软链 ✓|实例内 ls 可见 + touch = Read-only file system + mountinfo ro,nosuid,nodev|反证 plugin_bundle_missing 报错可读、实例不崩|⛔ 47 的 users/ 下该用户零 storyforge 命中(连软链都没有,目录 8.0K 空壳)= D2 主判据过。停用腿同验;用户面零残留(profile 已复原)。 附带修正(真机暴露的既有缺陷,两条):① pnpm install --ignore-workspace-root-check —— pnpm 9 不认该 CLI 选项(只当 config 项)⇒ Unknown option 直接失败(旧代码一直裸写,2026-09-13 07:43:58 曾因此把平台带崩、当时只加 try/catch 兜住进程)⇒ 改 -w(与 scripts/ensure-biz-plugins.cjs 既有正确姿势一致)+ 回归测试钉住;② 启用项先落盘再 pnpm ⇒ 失败留半装状态(依赖已声明、实际未安装)⇒ 改先解析全部包(失败即抛、profile 未改)+ pnpm 失败回滚。另修 RemoteSpawner.call() 既有 4xx 重试 bug(4xx 的 throw 被 catch 吞掉、循环跑满 4 次)。 部署:两机 lib/ 同批(plugin-assembly.js md5 134b5704… 逐字一致)+ 备份 lib-pre-s3-*.tgz;47 restart dshs 后 v15 两张台账表已落;106 共享层已同步(storyforge + mcn-suite);relay-dialer 实测连到 ops/w-106。落地件 ⇒ 交付物/S3跨机装配落地-20260923.md。 🔴 本棒抓出 1 条口径级缺陷(已取证 · ⛔ 未擅改 ⇒ 待拍板):D-h 拍板的 file: 协议在 pnpm 9 下会把插件包整份复制进每用户 .pnpm/(实测 6.5 M/用户/包、116 文件、links=1、inode 与源不同),不满足 D2「实体一份、链接多份」;根因 = pnpm 对 file:(目录型)走复制、⛔ 不硬链(同 profile 普通依赖 links=9 可证)。已实测替换方案 link: = 4.0 K 纯软链直达共享层,且抗 reconcile(加依赖 / remove 后软链仍在,即 D-h 当初选 file: 的唯一理由仍成立)。⇒ 因涉及已拍板项 D-h,交用户拍板(候选 A 维持 file: / 候选 B 换 link:,优缺点见落地件 §六),倾向 B。⛔ 拍板前不得改协议写法。 下一棒(唯一) = 第 7 棒 · 执行棒 = automation 9cf7c353-415d-4347-9b59-7ae4449ae986(一次性 · scheduledAt = 2026-09-23T08:23,约 6 分钟后);接续点 = S5-a 兼容矩阵 + 迁移前结构备份 + S6 门户三段式 UI。⛔ 该棒不得碰装配协议(见上「待拍板」)。 🔴 新登记待拍板项(唯一):装配协议 file:(现状)vs link:(实测 4 K)。拍板落地位置 = src/supervisor/plugin-assembly.ts 的协议写法 + 交接单 8 处 file: 描述。
  • ✅ 2026-09-23 08:4x|第 7 棒 · 执行棒完成(S5-a + 迁移前备份 + S6 三段式 UI) —— §5 的 3 项全做完,文件面 8 改 2 新: ① S5-a 兼容矩阵落定稿 数据库/DB-03-插件数据面规范.md 新增 §四·补(6 行场景表:首次装 / 升级 / 等价换包 / 降级 / 幂等重传 / 同名异内容冲突;三条硬规则;B 档两条前置;4 条可复现验证)—— 核心口径 = 两个版本号是两件事,判"库该不该动"只看 schemaVersion。 ② 迁移前结构备份(第 4 棒记下的缺口):datastore.ts 新增 backupSchemaOnly()(pg_dump --schema-only → <backupRoot>/plugin-db/<pkg>/<v>-<ts>.sql)+ 接进 datastore/migrate 路径:非首次建库 ⇒ 先备份,ok=false ⇒ 500 schema_backup_failed 且不执行(设计件 §七-3 原文落地)。三条纪律:失败即不执行 / 只导结构 / 口令走 PGPASSWORD env ⛔ 不进 argv。两处自决:只在 !isFirstCreate 时备份(否则首次建库全堵死)· 0 字节视为失败(空库也会写头部 ⇒ 空文件=静默失败,不判它就把纪律绕过去了)· 包名路径折叠(先折分隔符再折连续点)。 ③ S6 门户三段式 UI:web/portal.html 新增第 3 个 tab「用户可见性(三段式)」+ .sec-head 等分段样式 + loadVisibility();三段数据来源分两路不混——① 平台共享只读 ← 新增 GET /api/plugins/shared/catalog,②③ 已启用/已停用 ← /api/plugins/mine(该路由保持原样,⛔ 未顺手加过滤),另附「⋯ 暂不可用」把"池里有没发布"与"用户没开"分开(混讲会误导 admin)。 🔴 新增开关 DSHS_SHARED_CATALOG_PUBLIC(默认 false):新路由是用户可见面变化(此前登录用户无任何共享清单口子)⇒按 §1「扩大可见面」红线默认关,关着 = 404(⛔ 不是空列表:空列表会被读成"共享层是空的")。未擅自开。 ④ DB-03 新增 §八·补 端侧边界声明:端侧走同一个平台 API ⇒ 只保证「能看见 + 能开通」;⛔ 不要求本地装包 / ⛔ 不下发平台级凭据 / ⛔ 端侧不落插件数据。 验证:npm run build rc=0 | npm test = 447 / 445 过 / 0 败 / 2 跳过(基线 439/437 ⇒ +8:新文件 5 条 + plugin-data 追加 3 条)|check-layering ✅ 无新增违规(added: 0)|verify-inject 全合格。落地件 ⇒ 交付物/S5-S6落地-20260923.md。 ⚠️ 本棒记的已知缺口(⛔ 未假装做了):未部署到 47/106(deploy-deferred-to-next-step)⇒ S5-E / S6-E 的真机 E2E 未跑(须部署 + 先开可见面开关);跨节点内容分发仍无平台侧通路(可立独项)。 ⛔ 本轮严格未碰装配协议:plugin-assembly.ts 零改动、交接单 file: 相关描述零改动。 下一棒(唯一) = 第 8 棒 · 执行棒 = automation 87c8221c-a7ce-41e2-b5fa-e56fc0c8c929(一次性 · scheduledAt = 2026-09-23T08:49,约 7 分钟后);接续点 = 部署到 47/106 + S5-E / S6-E 真机 E2E。 🔴 待拍板项仍是同一条(唯一,⛔ 未变):装配协议 file:(现状)vs link:(实测 4 K)。拍板落地位置 = src/supervisor/plugin-assembly.ts 的协议写法 + 交接单 8 处 file: 描述。 🔴 下一棒新增红线:可见面开关 DSHS_SHARED_CATALOG_PUBLIC 属红线门禁(CODEBUDDY.md §1 扩大可见面)⇒ ⛔ 下一棒不得擅自打开;S6-E ② 若要跑,要么先向用户取得开启许可,要么只跑不需要该开关的部分(E ①③)。
  • ✅ 2026-09-23 09:5x|第 8 棒 · 执行棒完成(部署 + S5-E / S6-E 全绿) —— §5 的 4 项全做完: ① 部署:npm run build rc=0 ⇒ 产物 scp 到 47 /opt/dshs + 106 /opt/dshs-cluster,三机 md5 逐字一致(db/plugin-data/datastore.js 9fedd77c… / web/routes/business-plugins.js fe456d60…);47 restart dshs rc=0;重启后 plugin_datastores + plugin_data_audit 仍在(v15 未丢)。部署前备份 /opt/dsh/backups/lib-s5s6/{47,106}-pre-s5s6-20260923-085319.tgz。 ② S5-E ①–⑤ 真机 E2E 全 PASS:① v1→v2 加列升级,升级前后均 3 行(存量原样)|② 强行降级 ⇒ items=[] + forbidden=[drop_column p_s5probe_notes.pinned] + executable=false + state=blocked ⇒ 409 plan_blocked,列与行数一字未动|③ 同名同版本重传 http=200、tgz md5 与源一致(幂等)|④ /opt/dsh/backups/plugin-db/<pkg>/ 2 个 .sql(v0-…643B / v1-…1344B)、0 字节文件数 = 0、台账 datastore_migrate_backup_ok x2|⑤ 对照 200 + 反证 500 schema_backup_failed(mkdir_failed: EPERM)、库结构未变,环境已原样还原。 ③ S6-E ①③ PASS:① admin 池 5 = guest /mine 5|③ admin 新增 @dsh-local/s6newprobe ⇒ guest 5 → 6、无需管理动作。② 只记事实 ⇒ DSHS_SHARED_CATALOG_PUBLIC 未设置(默认 false)⇒ 404;🔴 开关全程未开(红线门禁遵守)。 🔴 本棒抓出并修复 1 条真实缺陷(真机专属 —— 本机测不出):datastore.ts 里 pgEnvFrom(baseUrl) 的返回值被展开在 execFileSync 的 options 层(与 env: 同级)⇒ PGHOST/PGPORT/PGUSER 没下发到 pg_dump 子进程 ⇒ 退回 Unix socket :5432 ⇒ 47 上 No such file or directory ⇒ 结构备份在生产上根本跑不通。修法 = 新增 pgDumpEnv() 并合进 env;另修 business-plugins.ts 里第 4 棒遗留的 backup:'not_implemented' 不实文案 ⇒ 改真实值(skipped_first_create / taken + backupPath)。回归钉住 +2 用例(⚠️ 首版用 PATH 假 pg_dump 桩在 Windows 上失败(.cmd/.bat 无法解析 ⇒ ENOENT/EINVAL)⇒ 改为直接断言 env 不变量:四个 PG 变量必须存在、且 ⛔ 不得混进 stdio/timeout/cwd/encoding)。重部署后 ④ 腿备份成功落盘 ⇒ 闭环。 验证:npm run build rc=0 | npm test = 449 / 447 过 / 0 败 / 2 跳过(基线 447/445 ⇒ +2)|check-layering ✅ 无新增违规(added: 0)。落地件 ⇒ 交付物/S5-S6真机E2E验收-20260923.md。 ⚠️ 本棒记的已知缺口 / 未做项(⛔ 未假装做了): ① S6-E ② 字面判据(/mine 返 enabled:true)未闭合 —— 不是代码缺陷,是拓扑形态的必然:客实例(uid 100002)在 106(worker),而 /mine 读的是平台本机 profile 快照(47 上为空)⇒ 本机快照反映不了远端装配。实测 enable 已正确落到实例所在机 106(node_modules/@dsh-local/storyforge 软链 09:43 建立、可解到 package.json、package.json 写入 file:/var/lib/dshs/bundled-plugins/_dsh-local_storyforge)⇒ 判据用 106 侧文件证据闭合。旁证:dsh-100002-9648cef4.scope 沙箱命令行含 --ro-bind-try /var/lib/dshs/bundled-plugins(S1 共享只读层挂载成立)。🔴 ⇒ 暴露一条缺失能力:平台侧"用户可见启停状态"没有跨机回流通路(现在只有单向 apply) —— 建议单独立项,本棒只记录、未改架构。 ② 门户三段式 UI 的 CSS 视觉渲染 / tab 点击 未验(本机 agent-browser 每次调用均挂死,属环境限制 ⇒ fail-fast;已清理 chrome 进程)⇒ 改用等价判据 ui-logic-check.mjs 复跑 portal.html 的 loadVisibility() 逻辑:7/7 断言通过(互斥 / 完备 6 vs 6 / 语义正确 / 开关关 ⇒ ③=0)。 ③ 跨节点内容分发仍无平台侧通路 ⇒ 需单独立项。 清理:探针库 / 候选池行 / 账本行 / 审计行 / 备份目录 / 共享层探针 tgz 全清,零残留(%probe% 三处计数全 0、池子恢复原始 4 个);platform-dirs.conf 原样还原(DSH_PLATFORM_DIR=/opt/dsh + 无 .bak-s5e);106 客实例 storyforge 启用已回退(回到 4 依赖原始态:business-plugins / portal-entry / workspace-scoped-picker / dsh-file-preview),106 dshs-worker + dshs-relay 均 active。 ⛔ 本轮严格未碰装配协议:plugin-assembly.ts 零改动、交接单 file: 相关描述零改动;⛔ 未开可见面开关;⛔ 未 commit / 未 push / ⛔ 未引新依赖 / ⛔ 无 merge。 下一棒(唯一) = 第 9 棒 · 规划棒 = automation 9028a886-6b79-497e-a57c-1a33dcd796c3(一次性 · scheduledAt = 2026-09-23T09:59,约 7 分钟后);接续点 = 跨机可见性状态回流(缺口立项)+ 三门一致性复验。⛔ 三条硬禁止:⛔ 不碰装配协议(D-h 待拍板)· ⛔ 不开可见面开关 · ⛔ 不改 plugin-assembly.ts。 🔴 待拍板项仍是同一条(唯一,⛔ 未变):装配协议 file:(现状)vs link:(实测 4 K)。拍板落地位置 = src/supervisor/plugin-assembly.ts 的协议写法 + 交接单 8 处 file: 描述。 🔴 待拍板项(本轮新增 · 第 ② 条):平台侧"用户可见启停状态"的跨机回流通路要不要立 —— 现状是单向 apply、无状态回流 ⇒ 实例在 worker 上的用户,平台侧 /mine 恒显 enabled:false。候选 A:立专项做状态回流(优点:平台侧启停状态全域准确、admin 与用户看到同一事实;缺点:新增一条控制面回传链路、要定拓扑与重试语义)|候选 B:维持现状、把"跨机时 /mine 的 enabled 语义明确为「本机快照、可能滞后」并在 UI 上标注(优点:零改动、立刻可交付;缺点:admin 侧看到的启停状态在跨机场景下永远是错的,会误导管理动作)。
  • ✅ 2026-09-23 10:0x|第 9 棒 · 规划棒完成(跨机可见性状态回流设计件 + 三门基线复验 + 交接单登记) —— §5 的 3 项全做完,只出设计件 · 零代码改动 · 未动服务器: ① 设计件 ⇒ 交付物/跨机可见性状态回流-设计件-20260923.md(10 节):问题定义(4 条影响面 + 3 条排除项)|事实基础 13 条(现读代码,带行号)|🔴 三条硬约束(C1「worker 主动上报」在"只拨出"形态下物理走不通;C2「Manager 主动拉」的骨架 reportHost() 已在跑、增量只是把返回值加宽;C3 读路径必须与响应路径同源)|通路形态三候选(A 主动拉/B 共用台账/C worker 上报,逐条优点+缺点 + 一张对比表)|写入时机 T1(apply 完成后)+ T2(实例启动 reconcile,由 worker 本机驱动、Manager 不在场)|陈旧度语义(asOf 必带 + stale 现算不落库 + 阈值 = 3×心跳周期)|失败重试与不可达降级(5 场景表,核心 = ⛔ 不许把"未知"显示成"未启用")|与 v15 台账的关系(粒度不同塞不进去;A 案不建表、B 案才建且必须落控制面)|"不做会怎样"四尺度(长期越晚越贵:多 Manager 联邦下每个区都要各造一套)|落地轮廓 + 附复核命令。 🔴 本件的关键否证:候选 B 不能单独成立 —— reconcile 的驱动点在 worker(agent.ts 20 s 本地定时器),Manager 不在场 ⇒ "apply 时写台账"覆盖不到该路径 ⇒ 台账只能当缓存、⛔ 不能当权威。候选 C 要新开一条 worker→Manager 入站口 ⇒ 撞 §1「扩大可见面」红线。 技术倾向(可推翻 · ⛔ 未替拍):A 为骨架 + B 降级为可选缓存;C 需用户先授权新开入站口。 ② 三门一致性复验(基线记录 · 未改代码 ⇒ 确认第 8 棒部署未引入漂移):npm run build rc=0(tsc -p tsconfig.json 无输出)|npm test(Node 22)= 449 tests / 447 过 / 0 败 / 2 跳过(与第 8 棒基线逐字一致)|node scripts/check-layering.mjs = ✅ 无新增违规(现存 5 条全在基线内;⚠️ 报「未归类 1 = src/platform-paths.ts」属既有状态,非本棒引入)。 ③ 交接单登记:交接单/插件投放与分库线-①共享只读包库与插件数据面.md §五 S6 新增「🔴 已知边界」小节(成因代码位置 + 106 真机实证 3 条 ✅ / 1 条 ❌ + 影响面 3 条 + ⚠️ 区分响应路径与读路径 + 状态=缺失能力已立项);§六 验收表第 8 行同步加注(② 的字面判据仅同机形态成立)。 ⛔ 本轮严格未碰装配协议:plugin-assembly.ts 零改动、交接单 file: 相关描述零改动;⛔ 未开可见面开关;⛔ 未改任何生产代码、未动 47/106 服务器。 下一棒 = ⛔ 未登记(拍板项未定 ⇒ 按用户级记忆「要用户拍板的,等拍了再新建接续会话」)。接续点 = 用户对通路形态拍板后,排「跨机可见性状态回流」执行棒。 🔴 待拍板项 = 两条(⛔ 未变 + 新增): ① 装配协议 file:(现状)vs link:(实测 4 K)。落地位置 = src/supervisor/plugin-assembly.ts 协议写法 + 交接单 8 处 file: 描述。 ② 跨机可见性回流的通路形态(A 主动拉/B 共用台账/C worker 上报,逐条优缺点见设计件 §三)。落地位置 = worker/agent.ts 的 /healthz + leased-spawner.ts 的 reportHost() + business-plugins.ts 的 /mine。
  • ✅ 2026-09-23 15:4x–16:2x|A 线收口 · 装配协议 link: 全落地(① ② ④ ✅ · ③ 实测撤销) —— 用户口令「先把 a线做完,处理完成后在做b线」,本段为 A 线(插件投放与分库线) 的收口: 代码:plugin-assembly.ts 新增 depRefOf()(+ pnpmExecDisabled()/runPnpm() 测试缝)|business-plugins.ts 4 处 fileRef 同步为 depRefOf。三门全绿:npm run build rc=0|npm test = 450 / 448 过 / 0 败 / 2 跳过|check-layering ✅ 无新增违规(added: 0)。 部署:lib/supervisor/plugin-assembly.js + lib/web/routes/business-plugins.js 已 scp 两机 —— 🔴 47 = /opt/dshs/、106 = /opt/dshs-cluster/(106 上没有 /opt/dshs,这是本轮新查实的事实);两机 md5 逐字一致(fe5162ec…/ed507b7a…);restart 47 dshs+dshs-worker、106 dshs-worker;门户 3080 → 200、worker 19100 → 401;旧产物备份 /opt/dsh/backups/plugin-assembly-20260923/。 真机复验(47):软链 54 B + .pnpm 8.0 K(对照 file: 6.5 MiB/116 文件 ⇒ 约 1000 倍差距)、目标回溯直达共享层、用户身份下可读、反证原子抛错;探针用户已回滚。 🔴 ③ 存量用户迁移实测撤销(本轮最重要的否证):全平台只有 1 个带依赖实例,5 条依赖全 .tgz 型(目录型 0 条),只有 1 条在共享层有目录 ⇒ 原迁移方案会让 4 条悬空 ⇒ 实例崩。成因 = 那 4 个包是平台自研核心组件、从不进共享层。⇒ 已从待落地清单撤下,交接单 §9.3 整节改写。 文档:交接单该文档 —— 状态行(S3 → 全绿已上线)|§五 S3-E 新增真机复验表 + 部署状态段|§五 S4-2 无改|§9.3 整节改写(含"dsh-file-preview 已指向共享池"的误读纠正)|§十二 执行回报第 1 行(S3 → 已做)。入口 §5 待落地块 + 本线后续第 4 条同步。 ⚠️ 残留一条待补(不阻塞):106 侧跨机腿 —— 106 现无带插件用户实例可复验,等有用户启用时按 S3-E ④⑤⑥ 补一次。 ✅ 2026-09-23 18:3x|用户拍板 —— 跨机可见性形态 = D「按需定向拉(主)+ worker 变更上报(辅·后置)」 —— 用户原话「可以 worker 上报,具体要管理那个用户的时候 manager 针对性的去查」。原 A / B / C 三候选作废;设计件 §十 已整节改为定稿。 🔴 本棒(设计阶段)拿到的最有价值事实:fenceOnAgent()(src/supervisor/leased-spawner.ts:144-155)里已经有「findUserInstance(userId) → agentFor(hostId) → 带 token 定向 POST 到那台 worker」的完整三步定位链 ⇒ 主路径=只把目标端点从 /fence 换成新只读口(建议 /profile-bundles)。⇒ 零新增监听口 · 零新增凭据体系 · 零广播,且天然覆盖 reconcile 路径(原 B 案的死结,因为查的是对端现算真值而非历史快照)。 ⛔ 两条硬约束(写码前必守):① 新端点必须带 token(与 /fence 同级)—— ⛔ 绝不可挂到免凭据的 /healthz 上,否则等于开匿名任意读口;② 查不到须返回 not_here,⛔ 不等于"零插件"(两者语义不同,混用即复制本缺陷)。 📌 落地登记 = 下方「⏭️ 本线后续」第 1 条(本条为设计与拍板落定,⛔ 未写一行生产代码、未动 47/106)。 🔴 下一棒(automation)本轮排不出来 —— automation_update 创建时返回 file is not a database:宿主活动库 E:\ProgramData\.workbuddy\workbuddy.db(以 CODEBUDDY_CONFIG_DIR = E:\ProgramData\.workbuddy 判定)第 1 页 header 已丢失 ⇒ 全机 automation 功能不可用。⚠️ 与拍板结论无关,⛔ 不要因为排不出棒而认为形态没定;库修复后按本条与设计件 §十 直接排。 下一步 = B 线(IM 线):按用户口令「处理完成后在做b线」,接续点 = 接续入口_IM线_20260922.md(推荐先做 ①档位重写,它是全部 E2EE 代码的硬前置)。
  • ✅ 2026-09-23 20:5x|carbon 线「两问」答辩(纯文档 · 零代码 · 未动 47/106) —— 触发 = 用户口令「看插件会话的问题反馈 dsh-plugin-carbon/对接文档_carbon插件-平台侧两处阻塞_20260922.md」⇒ 读 carbon 线回执 §H–§K,在该对接单追加 §L 答辩,逐条裁定: ① 凭据落点 → 迁 D4 目录(<userRoot>/home/.dsh/plugins/dsh_plugin_carbon/mcp.json;判据 = 清理单元一致 / 一处约定 / 暴露面不变 ⇒ 不触发 R5);并将目录名口径定死 = pluginId(去 scope·小写·非 [a-z0-9_] 折 _,与库名 dshs_pl_<pluginId> 同 token ⇒ carbon = dsh_plugin_carbon,⛔ 不是 carbon)。 ② 零数据面插件 ⛔ 不要显式提交空声明:无 dsh.data ⇒ none ⇒ 放行;tables: [] ⇒ invalid ⇒ 状态恒 blocked、永远发不出去;单放一个 dsh.data.yaml 根本不被读取(不自动探测)。⇒ carbon 现状即合规,什么都不用加。 ③ 🔴 自纠(我上一轮写错的一处):§C-2 D4 里的「只能走 SDK 的 im.paths.pluginData()」是错的 —— 该 API 三处零命中(源仓 src/ / 宿主仓 deepseek-harness / node_modules)⇒ 正确姿势 = join(process.env.DSH_HOME, '.dsh', 'plugins', pluginId)(DSH_HOME 由平台注入、必存在),⛔ 删 homedir() 回退(HOME = <userRoot>/ws,而 ws 会被平台按产物清理 ⇒ 回退 = 写到会被清掉的位置)。 ④ 另抓一条(对方没问、但会咬人):跨机投递 —— 用户实例在 worker 上时,管理机直写 <userRoot>/home/… = 静默空操作(user-fs.ts:122-124,档案 138 §五)⇒ 投递须落在 hostIdFor(userId) 那台机。⚠️ 平台侧无「插件凭据投给用户」通路(home 白名单仅 3 个裸文件名)⇒ 凭据仍带外投递;要自动化须立项,建议并进 worker /plugins/apply 同一条腿(零新增入站口)。 文档落笔:DB-03 新增 §六·补 插件文件落点与凭据投递(8 行口径表 + 现状缺口)|交接单 ①…md 三处同步更正(状态行「S5-b 口径已更正」· §四 路径段「SDK 口不存在、改 DSH_HOME 拼法」· §五 S5-b 五条改写含 pluginId 口径/凭据同目录/跨机/零数据面)。 ⛔ 本棒零平台代码改动、⛔ 未动 47 / 106、⛔ 未 commit / push。 下一棒 = ⛔ 未登记(本棒为即席应答、非接续棒;且 B 线(IM 线)第 15 棒正在跑,按用户口令 A→B 的顺序,本线不与 B 线并行抢资源)。⚠️ 本线两项可排(均非阻塞):a) 跨机可见性回流的执行棒(形态 D 已拍板 2026-09-23 18:3x ⇒ 按用户级「等拍板」纪律,条件已满足、随时可排);b) 可见面开关 DSHS_SHARED_CATALOG_PUBLIC 的开启决策(红线门禁 · 待用户拍板)。
  • ✅ 2026-09-23 21:3x|插件对接文档落库 + 仓库清理清单(纯文档 · 零代码 · 未动 47/106) —— 触发 = 用户口令「需要一份插件对接文档,给插件开发会话按照文档规范开发插件」+「放到 D:\github\dsh_shenxian\dsh-server-docs 中长期维护」+「清理这个文件夹中过时和临时以及不需要的文件」。 ① 新规范(长期维护 ⇒ 落文档库、不带日期) ⇒ dsh-server-docs/规范/08-插件开发与对接规范.md:§0 一句话 / §1 开工四问 / §2 包形态 / §3 数据面(含「零数据面 ⛔ 不要写空声明」+ 声明唯一入口澄清)/ §4 文件落点 D4 / §5 投放链路(谁做什么)/ §6 错误码表(判归属) / §7 提包前自测清单 / §8 端侧边界 / §9 回报格式 / §10 别做清单 / §11 变更记录。INDEX.md §一 已加一行索引(「写一个业务插件」→ 指向 08)。 ⚠️ 该件顺带纠正一处口径分叉:DB-03 §三 与平台回复里的「在 im.data 上声明表」是运行时对象的措辞;声明的存放位置只认 package.json 的 dsh.data.schema(内联对象或指向包内文件),且不自动探测包内 dsh.data.yaml(源码 src/db/plugin-data/schema.ts:701-746 现读)。 ② 仓库清理(D:\github\dsh_shenxian) ⇒ 交付物/清理候选清单-dsh_shenxian-20260923.md(只读扫描报告):P0 明确垃圾 9 项 / 2.5 MiB(dsh-server-docs/scripts/__pycache__、_tmp_seq40/*.out)· P1 疑似一次性 91 项 / 2.6 MiB(_tmp_seq40/(36) · _tmp_seq24/(8) · poc/**/*.tgz 20 个旧版本 · _中间产物_待清理/ · tmp-e9*.txt · tmp/seq46s0/)· §四 假阳性 10 项(⛔ 别删:scripts/overlay-probe.cjs 等 6 个在册工具、archive/ 存档、过程档案、poc/carbon-mcp-probe/(carbon 线归属)、.workbuddy/lock-hook.log(hook 正在写))· §五 五批次处理顺序(每批 ≤10、跟踪文件走 git rm、每批复核)。 🔴 ⛔ 本棒未删 / 未移 / 未改名任何文件 —— 批量删除属红线(用户长期规则「任何可能影响 >10 文件的操作用前必须先出受影响清单并取得确认」+「未明确要求不 commit / push」)⇒ 先出清单、等逐项点名确认。 ⚠️ 遗留(不阻塞):新增规范尚未进全文检索(docs-manifest.json 未重生成;生成器 = dsh-server-docs/scripts/docs-manifest.py,⛔ 本棒未擅自跑生成物)。 下一棒 = ⛔ 未登记(同前:即席应答 + IM 线第 15 棒在跑)。
  • ✅ 2026-09-23 21:5x|仓库清理执行(用户口令「按照建议清理」· 先备份后删 · 零代码影响) —— 备份 ⇒ 归档/dsh_shenxian-清理-20260923/deleted-20260923.tar.gz(1.23 MB / 36 条目 + MANIFEST.txt)。实删:批3 = 26 个 POC 历史 tgz(1.15 MiB) + 批4 = _中间产物_待清理/(5 文件) + 批2 = 2 项;批1/批2 的多数目标在动手前已被清掉(非本次动作,备份包可证);P0 类残留复扫 = 0。守卫全绿:.workbuddy/lock-hook.log(hook 在写)/ scripts/overlay-probe.cjs(在册探针)/ poc/carbon-mcp-probe/(carbon 线)/ poc/im-conversation-tabs/(IM 线)健在,备份包内 0 条他人路径。⛔ 未 commit / push。回报已落 交付物/清理候选清单-dsh_shenxian-20260923.md §六。
  • 📌 用户问「仓库根 docs/ 有什么用 / 何时建 / 要不要留」—— 判定 = 全部保留(2026-09-23 21:5x 取证):docs/ = 仓库自带的 7~8 篇安装向文档(blueprint / deployment / hard-isolation / domain-config / troubleshooting / k8s-deploy / k8s-deployment / architecture),建于 09-13~09-16(项目最早文档批次);README.md:5 明文「docs/(本仓库内)= 仓库自带 7 篇 …… ⛔ 不要往这里写我们的改造记录」+ README 表格逐篇索引 ⇒ 职责与 dsh-server-docs/(平台文档库)分离。其中 k8s-* 两份对应 2026-09-15 已下线的模式 B,但 README.md:55 明写「保留作为未来可选路线」⇒ 属有意留档,不列可清理项。 ⚠️ 同时澄清「仓库只有代码和文档」不成立:实存非源码内容 = lib/(构建产物 3.5 MiB,服务器跑的就是它)· node_modules/ · .workbuddy/(钩子日志,正在写)· config/ · poc/(各线 POC 源码)· deploy/ assets/ .github/ · dsh-server-docs/(文档库,含 scripts/ ops/ skills/ archive/ tmp/)· 根级 Dockerfile* cordis.patch.yml tsconfig.json package*.json mksess.cjs 等 ⇒ ⛔ 均在用,不属可清理项。

§1 用户原话(目标,⛔ 不得改写、不得替拍)

「插件由admin上传后,应该是建独立库据库进行对接(需考虑更新插件的情况),然后分发到服务器worker,或端侧设备上,任何登录用户都可以访问才对」

拆成四条判据:

  • P1 admin 上传插件 ⇒ 建该插件独立的库并完成对接
  • P2 插件更新的情形要有定义(升级后库/表/元数据/存量数据怎么办)
  • P3 分发的终点面 = 服务器 worker + 端侧设备(不止 Manager 本机)
  • P4 任何登录用户都可以访问

§2 现状(本会话已核实 / 已定稿,执行棒复核时只读)

# 事实 来源
1 一插件一库已拍板:库名 dshs_pl_<pluginId>,全部建在同一个 PG 13.23(47 · 127.0.0.1:15432);库内归属列 user_id / room_id 强制 定稿 dsh-server-docs/架构设计/数据-分库与权威存储架构.md;2026-09-22 用户拍板
2 插件数据面实现为零:47 的库内 13 张表全是内核表,p_* 零命中;src/db 无对应模块 2026-09-22 实测
3 分发现状 = admin 导入候选池 → 用户在实例「功能管理」自助启用;安装动作 = 实例内 pnpm add file:<tgz> src/web/routes/business-plugins.ts:625-651
4 跨机那条腿是断的:/api/dsh/restart 的 handoff 已停写(只留日志)⇒ 装不到 worker 上实例 src/web/routes/dsh.ts:149-175(本会话核实)
5 端侧(桌面壳 / 设备)没有插件投放通路,目前只有"设备登录接入" 序㊻ 范围
6 版本一致性已有档案:候选池同名替换 + 版本对账 调整方案/144-插件规模化投放与版本一致性.md
7 多节点投放与数据面已有档案:路 A / 路 B、worker 自举、目录权限 711 调整方案/147-多节点形态下的插件投放与数据面.md

⚠️ 与既有硬说明的关系:记忆里那条「投放业务插件只有一种方式:admin 导入候选池 → 用户自助启用(不铺 profile)」说的是启用语义;本线的 P3 是包分发面(把包铺到各节点 ≠ 替用户铺 profile)。规划棒须把这两层分清,⛔ 不要当成"推翻既有说明"。


§3 技术方向 · 已拍板口径(用户原话,2026-09-22 13:2x,⛔ 不得改写)

「c 全员默认可见,需自己开通,服务器上所有用户共享只读插件库,不会每个用户分发一份,使用中数据入库,文件保存到用户插件文件夹下对应插件名称的路径中」

拆成四条硬口径(规划棒 ⛔ 不得自行改档):

  • D1 可见性:候选池里的插件对所有登录用户默认可见;开通由用户自己点(沿用 /api/plugins/mine/apply)⇒ ⛔ 不采用"默认安装到所有实例"
  • D2 包共享:服务器上一份只读插件库供所有用户共享,⛔ 不给每个用户复制一份
  • D3 数据:使用中产生的数据走库(按插件分库 dshs_pl_<pluginId>,见 §2-1 定稿)
  • D4 文件:用户侧文件落 「用户插件文件夹 / <插件名称> / …」(目录真身须现场核实,见 §5-3)

📌 2026-09-22 16:3x 补(只记实况,⛔ 未改上面口径):两条待拍板已闭合 —— ① 共享包库版本策略 = 只保留最新一版(用户原话「1 B」)② D4 落点 = <userRoot>/home/.dsh/plugins/<pluginId>/(口径已对齐:用户说的就是平台用户自己的目录;落点由规划棒自决,可推翻)。详见交接单 §4.1 D-i / D-j 与 §4.2 注;执行棒以交接单 §五 为准。

技术方向(可推翻,逐条对应上面):

  1. 包分发:admin 上传后由平台/worker agent 铺到每个节点的共享只读包点;实例侧只引用、不复制。
    • 首选实现 = 共享只读 pnpm store(节点级 store-dir,o+rX;node_modules 走硬链/符号链接)⇒ 实体一份、链接多份,天然满足 D2;注意硬链要求同一文件系统。
    • 备选 = 解包到共享目录后 symlink 进 profile。
    • ⛔ 禁止把 tgz 实体复制进各用户 profile(这正是 D2 明令否掉的形态)。
  2. 不依赖 handoff:包已在节点本地 ⇒ 安装动作不需要"重启后执行命令"那条链(跨机 handoff 已停写,见 §2-4)⇒ 也⛔ 不必给登录用户开命令口。
  3. 建库是平台侧动作(不在实例内):上传钩子里 CREATE DATABASE dshs_pl_<pluginId> + 迁移登记;粒度沿用定稿(默认独立 database,schema 档留作备选)。
  4. 端侧先只做可见性:端侧设备访问同一个平台 API ⇒ 只管"访问得到",不要求端侧本地装包。

§4 P4 粒度:已拍板 = 候选 C(2026-09-22 13:2x)

原三案存档(供追溯):A 全员默认安装 | B 逐用户自助启用 | C 折中。

定案 = C,且用户把 C 的语义收窄为「全员默认可见、需自己开通」:

  • ⛔ 不采用 A 的"默认安装"(不把内存/磁盘代价摊到所有实例)
  • ⛔ 不加 admin 侧的"默认启用"标记(用户未要求,开线棒提过,已按用户口径去掉)

⇒ 规划棒:这一项不再出候选,只管把 C + §3 的 D1–D4 落成可执行方案。


§5 下一棒要做什么

🔄 2026-09-23 更新:本节原有内容(规划棒口径)已由规划棒①执行完毕(交接单已产出并跑完执行棒①的 S1/S2)。当前执行依据改看 §0 最新一行 + 下方「⏭️ 本轮动作」。

⏭️ 本轮动作(第 9 棒 · 规划棒 · ✅ 已收官 2026-09-23 10:0x)= 跨机可见性状态回流(缺口立项)

✅ 第 9 棒已收官(设计件 交付物/跨机可见性状态回流-设计件-20260923.md + 三门基线复验全过 + 交接单 §五 S6 已知边界登记,见 §0 2026-09-23 10:0x 行)⇒ ⛔ 不要重做。 ✅ 第 8 棒已收官(部署 47/106 + S5-E ①–⑤ 全 PASS + S6-E ①③ PASS/② 只记事实 + 修掉 pg_dump env 真实缺陷,见 §0 2026-09-23 09:5x 行;落地件 交付物/S5-S6真机E2E验收-20260923.md)⇒ ⛔ 不要重做。 ✅ 第 7 棒(S5-a + 迁移前备份 + S6 三段式 UI)、第 6 棒(S3 跨机装配)、第 5 棒(三份文档回写)、第 4 棒(建库口接线)同样已全部落地 ⇒ ⛔ 不要重做。

⏭️ 下一棒(⛔ 未登记 —— 等用户拍板):

🔴 接续点 = 用户对「跨机可见性回流的通路形态」拍板(候选 A 主动拉/B 共用台账/C worker 上报,优缺点见设计件 §三)。拍板到手后,排**「跨机可见性状态回流」执行棒**。 ⛔ 本轮不登记下一棒:拍板项未定 ⇒ 按用户级记忆「要用户拍板的,等拍了再新建接续会话」——先判"是不是要拍板",命中即停,⛔ 不得因"候选排序看起来有优劣"就自决登记。 ✅ 已解决的待拍板项(装配协议)见本节末「⚠️ 已拍板待落地」 —— ⛔ 该条退出待拍板清单,拍板前禁令随之解除(禁令仅针对"未拍板就改",不针对落地)。

⛔ 执行棒开工前仍须遵守:⛔ 不开可见面开关 DSHS_SHARED_CATALOG_PUBLIC · 不做跨节点内容分发(需单独立项)。

要求:判据给「命令 + 期望输出 + 退出码」。

⚠️ 已拍板待落地(用户 2026-09-23 拍板 · ① ② ④ 已落地 ✅,③ 已实测撤销)

🔴 装配协议 = 采用 link:(推翻原 D-h file:)。用户原话:「按照建议处理」。 落地范围(4 处)现状: ① ✅ 已落地 —— src/supervisor/plugin-assembly.ts 新增 depRefOf()(file: → link:)+ business-plugins.ts 4 处 fileRef 同步;npm run build rc=0 | npm test = 450/448 过 / 0 败 / 2 跳过 | check-layering ✅ 无新增违规。两机已部署(47 /opt/dshs + 106 /opt/dshs-cluster,md5 逐字一致 fe5162ec…/ed507b7a…)+ restart 完成(门户 200 · worker 401)。 ② ✅ 已落地 —— 交接单该文档 file: 描述已改(含 §9.3 整节改写 + 新增 S3-E 真机复验段)。 ③ ⛔ 已撤销(实测证伪,⛔ 别再做) —— 全平台扫描:带依赖的实例只有 1 个,5 条依赖全是 file:…tgz(.tgz 型 0 条目录型),且只有 1 条能在共享层找到对应目录 ⇒ 原「把 4 条改写成共享层引用」会让 4 条依赖悬空 ⇒ 实例崩。成因 = 那 4 个包是平台自研核心组件、从不进共享层。⇒ 迁移在真实数据上不成立,等这些包进共享层再议。详见交接单 §9.3。 ④ ✅ 已落地 —— 47 真机复验(applyProfileChanges 真实入口):profile 写入 link:/var/lib/dshs/bundled-plugins/dsh-plugin-mcn-suite ✓|node_modules/<pkg> = 软链 ✓|软链目标回溯直达共享层(非复制)✓|软链本体 54 字节 + .pnpm 8.0 K ✓|用户身份下可读 ✓|反证 plugin_bundle_missing 原子抛错 ✓。对照 file: 的 6.5 MiB / 116 文件 ⇒ 量级差约 1000 倍。探针用户已回滚(dependencies 回空、软链已移除)。 ⚠️ 残留一条待补(不阻塞):106 侧跨机那条腿 —— 106 上当前无带插件的用户实例可复验(其用户 profile 无依赖),等 106 有用户启用插件时按 S3-E ④⑤⑥ 原命令补一次即可。装配实现两机共用同一份 md5,不因此判 S3 未完成。 ⚠️ 跨机前提:软链目标必须是两机同路径的共享层(现为 /var/lib/dshs/bundled-plugins/<flat>,两机同名 ✓);⛔ 若将来两机路径分叉,link: 会比 file: 更脆 —— 已写进 depRefOf() 注释。⚠️ 注意 /opt/dshs(47 代码根)与 /opt/dshs-cluster(106 代码根)两机不同名不影响 link: —— 共享层路径才是 link: 的目标。

⏭️ 本线后续(⛔ 本棒不预登记队列)

  1. ✅ 跨机可见性状态回流 —— 形态已定稿(2026-09-23 18:3x 用户拍板 = 形态 D「按需定向拉(主)+ worker 变更上报(辅·后置)」) —— 用户原话「可以 worker 上报,具体要管理那个用户的时候 manager 针对性的去查」。原 A / B / C 三候选作废(§三 仅存为决策过程)。设计件 §十 已整节改为定稿(10-1 形态 / 10-2 关键发现 / 10-3 两条硬约束 / 10-4 五步落地轮廓 / 10-5 四条真机验收)。 🔴 关键发现(写出这条是为了让下一棒不用重查):src/supervisor/leased-spawner.ts:144-155 的 fenceOnAgent() 已经实现了完整三步定位链 —— findUserInstance(userId,'main') 拿 hostId → options.agentFor(hostId) 拿 {agentUrl,token} → post('/fence', body, target) 带 token 定向 POST 到那台 worker ⇒ 主路径只需把目标端点从 /fence 换成新的只读口(建议 /profile-bundles)。⇒ 零新增监听口 · 零新增凭据体系 · 零广播,且天然覆盖 reconcile(查的是对端现算真值,不是历史快照 —— 这正是原 B 案的死结)。 ⛔ 两条硬约束:① 新端点必须带 token(与 /fence 同级)—— ⛔ 绝不可挂到免凭据的 /healthz 上(那等于开匿名任意读口);② 查不到该 userId 必须返回 not_here,⛔ 不等于"零插件"、⛔ 不许把"未知"显示成"未启用"。 落地位置:src/worker/agent.ts(新端点)|src/supervisor/leased-spawner.ts(复刻定位链)|src/web/routes/business-plugins.ts:1648-1670 的 /mine(先判本机 ⇒ 本机直接读盘零网络,非本机才定向查)。 ⚠️ 下一棒 = 待排(原记「automation 不可用,见 §0 末行」—— 🔻 该前提已于 2026-09-23 19:4x 作废,见本节末「状态更正」)—— 本项未开工,⛔ 不要当成已落地。
  2. 跨节点内容分发(后续棒 · 需单独立项)—— 平台侧把共享层铺到各节点的通路;第 8 棒 106 的共享层是手工同步的。
  3. 可见面开关的开启决策(红线门禁 · 待拍板)—— DSHS_SHARED_CATALOG_PUBLIC 开与不开会让"用户能看见什么"变化 ⇒ 需用户拍板后才开。
  4. 装配协议 link: 落地 ✅ 已完成(2026-09-23) —— 见上方「⚠️ 已拍板待落地」;③ 存量迁移已实测撤销。
  5. 🔴 E 单(im-conversation-tabs)的线上可见面投放 —— 此前无归属,2026-09-23 19:4x 由 IM 线第 12 棒显式登记到此(登记依据 = 接续入口_IM线_20260922.md 第 136 行的收口要求)。
    • 是什么:IM 线 E 单的 UI 落点(用户已拍板 C 案 = 实例内 dsh 会话页 tab);代码面已完成(poc/im-conversation-tabs/)。
    • 为什么以前没人做:本入口此前对 im-conversation-tabs 零命中 ⇒ IM 线以为"投放线会接力"、投放线没登记 ⇒ 两不管。
    • 🔴 硬阻塞(⛔ 未解,先别投):IM 线查出一处 P0 协议不一致 …… ✅ 已解(2026-09-23 19:5x,IM 线第 13 棒) —— 客户端已对齐平台的 op 制(出站收敛到 subscribe / ping / resume,入站 switch (msg.op) 全量 9 分支),并新增两侧源码机械对账用例 E-14 守着;⛔ 不再有 type: 出站帧。
    • ✅ 投放已执行(2026-09-23 20:2x,IM 线第 14 棒):按本线既有流程走完 —— admin POST /api/plugins/business(http=200,池 4→5,trustedOverride:false)→ POST /api/plugins/business/share(http=200 action:"created",fileRef = link:/var/lib/dshs/bundled-plugins/_dsh-local_im-conversation-tabs)→ 回滚素材 /opt/dsh/backups/plugins/_dsh-local_im-conversation-tabs/0.1.0.tgz;审计 audit_log id 360 / 361。⛔ 未铺 profile、未 commit、未改动本线任何代码。⚠️ 「共享层发布」这一步是 link: 装配协议的必需项(只进池不发布 ⇒ 用户启用撞 plugin_bundle_missing)—— 本线后续给任何插件投放时都要带上这一步。
    • 📌 归属 = IM 线自行认领完结(2026-09-23 20:2x 第 14 棒):投放的可自动部分由 IM 线做完;剩余唯一未闭合项 = 用户在实例「功能管理」启用(用户动作,属投放语义),操作单 ⇒ E:\ProgramData\AIProject\aliyun-dsh-server\交付物\IM群组-ui投放与线上取证操作单-20260923.md。⇒ 本线不再有该条的待办,⛔ 不要重复认领。⚠️ 一条留在本线名下的关联缺口:目标实例若在 106,其共享层缺该包 ⇒ 与本入口 §5-2「跨节点内容分发(需单独立项)」是同一件事,操作单里给了手工同步命令。

📌 状态更正(2026-09-23 19:4x,IM 线第 12 棒顺带核对 ⇒ ⛔ 未替本线排棒):§0 末行「automation_update 返回 file is not a database ⇒ 全机 automation 功能不可用」的前提已消失 —— 宿主活动库已由 app 自动重建(2026-09-23 18:55;坏库改名 workbuddy.db.corrupt-2026-09-23T10-55-47-845Z 留档,代价 = 历史 automation 记录全丢)。本会话实测 mode=list / mode=create 均可用 ⇒ 本线「下一棒」可以正常登记了。⚠️ 排棒时机仍由本线收口时自行决定(⛔ 不预登记队列)。


§6 开工校验(新会话先跑这三条)

  • 口径门禁:本入口 md5 见启动棒 prompt(开工时现算比对;不符 ⇒ 先读最新版再动手)
  • 源仓 HEAD:git -C D:/github/dsh_shenxian log --oneline -1(以实跑为准)
  • 相关口径复核:/etc/dshs.env 无 DSHS_ENABLE_PATCH(47 / 106 于 2026-09-22 实测)—— 本线 §3.2 正是为绕开它而设计

⛔ 不要重做:carbon 线两处阻塞的定位(memory 10:0x 节已判完);一插件一库是否分库(已拍板,见 §2-1)。