# 2026-09-20 ## 04:3x · IM 群组对话 —— 需求拍板 + 规划档案 142 落盘 - **用户拍板 B 方案**(人 + Agent **一次做齐**),并给出**两条决定形态的语义**: ① **群聊消息 = agent 的上下文**(不是只看到 @ 它那一条); ② **agent 两种形态** —— **能力机器人**(挂功能区、**占独立成员位**、被 @ 才答)/ **组员代理**(组员开「待应答」后 @ 组员 = 其 agent 代答;**组员与 agent 是一个整体 ⇒ 不占独立成员位**)。 - **追加硬要求(平台化)**:IM 群组**不是单一场景功能** —— 原话「不同插件都要能接入,比如**协作办公插件群**、**mud游戏群**、**跑团群**等」 ⇒ 形成**第二个关键判断**:内核只做通用地基,**七个扩展点**(房间类型 / 消息载荷 / 成员角色 / **发言规则** / 能力机器人 / 事件订阅 / 房间内 UI)下沉到插件; 配 **三条平台保护**(插件不直连 DB · 插件故障不拖垮房间 · 能力吃预算)+ **两档传输**(**聊天档** 按 107 口径 / **实时档** 按 108 游戏口径 —— 两档不是同一套参数)。 ⚠️ 跑团要**回合制发言**、MUD 要**低延迟**,均与自由并发聊天不同 ⇒ **发言规则必须是插件可定义的**,否则第一类新场景进来就要改内核。 - **产出**:`04-调整方案/142-IM群组对话-需求基线与方案.md`(规划态)+ `INDEX.md` 登记行;`docs-audit.py` **RC=0 / 无 P0**;新档 CR=0(纯 LF)。 - **取证结论(跨会话可复用,勿重查)**:`src/` 应用层 `room|chat` **零命中**;`schema.ts` 11 个迁移**无消息 / 房间 / 成员表**; 用户间通道**故意隔离**(`src/net/relay/server.ts:462`);可复用 = relay ×2 / `DIAL` / `PRESENCE` / 一机一钥 / 网抽象 `ops`·`u:`。 - ⏳ **未拍板**(档案 §六):**首批场景优先级**(办公群 / MUD / 跑团)· UI 落点(门户面板 vs 独立页)· 房间上限与发言预算数值(待压测标定)· 插件扩展开放边界 · E2EE 是否本期。 - ⛔ 本轮**零代码改动、未动服务器、未 commit / push**;全局执行锁**已释放**。⏭ 下一棒 = 规划细化(出可执行交接单),automation 一次性排 04:41。 ## 04:5x · IM 群组对话 —— 规划细化:五份可执行交接单落盘 - **产出(全部在 `dsh-server-docs/交接单/`,纯 LF)**: `IM群组-A-房间内核与DB.md`(10,024 B)· `IM群组-B-平台API与用户端IM-WS通道.md`(8,409 B)· `IM群组-C-Agent接入插件.md`(9,283 B)· `IM群组-D-插件SDK与扩展点契约.md`(10,052 B)· `IM群组-E-会话界面UI.md`(8,033 B); `交接单/README.md §一` 增补 5 行 + 1 段串行建议(`git diff --numstat` = **8 / 0**,未触碰既有行)。 - **验收**:`docs-audit.py` **RC=0 / 无 P0**(改前基线同为 RC=0;对比无新增问题);新档 `CR=0`。 - **串行建议**:**A → B →(C / D 可并行)→ E**;除 A/B 前置于后三者外,C/D/E 文件不重叠,但**并行度仍受全局执行锁约束**。 - **本轮新取到的锚点(跨会话可复用,勿重查)**: ① `src/db/schema.ts` 迁移**到 v11 为止**(`MIGRATIONS` 第 476 行起)⇒ IM 新表 = **v12**,且**双后端**(`sqlite` + `pg` 各一份 SQL); ② 平台路由 = `src/web/server.ts:58–68` 集中 import **11 个** `routes/*.ts`,**无 `im.ts`**; ③ **全仓只有 1 处 WS upgrade 处理点** = `src/supervisor/proxy.ts:718`(per-user 子域隧道)⇒ B 单必须**在同一 upgrade 回调里按路径分流**,⛔ 不改隧道语义; ④ 🔴 **前置缺口(C 单第一件事)**:`poc/business-plugins/lib/index.js` **仅 21 行**、server 侧**不触达平台**;能力全在 `lib/client.js`(**4,001 行**)且靠**浏览器 session cookie** 跨子域 ⇒ 实例内「**服务端拨出**」**缺一条凭据通道**(已在单里定为「平台生成 per-instance token → orchestrator 注入 env `DSH_IM_INSTANCE_TOKEN` + `DSH_PLATFORM_IM_URL`」,实例重启即换); ⑤ 实例 env 注入块 = `src/supervisor/orchestrator.ts` 第 **760–780** 行一带;测试布局 = `test/*.test.mjs` + `node --test`(构建产物 `lib/`)。 - **未决项(⛔ 未替用户决定,已逐单显式标注)**:**UI 落点**(门户新面板 vs 独立页,档案 142 §六-1)· **首批场景优先级**(办公群 / MUD / 跑团,§六-5)· **E2EE 是否本期做**(§六-3,会改协议形态;我在 D 单给了倾向 A=本期不做并列了优缺点)· 房间上限与发言预算**数值待压测标定**(§六-2,单内先给保守初值:房 2000 / 每 agent 每 10 分钟 10 条 / 静默 30 s)。 - ⛔ 本轮**零代码改动、未动 47/106、未 commit / push**;全局执行锁**已释放**。⏭ next = 用户拍板(UI 落点 / 首批场景)后按 A 单开工。 ## 08:1x · IM 数据面追加裁决(DB 选型 · 缓存队列 · 对象存储)→ 已写进 A 单 §九 - 用户追加四问(多插件接入表名隔离 / 多领域·项目·会话组 / 主从与负载均衡 / 缓存与队列预留),随后再补一问(**IM 会话产出各类文件 ⇒ 考虑接对象存储桶**)。 - **实测取数(本轮新取,跨会话可复用)**:`package.json` 依赖里与数据相关的**只有 `better-sqlite3` + `pg`**;`src/` 里 **0 处** `redis` / `ioredis` / `bullmq` / `amqplib`/ cache 抽象 ⇒ **缓存与队列是空地,不是"已有待改造"**。 - **裁决(全为技术项,自决 · 已落 `交接单/IM群组-A-房间内核与DB.md §九`,标注为「设计约束,冲突时以本节为准」)**: ① 插件表隔离 = **命名空间前缀 `p__*` + 内核 API**,⛔ 不给插件直连 DB(前缀只解决物理隔离与归属); ② 多领域/项目/会话组 = **同一套房间/消息模型 + 维度字段**(`owner_scope` · `room_type` · `thread_id` · `project_id`),⛔ **不按领域分表**(否则新场景就要改内核); ③ 主从/负载均衡 = **保留 PostgreSQL**(既有 PG13 + `src/db/` 双后端可切)> MySQL > SQLite(**不支持主从**);⚠️ **读写分离按数据分类**:元信息读可走只读副本,**消息读必须走主/sticky**(否则"刚发的消息看不见",违反游标补拉语义); ④ 缓存与队列 = **预留端口、不预置进程**:队列先用 PG(`SKIP LOCKED` + `LISTEN/NOTIFY`),Redis 仅在「跨机扇出/presence 广播」实测成热点后引入(**单实例 ≤128–256 MiB** 硬前提,宿主内存本就紧:实例硬顶 1024 MiB / 宿主 1870 MiB);端口 = `src/im/cache.ts` / `src/im/queue.ts`; ⑤ **文件与对象存储** = **桶存内容、PG 存元数据**(`files` 表带 `sha256`/`bucket_key`,⛔ 大对象不进 PG);**S3 兼容**(MinIO 自建或 OSS/COS 兼容端点)不锁厂商,端点/密钥走档案 140 的 `config/platform.env`(不入库、默认中性);**上传 = 预签名直传桶(平台不经中转)**,**下载 = 短 TTL 预签名(≤5 min)**、需审计时走 `/api/im/files/:id` 代理;消息 payload 只存 `file_id`(保住扇出与两档传输口径);生命周期 = 内容寻址去重 + 桶 lifecycle(⛔ 不做同步删);权限**复用房间 ACL**;插件产出物**走同一 files 面**(⛔ 不各自建桶/凭据);转码抽帧**不在内核**(走后处理队列);端口 = `src/im/files.ts`,首版单桶多前缀 `rooms///`。 - **立单时机**:文件面依赖 A 的 ACL 与 B 的接口面 ⇒ **等 IM 主链 A→B 跑通后另立 F 单**;本轮只把约束写死(⛔ 未在 A 单里顺手实现,未改五单范围)。 - 验收:`docs-audit.py` **RC=0 / 无 P0**;A 单 15,828 B / CR=0。⛔ 仍**零代码改动、未动服务器、未 commit**。 ## 08:2x · IM 数据面追加(插件如何建表与管数据)→ 已写进 **D 单 §九** - 用户追问「**插件如何建表以及访问和管理数据**」⇒ 契约落在 **`交接单/IM群组-D-插件SDK与扩展点契约.md` 新增 §九「插件数据面契约」**(A 单 §九-1 加了指针互指;D 单那行台账也补了说明)。 - **裁决(技术项,自决)**: ① **先判落点再建表**(沿用集群化分层判据):跟房间走的业务数据 ⇒ **平台库** `p__*`;只属于某用户自己的 ⇒ **实例 home(走 `UserFs`)**;本机运维 ⇒ Worker 本地库。⛔ **同一份数据不双写**(双写=脑裂)。 ② **建表 = 声明式,⛔ 零裸 SQL**:中性类型集合(`text/integer/bigint/real/boolean/json/timestamp/uuid`)由内核翻译成 **sqlite + pg 两套 DDL** ⇒ 同时保住「双后端可切」与「插件不直连 DB」;内核建表时自动补前缀/`room_id`/审计列。⛔ 禁自定义类型、触发器、存储过程、跨前缀 JOIN。 ③ **迁移只增不减**:只允许加列(带默认值)/加表/加索引;在**启用插件时**执行,任一步失败 ⇒ **拒绝启用**(不做半迁移)。 ④ **访问走内核数据 API**(`table().insert/update/delete/find/count` + 游标分页):room-scoped 表**自动注入房间 ACL**(复用 `canSee`);跨前缀/内核表**只能经内核 API**;事务仅限同前缀多表;写与"刚写就读"**走主库**。 ⑤ **管理**:三重配额(总行数/单行字节/每房行数,超限给明确错误码,⛔ 不静默丢/截断)· 插件写吃**与 agent 同族预算** · DDL 与超阈值写记入内核表 `plugin_data_audit` · **卸载默认保留 30 天**,`DROP TABLE` 属不可逆 ⇒ 必须 **admin 显式确认 + 先出清单**(⛔ 不做"卸载即删")。 ⑥ 数据面异常(超时/超配额/迁移失败)⇒ **降级为普通聊天**,房间消息流不受影响(142 §3.6 保护 2)。 - **D 单新增验收断言 8–13**:零裸 SQL / 双后端可建 / 越权拒绝 / 房间 ACL 为 0 行 / 迁移拒绝删列改类型 / 卸载保留与 DROP 确认。 - 验收:`docs-audit.py` **RC=0 / 无 P0**;A 单 16,017 B、D 单 14,468 B、README 164 CR(**均为纯 LF 新档 / 既有 CRLF 未变**)。⛔ 仍零代码改动、未动 47/106、未 commit / push。 ## 21:5x · 「数据库专区」成稿(**抢锁失败 ⇒ 未落文档库,全部暂存工作区**) - 🔴 **抢锁失败(R9 停手,未碰文档库任何文件)**:`交接单/.exec-lock/OWNER` = **`序47执行棒2`**,开始 **09-20 21:27**、`doing` 位为空(与记忆里那支 automation `90049d4c…` 一致)。双证:`handoff-guard.sh --claim-exec` **退出码 1** + Python 直读锁目录。 ⚠️ **环境坑(新增)**:本轮 `bash …/handoff-guard.sh` 的**中文输出变乱码**(UTF-8/GBK),且 **coreutils 间歇缺失**(`head`/`tail`/`grep` 报 `command not found`,PATH 被重置 ⇒ 会误触 WSL 阻断)⇒ **判锁改用 Python 直读 `.exec-lock/OWNER`**(`ls`/`python` 正常,管道到 `head` 不可靠)。 - 🔴🔴 **本轮最重要的实测发现(会误导后续执行者,必须优先修正)**:`src/db/schema.ts` 迁移**已到 v13** —— **v12 = `overlay device ledger (序㊻ 步骤6 / S3)`**(新表 `overlay_devices`)、**v13 = `session/device kind + session cap index (序㊼ S0)`**。 ⇒ **IM 房间三表必须取 v14**;而 `交接单/IM群组-A-房间内核与DB.md` 现写「新增 **v12**」,且其「只读前置 #1」原写「**若已 >11 ⇒ 说明别人先做了 ⇒ 停手回报**」——**这句现在会误判**(v12/v13 是覆盖网络线占的,不是 IM)。 - **成稿(暂存,未入文档库)**:`E:/ProgramData/AIProject/ai1net-dsh-server/tmp/数据库专区_草案_20260920/` `00-专区入口.md`(入口 + 五条判据 + **迁入清单**)· `01-接入指南-各种情形.md`(**六种接入情形**:内核表 / 插件表 / 实例 home / Worker 本地库 / 大对象桶 / 缓存队列)· `02-表结构台账与迭代机制.md`(**v1–v13 台账 + 内核表全集 + 三条硬纪律 + 四步跨版本 + 评审门禁 + 台账维护规则**); 另 `tmp/插件数据面规范_草案_20260920.md`(= 专区 `03-插件数据面规范.md` 的成稿)。 - **迁入清单(锁释放后一次做完,见 `00-专区入口.md` §🔴)**:抢锁 → `mkdir dsh-server-docs/数据库/` 落 4 档(**新目录不需占号**)→ `INDEX.md` 登记 → **修正 A 单(v12→v14 + 改掉"停手回报"那句误判判据 + §五-1 的 `SQLITE_V12/PG_V12`→`V14`)** → D 单 §九 加指针 → **删 tmp 草案(不留双源)** → `docs-audit.py` RC=0 + 纯 LF。 - ⛔ 本轮仍**零代码改动、未动 47/106、未 commit / push**;**未登记后续 automation**(锁在别人手里 + 排 automation 属花钱项,等用户口径)。 - 🔑 **跨会话可复用取数**:`grep -nE "^ \{ version:" src/db/schema.ts` = 版本清单(**现有 13 条**);`grep -noE "CREATE TABLE( IF NOT EXISTS)? [a-z_]+" src/db/schema.ts | sort -u` = 表全集(内核保留名 13 张 + `schema_migrations`)。 ## 22:4x · 迁入做成「一条命令」(锁仍未释放,文档库零改动) - 🔴 **锁竞争第 3 次**:`MCN线0.3.13棒` **自 22:10 持锁至今**(22:39 仍在,已 30 分钟)⇒ 两轮 `--claim-exec` 与一次实跑均**抢不到 ⇒ 按 R9 停手**,文档库**零改动**。 - ✅ **本轮产出 = 可一键执行的迁入脚本**:`tmp/迁入数据库专区.py`(**已三次 `--dry` 全绿**,实测抢不到锁时**零改动退出**并打印 OWNER)。 它一次做完 7 件事:① `mkdir 数据库/` 落 4 档(去掉草案头、加「已落文档库」状态行、LF)② 把 A 单 **v12→v14**(含**改掉「若已 >11 ⇒ 停手回报」那句误判**,改为「**v14 被占 ⇒ 取下一个空号继续,不要停手**」,并写明 v12/v13 = 序㊻/序㊼ 的 `overlay_devices` 等、与 IM 无关)③ D 单 §九 加专区指针 ④ **INDEX.md 二、全量清单 字节级插入一行** ⑤ 打印各档字节数与 CR 数 ⑥ 跑 `docs-audit.py` 打印 RC ⑦ `finally` 释放锁。 - **两个"防坑"设计(写脚本时踩到/主动做的)**:① 版本号**现算**(`schema.ts` 里 max+1)而不是写死 —— 序47 正在改代码,写死必然过时;② INDEX.md 用**字节级插入**(该文件 CR=5/LF=269 混排)而不是整文件 Write 覆盖 —— 遵循 README §三「共享文件禁整文件覆盖」。 - **⚠️ 脚本首个版本曾断言失败**:`\bV12\b` 匹配不到 `SQLITE_V12`(`_` 是 word 字符 ⇒ 无词边界)⇒ 改为无边界 `V12`。**教训:跨 `_` 的标识符不能用 `\b`**。 - ⏭ **下一步 = 等窗口跑那一条命令**:`"" "E:/ProgramData/AIProject/ai1net-dsh-server/tmp/迁入数据库专区.py"`(跑前可先 `--dry`)。最佳窗口 = MCN 棒收口后 **5~8 分钟**(其下一棒按纪律才排)。⛔ 仍零代码改动、未动 47/106、未 commit。 ## 22:5x · ✅ 数据库专区**已迁入文档库**(锁一释放就执行完毕) - **锁窗口**:22:46 复查发现 `MCN线0.3.13棒` 已释放 ⇒ 一键脚本抢到锁 → 落库 → 释放。 - **产物(文档库新目录 `dsh-server-docs/数据库/`,全部纯 LF)**: `DB-00-专区入口.md`(4,835 B) · `DB-01-接入指南.md`(6,588 B) · `DB-02-表结构台账与迭代.md`(6,737 B) · `DB-03-插件数据面规范.md`(9,625 B); A 单已改 **v12 → v14**(含改掉会误判的"停手回报"判据、写明 v12/v13 = 序㊻/序㊼ 的 `overlay_devices` 等);D 单 §九 已加专区指针;`INDEX.md` 二、全量清单 **字节级 +1 行**。 - **最终验收**:`docs-audit.py` **RC=0 / 无 P0**;`INDEX.md` **CR=5 未变**(行尾零扰动);专区四档 CR=0;全局锁**已释放**。 - 🔴 **两条新踩坑(跨会话必记)**: ① **新目录下的文档不能用 `NN-` 文件名前缀** —— `docs-audit.py` 的正则是 `^(\d+[a-z]?)-`(**只看文件名**),`01-/02-/03-` 会被当**档案编号** ⇒ 与 `04-调整方案/01..03` 撞号 ⇒ **RC=1**。正解 = 用**非数字前缀**(本次改 `DB-00…DB-03`),标题里可继续保留序号(`标题号 ≠ 文件名号` 只在**两边都解析出编号时**才比较)。 ② **`\b` 匹配不到 `SQLITE_V12`**(`_` 是 word 字符 ⇒ 无词边界)⇒ 跨下划线的标识符替换**不要用 `\b`**。 - **清双源**:`tmp/数据库专区_草案_20260920/` 与 `tmp/插件数据面规范_草案_20260920.md` 已**移入** `D:/github/dsh_shenxian/_中间产物_待清理/`(⛔ 未直接删,可回溯);工作区 `tmp/` 只留两个脚本:`迁入数据库专区.py`(⚠️ 一次性,重跑会在 A 单断言处安全中止)、`修正数据库专区命名.py`。 - ⏳ **残留小项(未做,等用户口径)**:① `scripts/docs-manifest.py` 机读清单未刷新;② 未 `scp` 到服务器镜像 `/opt/dsh/docs`;③ **未 commit / push**(红线:未明确要求不提交)。⛔ 全程未动 47/106 服务、未改一行代码。 - 📌 **技能待补(下一步做)**:把上述坑①(新目录文档命名与 audit 编号规则)补进技能 `dsh-knowledge-upkeep`(本轮上下文已近上限,未做)。 ## 22:5x · 收尾收官(清单 / 技能 / 服务器镜像) - ✅ **机读清单**:`scripts/docs-manifest.py` **RC=0** —— 文档 **213 份 / 2,234,077 字符**(档案 142 份;分层 hot 6 / warm 61 / cold 50;根级文档 71)⇒ 已含新 `数据库/` 目录。 - ✅ **技能补坑(三处 md5 一致 = `53d73df7`)**:`dsh-knowledge-upkeep` 追加「新增目录的文档命名 vs docs-audit 编号」一节 —— 本机 `.workbuddy/skills/` + 文档库 `skills/` + **服务器镜像 `/opt/dsh/docs/skills/`**(第三处本轮已 scp 并对账)。 - ✅ **服务器镜像**:`/opt/dsh/docs/数据库/`(**新建目录 700**)落 `DB-00~DB-03` 四档 + `交接单/IM群组-A-房间内核与DB.md`、`IM群组-D-插件SDK与扩展点契约.md` 共 **6 档 scp 全部 ✓**;权限 = 文档 600 / `DB-00-专区入口.md` 644(README 类)。 - ⚠️ **`INDEX.md` 未推(主动跳过)**:远端 md5 `8350cbc3` ≠ 本地 `b9c7b056` ⇒ 按 README §三-0-⑤「**防迟到的推送覆盖别人的新版本**」只报不动,**留人工对账**(这是本轮唯一残留动作项)。 - ⏳ 仍**未 commit / push**(红线:未明确要求不提交)。⛔ 全程未动 47 的生产服务(只写了 `/opt/dsh/docs` 镜像目录)、未改一行代码。 ## 08:0x–08:5x · MCN 工作台「点了没反应」+ 用户设置切语言「没反应」→ 已修复并真机验收(档案 143) - 用户:「admin账号dsh会话 点击左侧 MCN工作台入口 没有反应,找到原因修复后把MCN任务会话入口的位置改到MCN工作台 header区域(用图标即可)」+「设置选项中 用户设置里面切换 语言还是不起做用」。 - **两条同源:dsh 0.1.5-rc.1 改了插件依赖的老契约**(不是用户操作、也不是"改不生效"): ① 对话区槽位名 `conversation` → **`main.conversation`** ⇒ 插件 `querySelector('[data-slot="conversation"]')` 实测恒 null ⇒ 面板宿主 `pageHost` 一直 null、portal 不渲染 = **点了没反应**。 ② `ctx.locale.getLocale()` 由「返回 id 字符串」变成「返回**快照对象** `{active,locales,revision}`」⇒ `String(obj).split("-")[0]` = `"[object Object]"` ⇒ 受控 `