Files
admin c1b5e4d966 chore(工作区): 全量入库 + 补齐 .gitignore(以工作区为准)
- 变更规模:新增 514 / 修改 62 / 重命名 155 / 删除 4(归档重组与文档轮次)
- .gitignore 修:`归档/**/db-cwd归一-备份-*/` —— 原规则写绝对层级(归档/db-cwd归一-…),
  目录搬进 归档/配置与备份/ 后**静默失效**,43 MB 的 DB 备份又变成未跟踪
- .gitignore 补:嵌套 git 内部数据(归档/内嵌git-20261008/、归档/skills-git-旧线-20261007/dotgit-原样移出/)
- .gitignore 补:运行态与部署副本(.workbuddy/collab/、.workbuddy/tools/、.workbuddy/.load-pending、.workbuddy/tmp-*)
- .gitignore 补:备份件(*.bak-*)
- 未跟踪文件从 2190 降到 890(其余为 归档/ 归档件与 .workbuddy/memory/ 知识文件,按口径入库)
2026-10-10 23:13:22 +08:00

1137 lines
133 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 工作区日志 · 2026-09-22
## 06:0x–06:3x · 技能加载闸门:扩作用域(B)+ 扩词表(B)+ 清退桌面副本
**用户拍板**:① 「1、B」= 改源脚本扩钩子作用域(一份实现,全部工作区受益);② 「2、B」= 只加三条窄词给 `agent-operating-rules` 一条机制通道。
### 做了什么
**① 源脚本作用域:单值 → 多值 + env 可覆盖**(`dsh-server-docs/scripts/`,两个脚本同形)
- `skill-load-guard.py`:`SCOPE = 'aliyun-dsh-server'` → `_SCOPES_DEFAULT = ('aliyun-dsh-server', 'dsh-ai1net-desktop')` + `_workdirs()` / `_in_scope()`
- `stop-dialog-guard.py`:同款改写,4 处引用(:213 / :509 / :554 / :568)全部换成 `_in_scope(...)`
- 🔴 **形态刻意与桌面副本的 `sync-scoped-guards.py` 生成物同形**(`_scopes()` + `_in_scope()`)⇒ 两侧可互换、副本的机械变换规则不再需要改写源文件
- ⛔ **只比工作区目录名**,不写死绝对路径 ⇒ 换机器 / 改盘符不受影响
- env `DSH_GUARD_SCOPES`(逗号分隔)可覆盖;设为空串 ⇒ 只走默认表
- 校验:两脚本 `裸 SCOPE 残留 = 0` + `ast.parse` OK
**② 词表:12 条 → 17 条,并按命中词分流**
- 新增分组 2(作业规则族):`按规则来` / `按规则做` / `按作业规则` / `遵守规则` / `按规矩来`
- 注入内容**按命中词分流**:命中规则族 → 指令加载 `agent-operating-rules`;命中决策族 → 仍加载 `dsh-decision-method`(原行为不变)
- 两组都附带 `dsh-feature-first`(涉功能判类型时)
**③ 🔴 清退桌面副本挂载(本次最容易被漏的一步)**
- 副本 `E:/ProgramData/AIProject/ai1net-dsh-desktop/.workbuddy/guard/` 的存在理由(源脚本作用域太窄)**已被 ① 消除**
- **实测确认重复**:源脚本与副本在**同一秒各写一条 `HIT`**(时间戳逐字相同 `06:15:41`)⇒ 桌面工作区每轮收到两份相同注入
- 处置:从全局 `settings.json` 的 `hooks.UserPromptSubmit` **撤下 2 条**(4 → 2),其余逐字不动
- 备份:`settings.json.bak-20260922-061649-retire-desktop-replica`(另有手工备份 `-scopemerge`)
- ⛔ **目录本身未删** —— 留作回滚锚点;`guard/README.md` 头部已写「已退役 + 原因 + 备份路径 + 如何重新启用」
- ⚠️ hooks 是**应用启动时快照** ⇒ 需完全重启 WorkBuddy 才生效
### 修掉的真缺陷(自己引入 + 顺带发现)
1. 🔴 **`NameError: self_refresh is not defined`(我自己引入)**:改 `sync-scoped-guards.py` 加 `native_multi` 快路径时,`_HEADER`(含 `self_refresh()` 定义)**只拼在 else 分支** ⇒ 新分支生成的副本调用不存在的函数 ⇒ **副本整个崩掉(RC=1)**。
- 发现方式:副本独立真跑 3 个表内用例**全 FAIL** ⇒ 不猜、直接看 stderr 原文
- 修复:`head` 提到分支外、**两个分支都拼**
2. **变换失配时误报"副本均为最新" + RC=0**:源脚本改形态后 `_RE_DEF` 必然失配 ⇒ 旧逻辑同时打印 `❌ 变换失配` 和 `副本均为最新`(自相矛盾),且退出码 0 让调用方以为成功。已修为「有错误时不报最新」。
3. **同步成功后仍提示"待同步"**:`stale` 是"源变过"的过程标记,未区分"已重建"。已修为**重建成功即不再计入 pending**。
4. **`stop-dialog-guard.py` 远端属主被 scp 改坏**:推送后变 `UNKNOWN:UNKNOWN`(scp 保留本机 uid 197108)。已 `chown root:root` + `chmod 755` 修回。
### 验证(全部真跑,非静态检查)
| 对象 | 结果 |
|---|---|
| 源脚本测试集(7 用例) | **7 PASS / 0 FAIL** |
| 桌面副本测试集(7 用例) | **7 PASS / 0 FAIL** |
| env `DSH_GUARD_SCOPES` 覆盖 | ✅ 生效(设 `mcn-project,StoryForge` 后 mcn 命中、aliyun 落空) |
| 分流正确性 | ✅ 规则族 → `agent-operating-rules`;决策族 → `dsh-decision-method` |
| 表外工作区(`mcn-project`) | ✅ **放行**(两脚本均不注入) |
⚠️ **两个初判 FAIL 是我测试用例的假设错误,不是脚本缺陷**(已如实记录,避免下次重踩):
- ① 钩子有 **300 s 同会话冷却** ⇒ 我复用同一个 sid,第二条被正确拦下 ⇒ sid 必须唯一;
- ② `stop-dialog-guard.py` 在 `UserPromptSubmit` 事件**只留痕不注入**(注入走 `Stop` 事件)⇒ 期望 stdout 非空是错误的。
### 沉淀
- 技能 `agent-operating-rules` **v1.2.0 → v1.3.0**,新增 **§10.2「机制扩面后必须回头清退绕行方案」**(三步判据 + 撤挂载安全姿势 + 三条反模式)
- 核心认知:**修好根因后,为绕开它而建的临时机制会从"补丁"变成"重复"** ⇒ 必须连**挂载**一起撤(只删代码不删挂载 = 两套并存)
- ⛔ 反模式:看到"功能正常"就认为没问题 —— **重复注入不会报错**,只会啰嗦 + 状态互扰
- 三处同步:技能 `SKILL.md` 本机 → 文档库 → 服务器(`/opt/dsh/docs/skills/`,600 root:root);源脚本 2 份推 `/opt/dsh/docs/scripts/`(755 root:root)
- `README.md` 第 39 行登记更新为 **v1.3.0** + 补 §10.1 / §10.2(按**行首前缀锚定**改,⛔ 不按整行文本匹配)
### 现场状态
- 三处 md5:技能 `13a1c124162e623ce33da3415928e26d`(本机 = 文档库 = 服务器)
- 源脚本:`8a9d804bacfb2f8ba38d1473de07e5cb` / `ea4d4b7222955d7d22d33c14b32ead45`(本机 = 服务器)
- `settings.json` UserPromptSubmit = **2 条**(仅源脚本);`PreToolUse` 3 组 / `SessionStart` 1 组**未受影响**
- 全局执行锁已释放
- 桌面 `guard/` 目录 + `_bak-*.py` 保留(回滚锚点)
---
## 06:2x–07:0x · 取证:这么多 dsh 技能,会话能不能准确用 + 后不后维护
**用户问**:「确认实际情况:这么多dsh技能是否会话能准确的使用和后续方便维护」→ 追加要求「结合历史会话,多检查几遍」。
**方法 = 10 层递进取证**(脚本全部落在 `tmp/_keep-20260922/`,可复跑):
| 层 | 查什么 | 关键产出 |
|---|---|---|
| 1 | description 全量 + 探针词重叠 | 12 技能 522KB;探针太宽 ⇒ **假阳性**("文档""规则"必然多命中) |
| 2 | **精提取引号内触发短语** | **零重复、零包含** ✅ ⇒ 第 1 层的"14 个场景重叠"确认是假阳性 |
| 3 | 真实说法 → 命中谁 | **2 个 ❌**:「实例起不来」「装了插件但界面没变化」⇒ **该加载的没加载** |
| 4 | 三处一致性 / 引用 / 版本 / 体量 | 三处 0 差异 ✅;7 处"悬空引用"待判 |
| 5 | 引用上下文逐条看 | **7 处全是假阳性**(插件名 / 工作区名 / npm 包名)✅ |
| 6 | 自然说法覆盖率 | **95%**(62/65),盲区 3 个 |
| 7 | **853 条历史真实原话**做验证集 | 命中 0 = 55.8% / 1 = 33.8% / ≥2 = 10.4% |
| 8 | 盲区二分 + 补漏词重跑 | 补词后命中 0 降到 **42.9%** ⇒ 部分是**我判据漏词**;追问型 154 条(正常)/ 独立型 212 条(多为项目线内容,非方法论) |
| 9 | **用户明确定过的规则**是否接得住 | 🔴 **7 条真盲区**,最典型「只做被明确要求的事」历史原话 **62 次**却无技能命中 |
| 10 | frontmatter 结构校验 | 0/12 有问题(**但我自己改时踩了重复字段的坑**,见下) |
### 改了什么(3 个真缺陷 + 1 处描述词盲区,均为净改进)
1. 🔴 **`dsh-instance-diagnose` 缺「实例起不来」** —— 用户最自然的说法命不中,会**误落到** `dsh-desktop-dev-shell`(后者声明了"dsh 实例起不来")。
⇒ 补触发词(实例起不来 / 挂了·崩了·一直重启 / 报 503·502)+ **与 desktop-dev-shell 双向指认边界**(服务器用户实例 vs 本机源码实例)。v1.1.0 → **v1.2.0**
2. 🔴 **`dsh-desktop-dev-shell`** —— 给「dsh 实例起不来」加「**本机**」限定,并写明作用域。v2.0.0 → **v2.0.1**
3. 🔴 **`dsh-plugin-diagnose` 缺「装了插件但界面没变化」** —— 同因(只有对方声明了同一说法)。⇒ 补词 + 双向指认。v1.2.0 → **v1.3.0**
4. 🔴 **`agent-operating-rules` 补 8 类高频点名场景触发词**(这是最重要的一条):
实测其 description **没有**「只做/明确要求·抢锁·法规·解决问题·妥协·D盘·直入主题·代词·成本·token·积分」**任何一个词**,
而这些恰是用户反复强调、且**已写进本技能正文**的规则 ⇒ **规则写在文件里 ≠ 在正确时机被取用**(与 `skill-load-guard.py` 同族问题)。
v1.3.0 → **v1.4.0**
5. `dsh-feature-first` 补「先做哪个 / 优先级怎么排 / 做不做」+ **补齐缺失的 `last_change` 字段**。v1.8.0 → **v1.8.1**
### 验收(三项全达标)
| 指标 | 修前 | 修后 |
|---|---|---|
| 真实场景撞点(❌ = 命中错) | **2 个 ❌** | **0 个** ✅ |
| 自然说法覆盖率 | 95% | **100%** ✅ |
| 用户明确定过的规则盲区 | **7 条** | **0 条** ✅ |
- 三处 md5 一致性:5/5 全一致(本机 = 文档库 = 服务器,600 root:root)
- frontmatter 结构校验:**0/12 有问题** ✅
### 🔴 自己踩的坑(如实记录)
- 改 `dsh-plugin-diagnose` 时 **old_string 少截了一行 ⇒ 产生两个 `last_change`**。发现方式:改完逐字段清点行结构。已按行号删掉旧行(带断言:第 1 行必须是新版、第 2 行必须是旧版,不符即中止)。
⇒ **教训:改 frontmatter 后必须数一遍字段出现次数**(无人守这个唯一性)。
- 用 `python -c` 改 README ⇒ **反引号被 shell 吃掉**,pattern 匹配 0 行(静默无效果)。改为落盘 `.py` 后一次通过。
⇒ 再次验证 MEMORY 里那条规矩:**`python -c` 先落 .py**。
### ⚠️ 未动(留待拍板 / 属别人 lane)
1. **`dsh-change-workflow` 的 version = `2.9.2-playwright-banned`** —— 非标准 semver(版本号里塞了策略标记)。**已污染下游**:README 登记行现也带此值。倾向改成 `2.9.3` 并把该信息留在 `last_change`;但它可能是别的会话**有意**设的标记 ⇒ **未擅自改**。
2. **`last_change` 字段 12 个里缺 5 个**(architecture-lifecycle / auto-handoff-chain / decision-method / env-bootstrap / knowledge-upkeep)—— 字段规范不一致。⛔ 未批量补("只做被明确要求的事")。
3. **体量不均**:`dsh-opensource-release` 181KB + `dsh-change-workflow` 116KB = **全量 46%**;且 change-workflow 的 `last_change` 堆成 **3000+ 字符**的巨型变更日志("此前…此前…"反复)⇒ 维护成本集中在这两个身上。
4. **独立型盲区 212 条的主题分布**:覆盖网络线 27 / 数据权限多用户 25 / 画布前端 9 —— 这些是**项目线内容而非方法论**,按技能设计意图**不该做成技能**(归 `state.py` / `接续入口` / 文档库)。⇒ 判定为"分类正确",非缺陷。
### 结论一句话
**能用,且这次修完明显更准**(真实撞点 2→0、覆盖率 95%→100%、规则盲区 7→0);**维护链路本身健康**(三处零差异、引用无悬空、结构零缺陷),风险集中在**两个巨型技能**和**三个字段/版本规范隐患**上。
### 收尾补充(同轮,锁内完成)
**1. 又发现一个维护性缺口:README 漏登 4 个技能**
- 实测:README 只登记 **8 个**,缺 `dsh-instance-diagnose` / `dsh-knowledge-upkeep` / `dsh-opensource-release` / `dsh-plugin-diagnose`(全是本平台方法论技能,看不出有意省略 ⇒ 判为漏登)。
- ⇒ 已按现有行格式补 4 行 ⇒ 现登记 **12 个 = 全部**(11 dsh + agent-operating-rules)。补行时带断言:锚点唯一 + 目标未登记过 + **CR 计数未变**(防行尾被静默改写)。
- 顺带修服务器 `/opt/dsh/docs/README.md` 的**属主**(原为 `197108:197121`,非 root 存量问题)⇒ `600 root:root`。
**2. 沉淀:`dsh-knowledge-upkeep` 新增 §9「技能集可用性体检(10 层取证法)」**(v1.3.0 → **v1.4.0**)
- 该技能本就是"知识库维护与纠偏方法",**技能集体检正是它的子集** ⇒ 不新建技能。
- §9 五节:9.1 十层表 / **9.2 最高价值层 = 查"规则写下来了但没技能接得住"** / **9.3 判据漏词会伪装成技能盲区** / 9.4 撞点分恶性-良性 / 9.5 两条自踩的坑。
- description 同步补触发词「这么多技能会不会话能准确调用」,并顺手补齐它缺失的 `last_change`。
**3. 又踩一个坑(第 2 个自踩)**
- 新增 §9 时 old_string 用 `## 实测坑(2026-09-20)…` 标题占位,**替换后忘了把它加回去** ⇒ 那一节的 4 条 bullet **失去标题**(内容在、标题没了)。
- 发现方式:改完 grep 章节结构。修复:落盘脚本按行插回(带断言:python-c 行 / docs-audit 行各唯一 + 中间纯空行)。
- ⚠️ 连同上一条(重复 `last_change`)⇒ **同一个根因**:**替换长文本时把锚点当占位符用掉**。教训:**插入新章节时,锚点文本必须原样保留在新内容里**。
**4. 最终验收**
- 技能三处一致:**12/12** ✅(本机 = 文档库 = 服务器 `/opt/dsh/docs/skills/`,全部 600 root:root)
- README:文档库 ↔ 服务器 md5 一致 ✅
- frontmatter 结构:**0/12 有问题** ✅
- 全局执行锁已释放
---
## 06:4x–07:0x · 跨工作区冲突检查 + 精简评估 + **版本号统一为 1.0.0**(用户拍板 A)
**用户两问**:① 这些技能跨工作区使用是否冲突?② 在保证功能一致的前提下能否精简(⚠️ 强调功能不受影响)。
**用户拍板**:「A、将所有技能版本号统一改为 1.0.0」。
### ① 跨工作区隔离性:**无真冲突** ✅
扫描全部 12 个技能(12 个 .md × 全部 references)分三级判:
| 级 | 含义 | 命中技能 |
|---|---|---|
| A **严重**(会跑错家) | 指示读写本工作区 / 别的线专属路径 | 3 个:auto-handoff-chain(3) / env-bootstrap(1) / opensource-release(10) |
| B 示例(换环境要替换) | 本平台资源路径(`dsh-server-docs/`、`.workbuddy/`) | 8 个 |
| C 事实(正确且必要) | IP / 域名 / `/opt/dsh` | 多个 |
🔑 **逐条看上下文后判定:A 类全是"非冲突"** ——
- `auto-handoff-chain` 的 3 处 = **命令示例**(示范 `state.py --ws` 用法)+ 一张工作区对照表;
- `env-bootstrap` 的 1 处 = **快照文件内容**(设计上就是要被 `--inject` 替换的);
- `opensource-release` 的 10 处 = **技能固有内容**(本平台开源导出的工作根 `/dsh-ai1net-github/`、脱敏工具路径)⇒ 它本就是平台专属技能。
⇒ **结论:隔离性 OK**。根本原因是**技能按需加载**(无关工作区不会加载它),且真正需要跨工作区的是 `agent-operating-rules`(已确认**完全可移植**:A/B/C 全 0)。
### ② 精简空间:**≈1%,且不该动** ✅(结论与直觉相反)
- 量化:全 12 技能 6327 行;唯一成规模的"跨文件重复"是 `agent-operating-rules` 的 **14 句**(SKILL.md ↔ references)。
- 性质判定:14 句里 **1 句在 ``` 照抄块内**(模板,必须逐字一致);**13 句在正文** ⇒ 表面上"可精简",实测**仅 691 字符 = 全技能 1.0%**。
- 🔴 **但这 1% 也不该删** —— 它们是 **有意的双轨设计**:`SKILL.md` 存**每次加载必读的判据实体**,`references/` 存**按需展开的细节**。
而本工作区已有的分层判据明令「**不看会导致违规/事故的内容必须常驻实体,⛔ 不许只给指针**」⇒ 把 SKILL.md 那 13 句换成指针 = **直接违反既有判据**(加载 SKILL.md 时会看不到关键规则)。
- ⇒ **判定:不该精简**。当前"正文 + references 拆分"形态已是精简后的结果,继续压会伤功能(正是用户强调的红线)。
### ③ 版本号统一(用户拍板 A)
- **12/12 技能 `version` → `1.0.0`**(其中 1 个本就是 1.0.0)。含原 `2.9.2-playwright-banned`(非标准版本号问题**随之消解**)。
- **范围明确**:只改 frontmatter 的 `version`;**⛔ 不动**正文/历史里提到的版本号(如「v1.8.0 起三节收敛为指针」「原 v2.0.0 并入」)—— 那是**当时的事实记录**,改了会破坏追溯性。
- **留痕**:每个技能的 `last_change` **前面加一句**统一说明(含**原版本号** ⇒ 可追溯);4 个原本缺 `last_change` 的(architecture-lifecycle / auto-handoff-chain / decision-method / env-bootstrap)**新建该字段**。
- README 登记行 **11 处**版本号同步改(行数不变,CR 计数校验通过)。
- 三处核验:技能 **12/12 一致** ✅ + README 一致 ✅(600 root:root)。
### 🔴 本轮踩的第 3 个坑(**已沉淀进技能**)
**批量 `scp` 同名文件到同一目录 ⇒ 互相覆盖、只剩最后一个。**
- 实测:`scp a/SKILL.md b/SKILL.md … /tmp/push/` ⇒ 目标目录里**只有 1 个 `SKILL.md`**(后来者覆盖前者)。
- 所幸当时只推到**暂存目录**(未就位)⇒ 未造成损坏。
- 正解:**逐文件推到各自路径**(循环 scp),或**先在本地按名建目录树再整树传**。
- ⇒ 已写入 `agent-operating-rules/references/02` 的 §6 环境陷阱表。
---
## 07:0x · 排查:E 盘根为什么出现 dsh-logs / dsh-mcn-fix / dsh-worker-dev / e
**用户问**:「为什么 E 盘下面会出现 dsh-logs、dsh-mcn-fix、dsh-worker-dev、e 这些文件夹」
### 四个目录分两类
| 目录 | 建立时间 | 内容 | 判定 |
|---|---|---|---|
| `e/` | 09-20 02:07 | 29 MB / 355 文件;内含 `ProgramData/AIProject/{aliyun-dsh-server,dsh-ai1net-desktop}/.workbuddy/**` | 🔴 **误创建的影子目录** |
| `dsh-mcn-fix/` | 09-20 08:16 | **空**(0 文件) | 🔴 **空目录**(建了没用) |
| `dsh-logs/` | 09-18 20:48 | 1.4 MB;`106/` `47/` `state.json` | 🟡 某会话的服务器日志拉取落点 |
| `dsh-worker-dev/` | 09-20 09:49 | 1.2 MB / 101 文件;`bin` `controlplane` `instance-home` `token` `logs` `overlay` | 🟡 某会话的本机 worker 开发环境 |
### 🔴 `e/` 的根因(**当时仍在增长**)
**铁证**:`E:\e\ProgramData\AIProject\aliyun-dsh-server\.workbuddy\stop-dialog-guard.log`(2496 B,最后写入 **09-22 06:17 = 当天**)。
- 文件名 = **hook 自己的名字**;
- `E:\e\ProgramData\AIProject\` 下只有 **2 个**工作区 —— 正是**仅有的两个装了 hook 的工作区**(真目录下有 16 个);
- 另一部分内容是 `dsh-ai1net-desktop/.workbuddy/_devlogs/pud-f1/{Cache,Code Cache,blob_storage,Dictionaries}` = **Chromium 的 user-data-dir**(浏览器自动化)。
**根因(一条,两个受害者)**:
> **把 MSYS / Git-Bash 风格的路径(`/e/…`)交给了 Windows 原生程序**(原生 python / Chromium)。
> Windows 原生程序把 `/e/ProgramData/…` 解释成「**当前盘根 + 相对路径**」= `e\ProgramData\…`
> ⇒ 当前盘是 E 时落到 **`E:\e\ProgramData\…`**。
⇒ 与 MEMORY 里已有的铁律**同源**:「**Python exe 不认 `/e/…` ⇒ 传 `E:/…`**」。本次是它的**第 N 次发作,且这次留下了 29 MB 实物**。
### 修复(已落地并真跑验证)
两个 hook(`skill-load-guard.py` / `stop-dialog-guard.py`)各加 `_norm_path()`:
`^/([A-Za-z])(/.*)?$` → `<大写盘符>:` + 余部(`/e/foo` → `E:/foo`),**幂等**(已是 Windows 形式则原样返回)。
- `skill-load-guard`:入口 `workdir = _norm_path(_workdir(obj))`(下游全受益);
- `stop-dialog-guard`:`log()` + 全部 7 处 `os.path.join(root…` 统一包 `_norm_path(...)`。
**验证(真跑,非静态检查)**:
- 单元:两脚本各 **6/6** 用例通过(含 `/e`→`E:/`、已是 Windows 形式不变、空值 / None 不炸);
- 真跑:喂 `cwd=/e/ProgramData/AIProject/ai1net-dsh-server` ⇒ 日志写进**真位置**(+165 B),**影子位置未增长** ✅
- 三处同步:脚本推 `/opt/dsh/docs/scripts/`(755 root:root,md5 与本地一致)。
### ⚠️ 未动(按 personal-files-safety:扫描只读、删要确认)
`e/`(29 MB)与 `dsh-mcn-fix/`(空)**我没删**。`dsh-logs/` 与 `dsh-worker-dev/` 含实质内容,更需用户判断。清理需点名,届时先出完整清单。
---
## 07:00–07:05 · 收口(上下文 33 万)+ 登记自动接续
**用户指令**:「创建接续会话 执行重组优化,并检查确认」—— 即把两个千行级技能拆成 `SKILL.md` + `references/`,并验证功能不受影响。
**接续包**:`E:/ProgramData/AIProject/ai1net-dsh-server/接续入口_技能重组线_20260922.md`
- md5 = `1f8b78c5a75e3804f2b1cd53a448edcf`(4694 字节)
- 含 §1 接续点(目标原话 / 基线 / 校验命令 / 未完成 / 下一步 / 关键决定 / 回滚点 / 不要重做)+ §2 本轮动作(拆分方案与四条硬约束 / 四项验收)
**基线(写进接续包,供新会话机器校验)**:
- `dsh-change-workflow/SKILL.md` md5 `e5d20a959ed83f2d…` / **1052 行** / 21 个一级章节
- `dsh-opensource-release/SKILL.md` md5 `86f3dcc0c1233a07…` / **1089 行** / 16 个一级章节
- 文档库 HEAD `3d8f50e`;全局锁 = 无(空闲)
**已登记一次性 automation**:`b56314b0-e2e1-4ae6-bfc7-b053ab173abb`(scheduledAt 2026-09-22T07:06,cwds = 本工作区)
- prompt 按 `04-调整方案/126-会话接续规范.md §3.2.1` 模板:⓪ 先跑 `state.py` → ① 跑校验命令 → **①b 带接续包 md5 的口径门禁** → ①c 未定项不许替拍 → ② 从「下一步」开工 → ③ 工具调用 ≤8
- ⛔ prompt 内**不含**任务细节(细节唯一来源 = 接续包)
- ⚠️ 登记后**本会话不再改接续包 / 不再改关键判断**(若改了必须回来撤销或重登记)
**本轮全程(三条线,一次会话内完成)**:
1. 技能加载闸门扩作用域(B)+ 扩词表(B)+ 清退桌面副本(4 条 hook → 2 条)
2. 技能可用性 10 层取证 → 修 3 个真缺陷(触发词盲区)+ 7 条规则盲区 → 覆盖率 95%→100%、真实撞点 2→0
3. 跨工作区隔离性(无真冲突)+ 精简评估(≈1%,且不该动)+ **12 技能 version 统一 1.0.0** + README 补登 4 个技能
4. E 盘影子目录排查 → 定位 MSYS 路径根因 → 两个 hook 加 `_norm_path()` 并真跑验证
**新会话开工口令**(用户自己开时用):
`"E:/ProgramData/.workbuddy/binaries/python/versions/3.13.12/python.exe" "E:/ProgramData/AIProject/ai1net-dsh-server/state.py"`
## 07:08 技能重组线接续棒(自动化 · 校验+备份)
- 口径门禁与 §1 校验命令全部通过(接续包 md5、两个待拆技能 md5/行数 与期望一致)
- 回滚点已落 tmp/_keep-20260922/split-bak/SKILL.md;全局锁已抢并释放
- 拆分(change-workflow / opensource-release)与三处同步未开工
## 07:00–08:00 技能重组线(用户「确认执行」· 本轮主体)
- 拆分两个千行技能为 `SKILL.md`+`references/`(机械搬运、逐行未改):
· `dsh-change-workflow` 1052 → **319 行** + 9 档详情(867 行)⇒ **达标 ≤350**
· `dsh-opensource-release` 1089 → **534 行** + 5 档详情(629 行)⇒ **未达标**:R-O1–R-O17 硬规则 239 行等「不看会违规」常驻项 ⇒ ≤350 结构上不可达(判据常驻优先)
- 四项验收全绿:行数守恒(主干内容+详情=原行数)/三处 md5 一致(16 文件)/51+68 个标题全部可寻址/frontmatter version=1.0.0
- 修正下沉副作用:补「原章节 → 详情档」对照列,接上 5 处跨档断头引用(`见下表:dsh-univer-office`/`见 §8 坑 9`/`见坑 36` 等)
- 登记跟改:`README.md` 2 处、`INDEX.md` 4 处(精确串替换器,命中各 1 次)
- 回滚点:`tmp/_keep-20260922/split-bak/*-SKILL.md`+服务器 `/opt/dsh/backups/docs-skills/*.bak-*`
- ⚠️ 环境坑:bash 包装器本轮**整体起不来**(`ls`/`cat`/`head` 全 not found、export PATH 也救不回)⇒ 全程改走 PowerShell + 托管 python
- ⚠️ 发现(未动手):技能里 `scp -i ~/.ssh/id_ed25519_dsh` 指向的密钥不存在;`INDEX.md` L19/L202 仍写 R-O1–R-O14 与 v1.7.8(陈旧);MEMORY.md 超注入上限
## 07:36–07:50 技能重组线(续):把「技能整理/拆分优化」做成可复用能力
- 落点 = **`dsh-knowledge-upkeep` 新增 §10「技能重组:千行技能拆分」**(不新增第 13 个技能;§9 体检 → §10 整改 成闭环)
- 新增 `references/10-技能重组-千行技能拆分.md`(实操清单 + 首跑实测数据 + 断头引用扫法 + 6 条坑)
- 新增可复跑工具 `scripts/split_skill.py`(`--spec <json>` 声明式规格 ⇒ 纯机械搬运 + 四项自校验 + 幂等)
- 口径已写进 §10:**判据常驻 > 行数目标**;跨档引用断头用「覆盖的原章节」索引表修
- 三处同步(本机 / 文档库 / `/opt/dsh/docs/skills/` 600 root:root)+ 三方 md5 对账 + README/INDEX 登记跟改
---
## 07:38–07:5x · 查证:插件接入 DB「是否该分库」+ 数据库现状(**实测**)
**用户两问**:① 之前规划的插件接入数据库方案是否该设计「插件分库接入」② 现在数据库在哪台服务器、用的什么库。
**实测事实**(只读命令,2026-09-22 07:4x;来源 = `BRIEF.md` + 服务器实查)
| 项 | 读数 |
|---|---|
| 宿主机 | **47.77.182.89**(Manager,阿里云 iZrj99af19cibck1ge93tqZ)= 唯一数据库所在机 |
| 数据库 | **PostgreSQL 13.23**;数据目录 `/var/lib/dshs-pg`;单元 `dshs-pg` = **active** |
| 监听 | `127.0.0.1:15432`(**仅回环**,pid 662048) |
| 业务库 | **只有 1 个**:`dshs`(owner dshs);连接串 `postgres://dshs:***@127.0.0.1:15432/dshs`(源 = `dshs.service.d/*.conf`) |
| 库内表 | **13 张,全为内核表**;`p_*` 插件表 **零命中**;`schema_migrations` 已到 **v13** |
| 106 | 装了 PG 二进制但 `postgresql*` **全 inactive**、无 5432/15432 监听 ⇒ **不承载数据库** |
| 插件数据现状 | 只有 MCN 一份实例内 SQLite:47 上 `users/cce6d1cd-b376-4304-80f0-0e1c58c9ffde/home/.dsh/mcn-plugin.db`(2,383,872 B);106 用户 home 内**无** db 文件 |
⇒ **两条新事实**:(a) 插件数据面(声明式建表 + `im.data`)**尚未实现**,`src/db` 无对应模块、平台库无 `p_*` 表;(b) 连接层是**单库单适配器**(`src/db/index.ts`:有 `dbUrl` 走 PG,否则本地 SQLite),代码里**没有任何分库概念**。
**判定:不该设计插件分库接入** —— 维持 `DB-01 §主从与扩展` 与 `DB-00 §待定` 既有的「⛔ 不提前分库」,理由按硬度排:
1. **双后端同构(最硬)**:sqlite 没有 database/schema 命名空间对等物(`ATTACH` 是连接级、不持久)⇒ 分库 / 分 schema 破坏「任何结构在 sqlite 与 pg 上都建得起来」的既有承诺;表前缀在两边都只是名字,天然同构。
2. **收益为零**:插件不持有连接、不能写 SQL、数据面全走内核 API ⇒ 分库能给的权限隔离对插件用不上;平台侧隔离诉求已由前缀校验 + 房间 ACL + 三重配额 + `plugin_data_audit` 覆盖。
3. **成本随插件数线性涨**:PG 连接池是 per-database 的 ⇒ N 插件 = N 套 pool;47 是小机(实例硬顶 1024 MiB),backends 内存实打实。单库 = 1 个 pool。
4. **把不可逆操作常态化**:插件可启停/卸载 ⇒ 分库要动态 `CREATE/DROP DATABASE`;现行卸载 = 停用 + 数据保留 30 天可恢复。
5. **堵死跨域事务**:既有「跨前缀要原子 ⇒ 走内核事务口」在分库后无法实现。
🔑 **关键认知**:「独立库」这条路**已经存在** —— 插件的「只属于某用户自己的数据」落实例 home 本地 SQLite,天然每用户每插件一个文件(MCN 现况),随实例迁移、故障互不影响 ⇒ **真正需要独立库的场景已被覆盖,平台库不必再分**。
⚠️ MCN 09-21 那次迁移不顺的根因 = 「平台侧没有写用户 home 的合规通路」(`UserFs` home 白名单只有 3 个裸文件名 + 文本语义),**不是缺分库**;分库反而让「谁写、怎么迁」更绕。
**回头重估的触发条件**(不是现在):压测证明单库触到物理瓶颈(磁盘 / IO / 单表行数)**且**某插件占比超阈值(建议 20–30%);扩展顺序仍按 `索引与游标分页 → 只读副本 → 分区 → 分库`。
**⚠️ 未动(只报告)**:`DB-03-插件数据面规范.md` 里**没有**「插件是否该分库」的论证节 —— 该结论只散落在 `DB-01` 表格一格 + `DB-00` 待定一行。要落进文档时再动手(未改任何文档、未提交)。
---
## 07:5x–08:0x · 数据库重规划:**一插件一库 + 用户数据入库**(用户拍板,推翻我上轮判定)
**用户拍板(两次)**:①「我认为数据需要分库,并且用户数据也需要保存到库中方便备份和用户迁移,用户本地只保存文件相关内容(后续除了缓存和临时文件,主要文件内容也需要储存在 sso 中)」② **澄清粒度**:「我说的分库是指**不同的插件建立不同的库**」,且统一跑在 **PostgreSQL 13.23** 上。
🔴 **我上轮的判定被推翻,且其中一条论据是错的**(必须记住这个教训):
我上轮用「sqlite 没有 database 命名空间对等物 ⇒ 破坏双后端同构」+「连接池会爆炸」否掉分库。按**按插件分库**的口径:
- (a) sqlite 侧「**一个插件一个文件**」同样承载"库"这个概念 ⇒ **双后端同构仍然成立**;
- (b) 连接池爆炸**只在按用户分库时发生**(库数 = 用户数);**按插件分库的库数 = 插件数**(十几~几十)⇒ 完全可控。
⇒ **教训:否掉一个方向之前先确认它的粒度** —— 别拿 A 粒度的代价去否定 B 粒度的方案。
### 交付(已落文档库,纯 LF,未提交)
| # | 文件 | 动作 |
|---|---|---|
| 1 | `架构设计/数据-分库与权威存储架构.md` | **新建**(12 节定稿):分库口径 / 库清单 / **两个诉求正交** / database-vs-schema / 访问通路 / 备份迁移导出 / S0–S5 / 风险 / 衔接 / 回溯 |
| 2 | `数据库/DB-03-插件数据面规范.md` | **12 处更新**(148→162 行):落点改一插件一库 · 归属列强制 · 红线新增 2 条 · API 库路由 · 管理面按库备份迁移 · 验收 12 条 |
| 3 | `数据库/DB-00-专区入口.md` | 判据速查重写 6 条 + 指向定稿 + **原「⛔ 不提前分库」标作废** |
| 4 | `数据库/DB-01-接入指南.md` | 情形表改 7 行 · 情形 2/3 重写(用户数据入库、home 降级为缓存/临时)· 选型与扩展顺序更正 |
| 5 | `架构设计/README.md` · `INDEX.md` | 登记(INDEX 字节级替换,CR 5→5 未变) |
### 设计要点(核心 5 条)
- **分库维度 = 插件**:库名 `dshs_pl_<pluginId>`,全部建在**同一个 PG 13.23 实例**(不新建实例、不换版本)
- 🔑 **两个诉求正交**(本设计的关键):**库 = 插件**(满足"分库");**库内归属列 = 用户/房间**(满足"按用户备份迁移")⇒ **归属列强制**(内核自动补 `user_id`/`room_id`,插件不许省略)是分库的**前提**,缺了它"按用户迁移"直接不可能
- **默认独立 database**(非 schema):字面合规 + 隔离最硬(跨 database 无原生 join/事务)+ 卸载 = `DROP DATABASE` 一库带走;schema 档留作连接吃紧时的备选(同套代码,只改路由映射)
- **实例本地收窄为三类**:文件内容 / 缓存 / 临时 ⇒ 判据「**本地丢了不影响正确性,只影响延迟**」;🔴 新红线「**不得以实例本地库当数据权威**」(作废 MCN 那套自建 SQLite 当权威的形态)
- **"能备份能迁移"的正解 = 统一迁移器**:按归属列遍历 控制面库 + 各插件库 + 桶清单,一次遍历、可重入、可对账(落 S5)
### 事实更正(写进定稿)
47 上 PG 精确版本 = **13.23**(上轮笼统写"PG13");现有 **13 张表全是内核表**、`p_*` **零命中** ⇒ 插件数据面**未实现**,仍是规范。
### 未动(按纪律只报告)
- `交接单/MCN线-DB接入规范合规化_20260921.md §四` 的待拍板项(历史数据导入通路)**现已有答案**(定稿 S2 的可复用导入通路)—— 该单未改
- 未建交接单、未开工代码、未 commit / push
- ⚠️ 顺带发现:`dsh-server-docs/数据库/` 与 `架构设计/` 两个目录在 git 里**整体未跟踪**(`??`),即已存在多日但一直未提交
## 07:50–08:00 技能重组线(§10 首轮迭代:把两个新坑与 docs 根漂移写回技能)
- 修掉本轮自己引入的**登记误挂**:`NAME in line` 匹配把 §10 挂到了 `dsh-architecture-lifecycle` 行(该行描述里提到 `dsh-knowledge-upkeep`)⇒ 判据改为**只认行首单元格** `l.startswith('| `skills/<n>/`')` 且命中数必须 = 1
- 发现并关闭**登记漂移**:`/opt/dsh/docs/` 根也镜像 `/opt/dsh/docs/{README,INDEX}.md`(600 root:root);此前三处同步只推 `skills/` ⇒ 根登记落后一整轮。本轮推送 + 备份 `/opt/dsh/backups/docs-root/` + md5 复核一致
- 远端陈旧判定教训:⛔ 别用 SequenceMatcher 的「opcode 全 equal/delete」(`**` 密集行会假 replace ⇒ 假阴性停手);✅ 用**整行子序列覆盖率 ≥0.98**,或 `git show HEAD:<f>` 作 oracle
- 三条教训已写回 `dsh-knowledge-upkeep/references/10-技能重组-千行技能拆分.md`(坑 7/8/9)+ SKILL.md §10 第 4 条;技能三处复推并复核 md5 一致
- 08:10 又查出 **3 处技能内陈旧事实**:① 私钥名 `~/.ssh/id_ed25519_dsh` **不存在**(真名 `id_ed25519`,已实测 rc=0)② `~/.ssh/config` 的别名 `bt-server` 写死 `Port 32022`,而 sshd 只听 22 ⇒ 用它必 `Connection refused` ③ 平台速查把文档库写成 **不存在的** `E://ProgramData//AIProject//aliyun-dsh-server//dsh-server-docs`(实测 `Test-Path=False`;真源 = `D:/github/dsh_shenxian/dsh-server-docs`)⇒ 纠正脚本 `tmp/_fix_stale.py`(幂等 · 5 条精确串替换 + 残留自检 + 三处同步对账)已备好,但**抢不到锁**(DB 分库重规划线 07:50 起占用)⇒ 未落盘,已登记一次性执行棒② `8370a5dd-1fa8-4fa5-a90e-1f3a3f46bbaa`(09-22 08:26)接手
- 遗留未动(仅报告):`接续入口_技能重组线_20260922.md` 的 §2「下一步=拆分两个技能」已与事实不符(本轮被明令禁止改该入口 ⇒ 未动);`INDEX.md` 无 `dsh-knowledge-upkeep` 行、L19/L202 仍写 `R-O1–R-O14`/`v1.7.8`;按纪律未 commit / push
## 07:55 `humanizer_zh` 找回(结论:没丢,是没装)+ 登记执行棒②
- **真身** = 源码库 `E:/ProgramData/AIProject/_skill_src/Humanizer-zh/`(frontmatter `name: humanizer-zh`,MIT,翻译自 blader/humanizer,含 24 条 AI 写作模式);`.workbuddy/skills/` 下**从未装过**(那里只有英文 `humanizer`,它自带 `references/patterns.zh.md`)。判定依据:技能根 + `~/.workbuddy/skills` 双侧 `Test-Path` 全 False;全盘 `*humanizer*` 目录/文件搜索只剩源码库与审计报告 ⇒ 不是被删,是从未落地到技能目录
- **已恢复安装**:复制 3 文件(SKILL.md 19,382 B / README.md / LICENSE)到 `E:\ProgramData\.workbuddy\skills\humanizer-zh\`,md5 = `294DBBBD…` 与源一致;源码 mtime 仍为 09-20 11:26(未变)⇒ 当日审计「100 分 / 0 风险 / 无 scripts」对这批字节继续有效(报告:`dsh-ai1net-github/_安全审计_Humanizer-zh_20260920.md`、测评:`_中文侧测评_Humanizer-zh_20260920.md`)
- **登记执行棒②** `8370a5dd-1fa8-4fa5-a90e-1f3a3f46bbaa`(08:02,两阶段):A = 上一节那 3 处陈旧事实纠正(`tmp/_fix_stale.py`);B = 用 `humanizer-zh` 改写技能中文描述,**首单元 = `dsh-opensource-release/references/03-实测坑.md`**(用户举例所在),扩面顺序 opensource → change-workflow → knowledge-upkeep → decision-method → feature-first → 其余
- 🔴 文案优化硬门禁(写进棒里,防「改文风顺带动了功能」):① **技术 token 多重集相等**(反引号内联码 / 路径 / `--flag` / 全大写 env / 数字)② 表格首列标签不变 ③ 纯 LF ④ 条目/小标题不丢;落地只用**精确串替换**(命中数必须 = 1),⛔ 不整文件 Write
## 08:07 技能重组线 · 阶段 A + 阶段 B 首单元
- 阶段 A `_fix_stale.py`:5 条精确替换全 ✓(ssh 私钥真名 / bt-server 端口 32022 已死 / 00-平台速查的文档库路径),dsh-change-workflow 三处 md5 一致;残留自检报的 5 处经逐条复核**全是纠正文案自身引用旧值**的假阳性 ⇒ 真实残留 0。
- 阶段 B:`dsh-opensource-release/references/03-实测坑.md` 按 humanizer 判据人话化 43 处(去破折号解释链、改否定式排比、去指代、收敛空转句);门禁四项全过(技术 token 多重集 / 表格首列 / 纯 LF / 条目与行数);三处一致 `d13a42443e51`。首轮 1 条锚点写错 ⇒ 已反向还原后原子重落。
- 遗留待拍板:坑 20 尾部两条 ⚠️ 判据内容重复(「四次」与「三次」并存)⇒ 未自行动手。
- 下一棒 = `dsh-opensource-release/SKILL.md`(按 +7 分钟排)。
## 08:18 技能重组线 · 第 4 棒(dsh-opensource-release/SKILL.md)
- 目标文件:`dsh-opensource-release/SKILL.md`(534 行 / 61,802 字符;原文 ` —— ` 100 处)。
- 手法:机械规则 A `** —— `→`**:` 53 处 · B `)—— `→`):` 10 处(跳过 frontmatter 与围栏代码块);另 21 处语气需判断 ⇒ 精确串替换(全文命中须恰为 1 次)。
- 门禁四项:全过;未能命中 1 处;落盘 = 否(整体未落盘)。
- 三处同步:本机 `(未同步)` / 文档库 `(未同步)` / 服务器 `(未同步)` ⇒ 待核。
- 未动项:frontmatter(路由元数据 + 变更台账);引文模板与代码块内破折号按「技术标识/引文不动」保留。
- 下一棒 = `dsh-opensource-release/references/01-保留移除与脱敏口径.md`(+7 分钟)。
## 08:19 技能重组线 · 第 4 棒(dsh-opensource-release/SKILL.md)
- 目标:`dsh-opensource-release/SKILL.md`(534 行 / 61,802 字符;原文 ` —— ` 100 处)。
- 手法:通用破折号规则 ` —— `→`:` 64 处 + `」——**`→`」:**` 1 处(跳过 frontmatter · 围栏代码块 · 3 处引文/字面输出);另 26 处语气需判断走精确串替换。
- 门禁四项 有未过;未命中 0 处;落盘 = 否;字符 61802 → 61537。
- 三处同步:本机 `(未同步)` / 文档库 `(未同步)` / 服务器 `(未同步)` ⇒ 待核。
- 上一枪因锚点写错**整体未落盘**(旧脚本无此栏杆)⇒ 本版已修正通用规则并重落。下一棒 = `dsh-opensource-release/SKILL.md`。
## 08:20 技能重组线 · 第 4 棒(dsh-opensource-release/SKILL.md)
- 备份:`E:/ProgramData/AIProject/ai1net-dsh-server/归档/humanize-原文件备份-20260922\dsh-opensource-release`(技能全 .md + 文档库副本)。
- 目标:534 行 / 61,802 字符;原文 ` —— ` 100 处 ⇒ 破折号规则(反引号感知)替换 64 处 + 精确串 26 处。
- 门禁 8 项(原 4 + 语义 4:模态词 / 交叉引用编号 / 粗体标记 / 表格行)全过;落盘 = 是;字符 61802 → 61540。
- 两次前置失败均被门禁拦在写盘前:① 锚点写错(`**显得半成品**` 实为 `半成品**`)② 反引号内字面被误改 ⇒ 均未落盘,磁盘始终是原文。
- 三处同步:本机 `636a7490803f` / 文档库 `636a7490803f` / 服务器 `636a7490803f` ⇒ 一致。下一棒 = `dsh-opensource-release/references/01-保留移除与脱敏口径.md`。
## 08:26 技能重组线 · 坑 20 判据去重(方案 A)
- 决策:用户选「方案 A:回源核对数字,删掉错的那条」。
- 回源依据(`references/03-实测坑.md` 自身):坑 20 自述「四处一起改」= `OVERLAY` / `REQUIRED_EXPORT` / `_check_parity.mjs` 的 `MANUAL`·`PAIRS` / `_check_links.mjs` 的 `MANUAL`;L51 明写「③ 与 ④ 是两份独立实现 ⇒ 必须各改一次」;L52 验收判据要 links 与 parity 两处都回读 ⇒ 同类静默跳过共 **四次**。
- 处置:删除「已出现**三次**」那条(旧版枚举,把 `PAIRS` 单列却漏掉两个 `MANUAL`,与 L51 冲突),保留「已出现**四次**」那条。
- 门禁 5 项 全过;行数 93 → 92;字符 8605 → 8480;三处 md5 e4d9facfcdb6 ⇒ 一致。
- 改动前快照:`E:/ProgramData/AIProject/ai1net-dsh-server/归档/humanize-原文件备份-20260922\dsh-opensource-release\03-实测坑.md.pre-坑20去重`;⛔ 未 commit / push。
## 08:37 技能重组线 · 第 5 棒(dsh-opensource-release/references/01-保留移除与脱敏口径.md)
- 备份:`E:/ProgramData/AIProject/ai1net-dsh-server/归档/humanize-原文件备份-20260922\dsh-opensource-release`(技能全 .md + 本轮改动前快照 `.pre-hz06`;文档库副本已备份)。
- 改动:精确串 5 处(4 处破折号解释链 ⇒ 逗号/冒号、1 处指代代词 ⇒ 点名主体)+ 反引号感知通用破折号规则 0 处。
- 门禁 8 项 全过;落盘 = 是;字符 5924 → 5913,行数不变。
- 三处同步:本机 `84f1906d8488` | 文档库 `84f1906d8488` | 服务器 `84f1906d8488` ⇒ 一致;下一棒 = `dsh-opensource-release/references/02-多远端推送.md`。
## 08:44 技能重组线 · 第 6 棒(dsh-opensource-release/references/02-多远端推送.md)
- 备份:`E:/ProgramData/AIProject/ai1net-dsh-server/归档/humanize-原文件备份-20260922\dsh-opensource-release`(技能全 .md + 本轮改动前快照 `.pre-hz07`;文档库副本已备份)。
- 改动:精确串 3 处(破折号解释链 ⇒ 冒号、指代「自己」⇒ 点名主体、口语 ⇒ 书面);通用破折号规则 0 处。
- 门禁 8 项 全过;落盘 = 是;字符 1649 → 1643,行数不变。
- 三处同步:本机 `d3a6d2241973` | 文档库 `d3a6d2241973` | 服务器 `d3a6d2241973` ⇒ 一致;下一棒 = `dsh-opensource-release/references/04-交互式图示-archify.md`。
## 09:50 技能重组线 · 第 7 棒(dsh-opensource-release/references/04-交互式图示-archify.md)
- ⚠️ 本棒原定 automation(dd0d1db8)08:51 到点**未启动**(无会话、state.json 停在 08:44、锁未持有)⇒ 用户 09:48 追问后由本会话直接执行;同一 automation 已改为第 8 棒。
- 备份:`E:/ProgramData/AIProject/ai1net-dsh-server/归档/humanize-原文件备份-20260922\dsh-opensource-release`(技能全 .md + 本轮改动前快照 `.pre-hz08`;文档库副本已备份)。
- 改动:精确串 11 处;通用破折号规则 1 处;反引号外「——」残留 = 0。
- 门禁 9 项 全过;落盘 = 否;字符 5477 → 5452,行数不变。
- 三处同步:本机 `(未同步)` | 文档库 `(未同步)` | 服务器 `(未同步)` ⇒ 待核;下一棒 = `dsh-opensource-release/references/04-交互式图示-archify.md`。
## 09:52 技能重组线 · 第 7 棒(dsh-opensource-release/references/04-交互式图示-archify.md)
- ⚠️ 本棒原定 automation(dd0d1db8)08:51 到点**未启动**(无会话、state.json 停在 08:44、锁未持有)⇒ 用户 09:48 追问后由本会话直接执行;同一 automation 已改为第 8 棒。
- 备份:`E:/ProgramData/AIProject/ai1net-dsh-server/归档/humanize-原文件备份-20260922\dsh-opensource-release`(技能全 .md + 本轮改动前快照 `.pre-hz08`;文档库副本已备份)。
- ℹ️ 本棒为修正后**重跑**:首次尝试(09:53)有 1 处 old 串转写错字(写成 `当「`,实为 `用「`)⇒ `fails` 非空 ⇒ 门禁按设计整体拦下、未落盘、锁已释放;修正后本次落盘=是。
- 改动:精确串 13 处;通用破折号规则 0 处;反引号外「——」残留 = 0。
- 门禁 9 项 全过;落盘 = 是;字符 5477 → 5452,行数不变。
- 三处同步:本机 `af7561987ed1` | 文档库 `af7561987ed1` | 服务器 `af7561987ed1` ⇒ 一致;下一棒 = `dsh-opensource-release/references/05-已知待办与漂移.md`。
## 09:54 技能重组线 · automation「补跑」清理(用户明令:不允许补跑)
- 判据:一次性 automation 跑完不自动转完成态,调度器有 **12 小时补跑窗口** ⇒ 能再被扫到的 = `scheduledAt` 落在**过去 12 小时内**且仍 `ACTIVE` 的记录。
- 按此筛,当时在册 15 条 automation 里符合条件的**只有本线 4 条**:`f37f1668`(第5棒 08:35) · `62d370fd`(下一棒 08:14) · `8370a5dd`(执行棒② 08:02) · `b56314b0`(执行棒① 07:06) ⇒ 已全部 `mode=delete` 删除。
- 其余 11 条(carbon/浮层/MCN 三线的过期 `ACTIVE` 7 条 + `PAUSED` 4 条)`scheduledAt` 都早于 12 小时窗口 ⇒ 物理上不会再跑;是否一并删除待拍板。
- 协议加固(已写进第 8 棒 prompt):每棒**必须 `mode=update` 复用自己那条 automation 记录**,⛔ 禁止 `mode=create` 新建 —— 残留的「已跑过仍 ACTIVE」旧记录正是补跑风险源;收尾再 `mode=list` 清本线过期残留。
- 当前在挂的唯一接续棒 = `dd0d1db8`(第 8 棒,`scheduledAt` 10:01,目标 `references/05-已知待办与漂移.md`)。
## 10:0x · 判定:carbon 线「两处平台侧阻塞」——**它测的不是我们的代码基**(只读取证,零改动)
**输入**:`E:/ProgramData/AIProject/dsh-plugin-carbon/对接文档_carbon插件-平台侧两处阻塞_20260922.md`
🔴 **核心事实(本轮新发现,最易再犯)**:carbon 线的 `testlocal` 跑的是**开源导出物**,不是 47/106 的源仓平台。两套命名**并存且都真实存在**:
| | 源仓 / 生产 | 开源导出物 |
|---|---|---|
| 服务 | `dshs` / `dshs-worker` | `dsh_ai1net.service` |
| env 前缀 | `DSHS_*` | `DSH_AI1NET_*` |
| 落点 | `/opt/dshs` · `/etc/dshs.env` · `/var/lib/dshs` | `/opt/dsh_ai1net` · `/etc/dsh_ai1net.env` · `/var/lib/dsh_ai1net` |
| 数据根权限 | **711 + users/ 711**(47 / 106 实测) | **700 + users/ 700**(testlocal 实测) |
| enablePatch | `/etc/dshs.env` **无** `DSHS_ENABLE_PATCH` ⇒ false(47/106 实测 0 命中) | `DSH_AI1NET_ENABLE_PATCH=true`(导出物 `install.sh` 默认写) |
⇒ **carbon 线的行号引用全对**(`dsh.ts:149-175` / `business-plugins.ts:649` / `orchestrator.ts:65,582,864` / `local-user-fs.ts` 逐条吻合),**但结论不能搬到 47**。
- **事项一**:本平台**不成立**(711 已落地)。真缺陷 = 导出物 `install.sh:275 run chmod 700 "$DATA_ROOT"` 与源仓既定口径冲突(`bootstrap-worker.sh:139-148` 明写"users 必须 o+x(711),否则 setpriv 无法穿越 ⇒ 实例起不来" + 幂等自检)⇒ 归属**开源导出线**,⛔ 不需走 R5(711 是本平台既定架构口径,档案 02/122/144)。
- **事项二**:源仓 `dsh.ts:152` 的 `/api/dsh/restart` **确实从不写 handoff**(只留日志)。其注释两条前提:①"线上为 false" **对源仓仍成立**(对导出物失效);②"handoff.json 无人消费" **本身写错** —— 消费者是 watchdog(`:65` WATCHDOG_TASK + `:864`),且 `business-plugins.ts:649` 平台自己每次启停插件都写 handoff(空命令)。
- 🔴 **安全面定位修正(关键)**:恢复 `command` **不新增**权限面 —— `handoffPath` = `<userRoot>/handoff.json`,而 `users/<id>` 属主就是该用户 uid(700)⇒ **用户自己写文件 + 触发一次重启**即可让 watchdog 执行(`dsh.ts:162` 无条件 `spawnWatchdog`,仅受 `enablePatch` 门控)。增量风险**全在 `enablePatch=true` 这一个开关上**(=平台侧 D8 候选A 已列的待拍板项)。执行者是用户在**自己的 bwrap 沙箱、自己 uid**(`orchestrator.ts:1073`),不是 root 链路;但它是 **headless agent 会话**(由 LLM 解析执行),有 token 成本。
- ✅ **比 carbon 三案更好的一条路**:平台**已有**正是它"乙案"想要的落点 —— `LocalSpawnerHooks.onInstanceStart`(序47·S4,`orchestrator.ts:801-818 / 878`):main 实例 spawn 前调用一次、Manager 注入 worker 不注入、唯一用途就是"平台侧代签 + 走 UserFs 投凭据到实例 home",且**失败不阻断启动**。⇒ 凭据投递可挂这里,不必启 enablePatch、不必恢复 handoff。
- ⚠️ 未核实项:47 与 106 的 `users/<id>` 单目录模式已见 700(预期);导出物在 account 模式下 700 是否会同时打崩实例(未复现,机制上可能 —— bwrap 内父目录权限与宿主不同)。
## 10:06 技能重组线 · 第 8 棒(dsh-opensource-release/references/05-已知待办与漂移.md)
- 备份:`E:/ProgramData/AIProject/ai1net-dsh-server/归档/humanize-原文件备份-20260922\dsh-opensource-release`(技能全 6 .md + `.pre-hz09` 快照;文档库副本已备份)。
- 改动:反引号外破折号 61 处(冒号 41 / 逗号 18 / 行首删除 2)+ 指代修正 2 处;门禁 9 项 全过;落盘=是;字符 22818 → 22684,行数不变。
- 三处同步:本机 `e9384f74632d` | 文档库 `e9384f74632d` | 服务器 `e9384f74632d` ⇒ 一致;下一棒 = `dsh-change-workflow/SKILL.md`。
## 13:2x · **开新线:插件投放与分库线**(用户提要求,当场收口开接续捧)
**用户原话(目标,⛔ 不得改写)**:「插件由admin上传后,应该是建独立库据库进行对接(需考虑更新插件的情况),然后分发到服务器worker,或端侧设备上,任何登录用户都可以访问才对」
⇒ 四条判据:**P1** 上传即建该插件独立库并对接 | **P2** 插件**更新**要对库有定义 | **P3** 分发到 **worker + 端侧设备** | **P4** **任何登录用户可访问**。
**现状缺口(本轮已核)**
- 一插件一库 = **已拍板未实现**(定稿 `架构设计/数据-分库与权威存储架构.md`;库里 `p_*` 零命中)。
- 分发现状 = admin 导入候选池 → 用户自助启用 → **实例内 `pnpm add`**(`business-plugins.ts:625-651`);**跨机那条腿是断的**(handoff 停写,`dsh.ts:149-175`)⇒ P3 当前不成立。
- 端侧**没有插件投放通路**(只有设备登录接入)。
- P4:逐用户启用 ≠ "任何登录用户可访问";语义待拍(入口 §4 三案)。
🔑 **本线已定技术方向(可推翻)**:**把「包投递」与「实例内安装」解耦** —— ① 包按**节点预铺**(平台/worker agent 铺到各节点本地包点,不在实例内执行命令)② 安装仍走实例内 `pnpm add file:<本机已有包>` ⇒ **跨机 handoff 不再必需**,也⛔ 不必给登录用户开"重启后执行命令"的口子(正是 carbon 线卡住的那处)③ 建库是**平台侧**动作(上传钩子里 `CREATE DATABASE dshs_pl_<pluginId>` + 迁移登记)④ 端侧先只做**可见性**。
- ⚠️ 分清两层:既有硬说明「投放业务插件只有一种方式(不铺 profile)」说的是**启用语义**;P3 是**包分发面**,⛔ 不是推翻它。
**接续捧(已登记)**:`79d47dff-9a32-4a12-8c34-b3cf1b05fac0` · 规划棒① · `scheduledAt` **2026-09-22T13:33** · `cwds` = 本工作区
**入口(唯一一份)**:`E:/ProgramData/AIProject/ai1net-dsh-server/接续入口_插件投放与分库线_20260922.md`
- 最终字节:**md5 `dd8d5f6dc754e3bb758a384e469c1a03`** / 6411 B(该值已写进启动棒 prompt 作口径门禁)
- ⛔ 规划棒只出**一份交接单**,不改码 / 不部署 / 不 commit / 不替拍 §4
**本轮未动**:源仓代码一行未改、47/106 未动、未 commit。carbon 线的文件一个字未动(判定也没回传)。
### 13:2x 补充:用户拍板 **C 档 + 四条硬口径**(入口已按此改写,md5 变更)
**用户原话(第二段,⛔ 不得改写)**:「c 全员默认可见,需自己开通,服务器上所有用户共享只读插件库,不会每个用户分发一份,使用中数据入库,文件保存到用户插件文件夹下对应插件名称的路径中」
⇒ 四条硬口径(已写进入口 §3,规划棒 ⛔ 不得改档):
- **D1**:候选池插件对**所有登录用户默认可见**,**开通由用户自己点** ⇒ ⛔ 不采"默认安装"
- **D2**:服务器上**一份只读插件库共享**,**⛔ 不给每个用户复制一份** ←(这一条**修正**了开线棒的方向:原写"实例内 `pnpm add file:<本机包>`"会各自复制进 profile)
- **D3**:使用中数据**入库**(`dshs_pl_<pluginId>`)
- **D4**:用户侧文件落 **「用户插件文件夹 / <插件名称> / …」**(目录真身须现场核实,⛔ 不许猜)
🔴 **我原来的方向被修正的一处**:D2 意味着**不能靠每用户 `pnpm add` 复制实体** ⇒ 首选改为**节点级共享只读 pnpm store**(`node_modules` 硬链/symlink,实体一份)+ 备选"解包到共享目录后 symlink";注意**硬链要求同一文件系统**、共享包目录要给用户 uid `o+rX`(`users/` 711 那类前例)。
- ⛔ 同时**去掉**了开线棒提的"admin 可标默认启用"(用户未要求)。
- 入口最终字节:**md5 `1f62266bce87a35bab652b7e1133610c`** / 8192 B(已同步更新启动棒 prompt 的门禁值,**复用同一条 automation**,未新建)。
- 新增 **§5-3 现场待核实清单**(用户插件文件夹真身 / 当前 pnpm store 形态 / 共享 store 的迁移与旧副本处理 / 端侧加载通路),要求逐条实测、⛔ 不许推断。
---
## 13:3x–13:5x · IM 群组线(档案 142):按最新两份架构定稿复核,**五单逐单对齐**(纯文档修订,零代码)
**用户问**:「im 的功能规划的如何,根据最新平台网络和用户层架构还有要更新的地方吗」
**取证(最小,全部实测)**
- `src/im/` **目录不存在** ⇒ IM 实施进度 = **零**;`src/db/schema.ts` 最新迁移 = **v13** ⇒ IM 三表取 **v14**(142 与 README 台账里写的 **v12** 系 09-20 旧值,已更正)。
- 五单 `IM群组-{A,B,C,D,E}` **全部 ⏳ 待执行**、**均未登记执行棒**;串行建议 = **A → B →(C ∥ D)→ E**(冲突域:A 独占 `schema.ts`;B 独占 `server.ts` 挂载+upgrade 分流)。
- 四个待拍板项**仍未决**:① UI 落点(门户 / 独立页)② E2EE 本期做不做 ③ 首批场景优先级(办公群 / MUD / 跑团)④ 房间上限与发言预算数值(待压测标定)。
**发现的对齐缺口(09-20 成稿 早于 09-21 形态定稿与 09-22 分库定稿)**
1. **库归属未点名** ⇒ 钉死 = **控制面库 `dshs`**(`DB-03 §〇` 明列 `rooms`/`room_members`/`messages`/`files` 为**内核表**、不适用插件数据面规范)。
2. **联邦形态缺失** ⇒ 房主 = **建房那个区的 Manager**;**跨区本期不做**(依赖 149 §七 F6 跨区可见性,待拍板)+ G3 用户无区字段;但三表须**留可扩字段**(148 §G4:`overlay_devices` 主键已含 `network`)。
3. **142 §3.5「relay / DIAL / presence 可直接复用」—— 整体错** ⇒ **relay 与 DIAL 不在 IM 依赖链上**(IM 走平台侧 HTTPS/WS);**presence 只复用机制口径**(1 s 批合并 / 只订阅可见成员),**通道须平台 IM hub 自建**(网络层跨用户隔离是**结构性**的 `dialers` 默认拒绝,接不出跨用户在线态)。
4. **A 单 §九-1 / D 单 §九-1 的插件数据落点已作废** ⇒ 改 **一插件一库 `dshs_pl_<pluginId>`** + 归属列强制;**删掉「用户自己的 ⇒ 实例 home」**(实例本地只留缓存/临时,权威必须在库)。
5. **C 单卡在一条今天不存在的通路** ⇒ 插件要装进**用户所在节点**的实例,而跨机投放腿 `writeHandoff` 因 `DSHS_ENABLE_PATCH` 默认 false **空转**(147 G 系列)⇒ 已登记依赖 = 「插件投放与分库线」P3,或把验收限定在 47 本机实例。
6. **D 单 §九-1** 同上;示例插件须按**新投放面**打包(非 09-20 的「实例内直装」)。
**交付(6 个文件,已落文档库工作树,⛔ 未 commit / 未 scp)**
- `04-调整方案/142-...md`:头部加**架构对齐状态块**(过程档案纪律:**正文不改**)+ 新增「🔴 现状复核(2026-09-22)」。
- 五单各加 **§〇 架构对齐节**(与正文同等效力);A 单三处就地更正(§四-1 / §六-8 / §九-1)、D 单 §九-1 表格就地更正。
- `交接单/README.md`:台账 A 单行 **v12 → v14** + IM 串行建议处加**对齐要点行**。
- `INDEX.md` 04-142 登记行:加复核结论(relay/presence 口径 · 库归属 · v14 · 五单已对齐)。
**本轮踩的坑(已记,方法论级)**
- 🔴 **Bash 工具的 shim 整体起不来**(`shell-runtime-bash-env.sh: line 3: dirname: command not found` ⇒ PATH 未注入 ⇒ `ls`/`cat`/`head`/`mkdir` 全 not found;`export PATH=<PortableGit usr/bin>` 也救不回——shim 在 bash 启动时重设 PATH 覆盖手设值)。连带 effect:**钩子要求先抢全局锁,而抢锁是 bash 脚本** ⇒ 直接 Edit 被「无锁」钩子拦下。
- ✅ **解法(可复用)**:写 `tmp/claim_lock.py`,用**托管 python 的 `subprocess`** 调 `PortableGit/bin/bash.exe`,并给它一份**含 `usr/bin`+`bin`+`mingw64/bin` 的 PATH** ⇒ `handoff-guard.sh --claim-exec/--release-exec` 均 **rc=0** 正常。⚠️ 抢锁失败时脚本 exit 1 且**中文输出会乱码** ⇒ 别把「乱码+exit 1」误判成「锁被占」(本次实测是 `mkdir` not found)。
- 锁已 `--release-exec` 释放;**未动**源仓代码 / 47 / 106。
---
## 13:40–13:5x · IM 拍板(用户四条答复)+ 🔴 未落文档(锁被别线占)
**用户原话(逐条)**:①「1 说的是什么 ui 具体点,现在应该只讨论了 im 架构和接口,没有功能把」②「2 一步到位 但要看似不似最佳方案」③「3 都需要」④「4 待定」
**口径判定**
- **① UI 落点 —— 仍未拍板**;用户要求先讲清「UI 具体指什么」⇒ 议题**细化为三案**:A 门户内嵌「会话」分区(`web/portal.html`)| B 独立聊天页(`web/im.html`,可挂子域)| **C 实例内工作台面板**(走 dsh 既有 slots 机制,与 MCN 工作台同族)—— ⚠️ **原 E 单只列 A/B,漏了「实例内」这一面**。
顺带确认用户判断成立:五单里 A/B/D = 内核与接口契约、C = agent 接入,**E 是唯一用户可见界面且只有「最小闭环」骨架**(进房 / 看消息 / 发消息+@ / 待应答开关 / 成员表 / 代答标识),**没有功能细化**(群管理、通知、搜索、文件、已读、线程均未定)。
- **② E2EE = 本期做(一步到位)** ⇒ 即原 D 单**候选 B**。⚠️ 但「**看似不似最佳方案**」**未找到等价判据** ⇒ 我按「**工程务实档**」理解(对称群密钥 + 成员分发 + agent 解密授权;⛔ 不做双棘轮 / 前向保密),**已在回复里请用户确认该理解**。
- **③ 首批场景 = 三类都要**(办公群 / MUD / 跑团)⇒ D 单判据由「至少跑通**一个**非聊天场景」**升级为三类各跑通一个**;C 单 `room_type` 三类各给一份默认值(聊天档 / 实时档限速 / 实时档回合制)。
- **④ 预算数值 = 待定** ⇒ 维持压测标定,A 单沿用保守初值。
**🔴 落地状态 = 未落文档**:13:40 抢全局锁**失败**,被 **`插件投放与分库线-规划棒①`**(09-22 13:34 起、未声明单号)持有 ⇒ 按 **R9 停手**(⛔ 不删锁、不接管、不硬改)。
⇒ 四项拍板**只记在本日志**,待该棒 `--release-exec` 后由**下一棒/下一会话**落到:`04-调整方案/142 §六`、`交接单/IM群组-C §四-8`、`IM群组-D §四-6/7`、`IM群组-E §四-4/5`、`交接单/README.md` 台账行。
⚠️ 顺带风险提示:那条线在改 `交接单/` 目录(产出新交接单)+ 可能碰 `README.md` 共享台账 ⇒ 与 IM 五单的落地**有重叠面**,更要串行。
**✅ 13:5x 更正(同轮内已闭环)**:第二次抢锁**成功**(那条规划棒已自行 `--release-exec`)⇒ 四项拍板**已全部落地**,落点 = `04-调整方案/142 §六`(改为「待定 / 待拍板(2026-09-22 更新拍板结果)」)+ `IM群组-C §四-8`(场景三类都要 · `room_type` 三类各一份默认值)+ `IM群组-D §四-6·7`(E2EE 本期做 · 协议先行 · 三类各跑通一个)+ `IM群组-E §四-4·5`(**新增候选 C 实例内工作台面板** · 场景已拍板)+ `交接单/README.md` 台账(加「四项拍板」行)。锁已释放,**未 commit / 未 scp**。
⚠️ 遗留待确认一项:E2EE 形态档位(用户原话「看似不似最佳方案」)—— 我按「工程务实档」暂记,**已在回复里请用户确认**。
---
## 16:4x · **开新线:IM 线**(IM 稳定性实现 + 开源框架调研)
**用户原话**:「im具体如何实 才能在复杂环境下稳定 还需要调研同类开源框架,比如telegram等海外通讯项目,找个最新性能稳定最好的」
**本轮做了什么(开线棒;本会话上下文已 23.5 万 ⇒ 收口开线、不硬做深度调研)**
- **实搜 3 次,固化 8 条事实**(框架 5 + 我方约束 3),全文落入口 §3 ⇒ ⛔ 下一棒不必重搜。要点:
- **ejabberd 26.07**(2026-07-30 发布)— Erlang/OTP,官方称**单节点 200 万并发会话**(生产实测),WhatsApp / Nintendo / BBC 在用;XMPP + 400 XEP + 内建 MQTT/SIP/Matrix;社区版 GPLv2。
- **OpenIM**(Go 微服务)— msggateway(WS) + Kafka + MongoDB + Redis + etcd;官方口径单节点 **10 万+ 连接 / P99 < 100 ms**;**seq 全局序列 + 增量同步**;十万级大群。
- **Matrix** — Synapse(Python) / **Dendrite(Go)**;联邦 + 强制 E2EE;运维复杂度最高。
- 🔴 **Telegram 官方服务端不开源**;2026 第三方实现三条:**Opengram**(C#/.NET9 · layer 216 · 微服务) / **Ferrite**(C# · layer 214 · 494/732 方法) / **teamgram-server**(Go · layer 222 · **社区版仅私聊+基础群**,频道/超群在商业版)。
- 其他:Tinode / Rocket.Chat / Mattermost / Zulip / Openfire / Jackal / lhttp。
- 🔴 **我的初判(写进 §3-6/8,供规划棒核)**:除 **ejabberd 是单运行时**外,**其余候选都要外挂 3–5 个组件**(Kafka/Mongo/etcd/MinIO)⇒ 与我方**单实例硬顶 1024 MiB / 宿主 1870 MiB** 直接冲突 ⇒ **整框架引入代价高**;更现实 = **借机制**(seq 增量同步 / CQRS 削峰落 PG `FOR UPDATE SKIP LOCKED` / 无状态网关 / 心跳僵尸清理 / 背压熔断)+ 必要时只引**单二进制**实时层。
- **交付**:新建 **`接续入口_IM线_20260922.md`**(本工作区根,只此一份)—— §0 状态 / §1 用户原话三判据 T1–T3 / §2 本轮动作(第 1 棒 = 规划棒出交接单)/ §3 八条事实 / §4 未拍板三项 / §5 开工校验(含 bash 故障绕法)/ §6 不要重做。
- 锁:抢 ✓ → 释放 ✓|**未改码、未动 47/106、未 commit**。
**下一棒** = IM 线第 1 棒(规划棒:稳定性机制清单 + 框架判据矩阵 + 三案选型建议),已按 §3.1.1 登记一次性 automation(+6 分钟),只挂这一个。
**环境备忘(本轮反复用到)**:Bash 工具 shim 坏 ⇒ 抢/放锁走 `tmp/claim_lock.py`(subprocess 调 PortableGit bash + 补 PATH);`state.py` 与 python 脚本可直接跑;抢锁失败的「乱码 + rc=1」不等于锁被占。
---
## 17:0x · 用户更正口径:**机器可以换**(IM 线规划修正)
**用户原话**:「机器可以换 不要那这个限制规划」
⇒ **资源(单实例 1024 MiB / 宿主 1870 MiB)不再是选型否决项**。已落**入口文件 5 处**:
- **§0** 加口径更新行(明令:⛔ 不得以"装不下 / 现有机器跑不动"否掉方案)
- **§1 T3**:判据主位改为 **稳定性 · 性能 · 可运维性**,资源只作「需求测算」输出
- **§2-3**:三案改按**四维**比较(+ 资源需求=测算值)
- **§3-6**:原「资源硬约束」改写为「资源口径(现实基线仅供测算)」
- **§3-8**:借机制的**理由改为"机制更可控可验"**,⛔ 不再是"塞不下" ⇒ **整框架引入重新成为平等候选**
⚠️ **我上一轮的初判("整框架引入基本不可行")据此作废** —— 那条判断的地基就是资源约束,现已被用户撤掉;正确口径 = **自研薄内核 / 整框架引入 / 混合**三案**同台按四维比较**(ejabberd 单运行时 200 万并发实证、OpenIM 整装微服务都回到桌面上)。
已同步更新接续棒 prompt(automation `2798048a-0ac8-4515-85bd-b58375e756ac`,**只改 prompt、未动 scheduledAt** —— 避开 §4-⑦「改时间不触发」的坑)。
落点:本工作区入口文件(非文档库 ⇒ 免锁);未改码、未 commit。
---
## 13:33–13:5x · 插件投放与分库线 规划棒①(产出交接单,未启动执行)
**开工三步**:① `state.py` ✅(锁空闲 · HEAD `3d8f50e` · 47 上无异常)② 口径门禁 ✅(入口 md5 `1f62266bce87a35bab652b7e1133610c` 现算一致,**未改档**)③ 抢全局锁 ✅(13:34 起持有,13:5x 已 `--release-exec` 释放)。
**产出**:`dsh-server-docs/交接单/插件投放与分库线-①共享只读包库与插件数据面.md`(8 段 + §〇对齐 + §九实测 + §十 DB-03 差异 + §十一未决)+ `交接单/README.md §一` 新增一行。占号 `交接单/.lock-插件投放-①`。⛔ 零代码改动 · 零部署 · 未 commit。
**四条现场实测(入口 §5-3 要求,⛔ 全部实取,非推断)**
- **「用户插件文件夹」真身 = 不存在**:四个候选路径(`home/.dsh/plugins` / `ws/plugins` / `home/plugins` / `ws/.dsh/plugins`)在 47 实测**全部 MISS** ⇒ D4 必须新定落点(推荐 `home/.dsh/plugins/<pluginId>/`,与既存 `home/.dsh/mcn-plugin.db` 同族)。既有最接近的是 `home/.dsh/`(含 mcn-plugin.db 2.4M)· `home/skills/` · `home/.dsh-stage/`(20+ 份历史 tgz)。
- **pnpm store = 每用户一份**:`<userRoot>/ws/.local/share/pnpm/store/v3`(47 的那份 99M;`home/.pnpm-store` 8K 是旧位置残留)⇒ `runPnpmAs` 的 `HOME=<ws>` 决定(`business-plugins.ts:438/458`,注释 `:492-494`)。全库仅 2 份 store(47/106 各一)。
- **旧路径兼容**:profile `dependencies` 实测 5 条 `file:` 里**已有 1 条指向共享池**(`@softspark/dsh-file-preview → /var/lib/dshs/business-plugins/…tgz`)⇒ A2(`file:` 指共享目录)**不是新发明**;`node_modules/dsh-plugin-mcn-suite` 实测为**软链 → `.pnpm/…`** ⇒ dsh 解析软链正常(复核 144 §8.2①)。
- **端侧插件加载通路 = 无**:只有设备登录接入;桌面线载体未产出(工作区顶层只剩 `CODEBUDDY.md`/`docs/`/`_t.txt`)⇒ 序46 步骤 7 仍停 `client-artifact-missing`。
**本棒为 D2 落点顺带取到的两条高价值读数**
- **bwrap 全参(47 活实例逐字)**:`--tmpfs /var → /var/lib → /var/lib/dshs → /var/lib/dshs/users` → `--bind <userRoot>/tmp /tmp` → `--bind <userRoot> <userRoot>` → **`--ro-bind-try /var/lib/dshs/bundled-skills`** → `--ro-bind-try <profile>/{cordis.patch.yml,package.json,pnpm-lock.yaml}` → `--unshare-pid --chdir <userRoot>/ws` → `setpriv --reuid <uid>`。⇒ **共享插件库落点/挂法可逐字照抄 `bundled-skills`**(`orchestrator.ts:1052-1054`),且**必须排在 `--tmpfs /var/lib/dshs` 之后**(实测证实 144 §8.6③);**profile 三件策略文件已是 ro-bind** ⇒ 装配只能平台在正确那台机上写(= G4)。
- **47 与 106 各有一份候选池且内容不同**(47 五个含 storyforge;106 三个是 09-13/09-14 人工 scp 的旧包)⇒ **147 G2「内容出不了 Manager」的实证 + 版本漂移实证**。
**方案主干(写进单的判定)**
1. 🔑 **技能共享层已把 D1+D2+「同名替换(=P2 更新语义)」全落过一次**(`skills.ts:429-499` 上传/替换+`:539-548` 用户面合并三类)⇒ 本线**不是新设计,是复用该形态**,只多「依赖树」与「数据面」两件事。方案可行性由此大幅上调。
2. **卡点不在部署,在"谁在哪台机上动手"**:装插件那步(`runPnpmAs` `business-plugins.ts:454-461`)是 Manager 本机 `setpriv pnpm` ⇒ 跨机必 `ENOENT`;但**重启探活那条腿已经通了**(`supervisor.restartAndProbe` → `remote-spawner.ts:388` 按 `hostIdFor` → worker `agent.ts:459`)⇒ 只需补 `POST /plugins/apply`(模型 = 既有 `/restart-probe/:userId`),⛔ **不新开入站口**。
3. **D1 其实已是现状**:`GET /api/plugins/mine`(`:578`,`requireAuth`)走 `listBusinessPlugins()`,实测 SQL = `SELECT … FROM business_plugins ORDER BY name ASC`(`pg.ts:420` / `repo.ts:677`)**无任何过滤** ⇒ 任何登录用户今天就能看到候选池全量。⛔ 别"顺手加过滤"。
4. **三个"今天不存在"要新建**:`/var/lib/dshs/bundled-plugins`(47/106 都没有)· `dshs_pl_<pluginId>`(`p_*` 零命中)· D4 的「用户插件文件夹」。
5. **前置缺口 P1b**:worker 上实例要读写插件库 ⇒ 需「实例→控制面」数据 API,而该通道**今天为 0**(147 G3 / 序47 §②.1)⇒ 单里标 P1b 并**建议与序47 步 6–9 合批**。
**待用户拍板(单 §四-2,只有两条)**:① 共享包库版本目录策略(A 留 K=2~3 版 / B 只留最新 —— 倾向 A)② D4 落点(A `home/.dsh/plugins/<pluginId>/` / B `ws/plugins/<pluginId>/` —— 倾向 A)。
**收尾自检**:`docs-audit.py` 退出码 **1**,但**归因清楚且非本棒引入** —— 【1】档案编号冲突全部来自 `dsh-server-docs/skills/*/references/0N-*.md`(`git status` = `??` **未跟踪**,属**技能重组线在途新增**)与根级 `0N-*.md` 同名;【8】缺状态 33 个在 `04-调整方案/`。**新单与 README 改动在 audit 输出里零命中**。⛔ 按「只做被明确要求的事」**未去改他线的文件**。
**⚠️ 给下一位**:本棒持有全局执行锁 13:34–13:5x;**已释放**。IM 群组线 13:40 因本锁按 R9 停手并把四项拍板只记进了本日志(见上一节)⇒ **该线现已可继续**,其四项拍板待落到 `04-调整方案/142 §六` 等五处。
---
## 16:37–16:5x · 插件投放与分库线 · 拍板闭合 + 排执行棒①
**用户回话**:「1 B,2 我说的是 平台用户的文件路径 和你说的是一个吗」。
**① 版本策略 = B(只保留最新一版)** —— 用户原话「1 B」。落档:单 §4.1 新增 `D-i`;§4.2-1 由"倾向 A"改为"✅ 拍板 = B",并写明 ⛔ 不许因"要回滚"反向在共享库留多版本;**§五 S5-a-3 回滚整段按 B 档改写**(原文"① 回滚指向旧版本目录"在 B 档下**不成立** —— 会做出不存在的目录)⇒ 改为:从运维备份面 `/opt/dsh/backups/plugins/<pkg>/<ver>.tgz` 取旧包 → 覆盖共享库**固定文件名** `bundled-plugins/<pkg>/pkg.tgz`(⛔ 路径不带版本号,否则老用户 `file:` 悬空)→ 对受影响用户**显式 `pnpm add file:` 重装**(⛔ 不能只 `pnpm install`,路径未变 pnpm 未必感知)→ 库不回滚。B 档**强制两条前置**:(a) 每次上传先落一份 tgz 到 `/opt/dsh/backups/plugins/`(否则无回滚素材)(b) 台账必须记当前版本号。无灰度 = 已接受的代价,③ 须报"影响 N 用户"。
**② 口径澄清:是同一件事** —— 用户说的「平台用户的文件路径」= **平台用户(租户)自己的目录** `<userRoot>` = 实测 `/var/lib/dshs/users/<userId>/`;候选 A/B **都长在这棵树下**,差别只在 home 面 vs ws 面。⛔ 别读成"插件作者侧路径"或共享包库(D2)那条。**D4 落点自决 = A**(`<userRoot>/home/.dsh/plugins/<pluginId>/`,单 §4.1 `D-j`,**可推翻**),判据 = 144 §6.2 五问第 1 问 + 与既存 `home/.dsh/mcn-plugin.db` 同族。
⚠️ 顺带钉死一条易混点:**平台侧路径 = 实例内可见路径(同一绝对路径)** —— bwrap 实测 `--bind <userRoot> <userRoot>` 同路径绑定、`--chdir <userRoot>/ws`,**不重映射**;但实例内 `HOME` = `ws`(`business-plugins.ts:438/458`)⇒ 插件 host 半边 ⛔ 禁 `homedir()` 拼路径,只走 SDK `im.paths.pluginData()`。
**改动文件(⛔ 零代码 / 零部署 / 未 commit)**
- `dsh-server-docs/交接单/插件投放与分库线-①共享只读包库与插件数据面.md`:状态行 · §4.1 加 `D-i`/`D-j` · §4.2 改题并加"平台侧 vs 实例内路径"注 · S5-a-3 回滚重写 · S5-b 标已定 A · §十一-1 改 ✅ · **修正一处自述错误**(原写"docs-audit 退出码 0" ⇒ 实为 **1**,两条均属他线存量,本单零命中)。
- `接续入口_插件投放与分库线_20260922.md`:§4 D4 下**加一行日期补记**(只记实况 + 指向单 §4.1/§4.2,⛔ 未改任何已拍板口径行)。
**已排执行棒(本轮收口 + 15 min)**:automation `6fb3f4b2-da91-4515-b8ac-8bbb81628777`「插件投放与分库线 · 执行棒①」`scheduledAt=2026-09-22T16:52`,`cwds` = 本工作区;prompt 只给指针(入口 §3–§6 + 单 §〇/§四/§五),要求抢锁、照单范围执行、撞 P1b/序47 就停手报告、收尾走交付门禁。⚠️ 本棒会占用锁 ⇒ 已有会话请让路。
**⚠️ 顺带发现(未处置,待你发话)**:automation 列表里 **13 个一次性任务全部 `ACTIVE` 且 scheduledAt 早已过期**(carbon 线 3 个 09-21 的 · 浮层线 2 个 · 技能重组线 10:13 · 本线规划棒 13:33 · MCN 收口 09-21 06:45 …)⇒ 已完成的棒没有下线,与"同一时刻只挂一个"的判据相冲,建议清一遍(⛔ 未经你发话我没有动它们)。
## 17:0x–17:3x · 🔴 插件投放与分库线 · 执行棒①(automation `6fb3f4b2`)—— **S1 全绿 · S2 全绿并已上线**
**入口**:`接续入口_插件投放与分库线_20260922.md` | **执行依据**:`交接单/插件投放与分库线-①共享只读包库与插件数据面.md`(§〇 对齐节 + §四 D-i/D-j 已闭合 + §五 S1 起)。**逐条读数全在单 §十二**,这里只留状态与判据。
### 落地的(已上线,两节点)
- **S1 承重墙**:`config.ts` 加 `bundledPluginDir`(默认 `<dataRoot>/bundled-plugins`)+ `orchestrator.ts` env 注入 + `--ro-bind-try`(紧邻 `bundled-skills`、在 `--bind root root` 之后)+ 中间目录集合收进去;47/106 各建 `0755 root:root` 目录;两节点 env 文件加 `DSHS_BUNDLED_PLUGIN_DIR`(已备份)。
- **S2 共享只读包库(D2 核心)**:`business-plugins.ts` 新增 `materializeShared` + `.manifest.json` + `POST /api/plugins/business/:id/share`(+body 形态 `POST /api/plugins/business/share`)+ `DELETE …/share` + `GET /api/plugins/shared`;上传钩子补落 B 档回滚素材。
- **实测已投放**:`[email protected]`(657 文件)· `@dsh-local/[email protected]`(117 文件),各 **1 份实体**、`root:root 755`、依赖树 `.pnpm` 就位、回滚 tgz 在 `/opt/dsh/backups/plugins/`。
- **S1-E 真机取证(106 非 admin 用户 `4092b965`/uid 100002)**:mountinfo `ro,nosuid,nodev` + 实例内 `ls` 可见 + `touch` = **`Read-only file system`** + `grep -c bundled-plugins` = 2。
- **零回归**:`npm test` **248/247 pass/0 fail/1 skip**(与开工基线同)。
### 🔴 必须记住的四个坑(详见 `PLAYBOOK-实例与插件坑.md §22`,⛔ 别重踩)
1. **部署真源 = `/opt/dshs/lib/`**(`lib/` 被 gitignore);`/opt/dshs` 的 git HEAD 是噪声。
2. **两节点 `lib/` 会静默分叉** —— **实测 106 缺 14 个 `.js`、33 个文件内容不同** ⇒ 新 `config.js` import 缺失文件 ⇒ `dshs-worker` **崩溃循环**。**已整包同步修好**(106 现与 47 逐字节一致);⛔ **部署后必须全量对账**(`is-active` 看一次不算)。
3. **沙箱内验收 `nsenter` 挑错 PID = 读的是宿主(假阳性)** —— bwrap 的父监控进程在外面;必须用 `ps -eo pid,comm,args | awk '$2=="node" && /dsh --profile/'` 那个 pid,且先确认 `/proc/<pid>/mountinfo` 里有 `bundled-skills`。
4. **共享层物化四个必修**:`--config.auto-install-peers=false`(`@deepseek-ai/dsh-client-*` 是 optional peer,公共源没有 `^0.1.2-rc.1`)| scoped 包名必须走 body 路由(`:id` 不吃 `/`)| 解包后 `chown -R root:root`(tar 按归档 uid 还原)| share 时兜底补回滚素材。
### 🔴 卡住 / 待拍板(⛔ 未自决)
- **S4 建库**:`dshs` 角色 **`rolcreatedb=f`** ⇒ 平台建不了 `dshs_pl_*`。放开要**扩权**(授 `CREATEDB` 或新建角色+新口令进 drop-in)⇒ 命中 **R5 权限只准收窄** + 边界外④ ⇒ **停下报告**。
- **S4-3 / D7**:`P1B_INSTANCE_TO_CONTROLPLANE_API_MISSING`(单里已标的前置缺口,未自造第二处身份判据)。
- **S5-b 第 3 条**:`PLUGIN_SDK_NOT_IN_THIS_REPO` —— 本仓 `im.paths`/`pluginData` **零命中**;属 dsh 宿主面,**R2 零改动** ⇒ 退出本单可动范围(D4 落点已定,可由插件侧按 `DSH_HOME` 约定自行拼)。
- **未做**:S3 跨机装配(`OUT_OF_BUDGET_S3`,本单剩余最大一块,留给下一棒)· S5-a 兼容矩阵 · S6 门户三段 UI · 跨节点内容分发(按 §十一-3 登记后续)。
### 其它
- 📄 单 §一 状态改 🟡、**§十二 执行回报已回填**(含 §六 验收逐条 ✅/❌ 与具名原因码);`交接单/README.md §一` 行已更新;`数据库/DB-03` 头部加**执行状态块**(数据面尚未开工,⛔ 别当已实现读)。
- ⛔ 未 commit / push;⛔ 未动他线文件;锁 `--claim-exec` 起、`--release-exec` 收。
- ⚠️ 顺手发现(未动手,只报告):`bt-server` ssh 配置与 `config/platform.env:61` 的 `32022` 均已陈旧(覆盖网络线 R4 已回收该口)。**已改用 `[email protected]:22`**。
## 17:4x · IM 线第 1 棒(规划棒)收口 ✅
**产出**:`dsh-server-docs/交接单/IM群组-稳定性机制与框架选型.md`(新增)
- **T1 稳定性机制清单 25 条**(连接 A1–A5 / 消息 B1–B7 / 存储 C1–C6 / 跨区 D1–D4 / Agent 插件 E1–E3),每条 = 机制 → 可复现判据 → 落点(`file:line` 或「待新建」)
- **T2 框架判据矩阵**:7 类候选 × 7 条判据轴。判定 = Telegram 系三支**出局**(方向不匹配:强绑账号体系 + 3–5 组件 + 无插件扩展点)、OpenIM **出局**(不支持 E2EE,与用户已拍板「本期做」硬冲突)
- **T3 三案**(自研薄内核 / 整框架引入 / 混合)各写优点与缺点 + 四维比较(稳定性 · 性能 · 运维复杂度 · 资源需求测算)
- **建议(自决,可推翻)** = **案 3 形态 + 案 1 路径**:连接层写成可替换(Centrifugo 类当**可选后端**),内核自研,⛔ 不现在引整框架
- 四个实验 **E-1~E-4**,顺序 E-3 → E-1 → E-2 → E-4,一律落 **106**
**判据轴的口径(本次关键判断)**:决定性轴 = ① 身份可复用性 ② 权威存储同库性 ③ 七扩展点承载能力 —— ⛔ 不用 star / 项目年龄 / 生态规模排序(依据 `dsh-decision-method §4.6`:「热度型论据不构成选型主依据」)。
**其他落点**:`交接单/README.md §一` 加一行;入口 `接续入口_IM线_20260922.md` §0 加收口行 + §2 推进到第 2 棒。
**下一棒**:automation `b656009b-ecdd-4f12-a077-d12e8294dce8`(一次性 · 2026-09-22T17:52)= 执行棒跑 E-3 + E-1。
**过程教训(值得记)**:Edit 的 `old_string` 命中了**非目标位置** —— 用「资源不是选型否决项。现实基线(仅供需求测算)」去锚 §0,但同一文本也存在于 §3 第 6 条 ⇒ 状态行被插进了 §3 事实段(已修复)。⇒ **锚点必须带足够上下文或选全局唯一片段**,⛔ 不用「看起来眼熟」的句子。
**检查读数**:`docs-audit.py` rc=1,但**全部为存量告警**(编号占用 / 档案元信息缺失 / 体量分布),报告里**不含本单文件名**,且【6】交叉引用 ✓ 无悬空 ⇒ **本单零新增问题**。另:hands-off 预检中「越界未提交改动 71 个」为工作区存量的跨线改动,与本棒无关(⛔ 未 push)。
---
## 18:0x · 插件投放与分库线(执行棒①续 · automation `6fb3f4b2`)—— 数据面管理面流程修订
- **锁**:全局执行锁被「IM线-第2棒」(17:53 起)占用 ⇒ 按 R9 **未落笔任何共享文件**(交接单 / DB-03 / 代码仓 / 服务器全未动)。本轮 = 只读取证 + 设计件 + 工作区记忆。
- **用户新增口径(本轮触发)**:admin 全流程自闭环 —— 上传 → 检测通过 → 点「建库」按钮 → **建库成功才能开启**;插件加「更新」按钮 → 更新也检测 → 通过后**告知数据库改动** → 「确认执行」按钮。⇒ 交接单 `§十-D1`「建库 = 上传钩子」**须改写**为 admin 显式按钮。
- **产出**:`交付物/数据面管理面-建库与更新流程-20260922.md`(7 态状态机 · 检测新增第 3 项「数据面声明校验」· `dsh.data` 静态声明格式 · API 契约 · 两段式「预演→确认执行」· 改动文件清单 · 需回改条款 · 权限三候选取舍)。
- **实测读数(47)**:`dshs` 角色 `rolsuper=f / rolcreatedb=f / rolcreaterole=f`;`postgres` 超级用户;`dshs` **拥有** `dshs` 库 ⇒ `has_database_privilege('dshs','dshs','CREATE')=t`(**建 schema 零扩权**);`dshs_pl_%` 库数 = **0**;PG **13.23**。`plugin_datastores` / `plugin_data_audit` **均不存在**(信息模式零命中)。
- **🔴 新发现(版本元数据脱钩)**:`business_plugins` 记 mcn-suite `version=0.3.9 / file_size=1953267 / updated_at=09-13 00:02`,磁盘池内 tgz 实为 `0.3.13 / 1989680B / mtime=09-20 22:13`,且 **sha256 与共享层 `.manifest.json` 相同**(= 同一份文件)⇒ 该 tgz **09-20 被平台外方式替换**、记录从未同步。对照 `@dsh-local/storyforge` 记录与磁盘**逐项一致** ⇒ 走上传 API 的路径本身没问题。⇒ 更新/回滚判定只认**内容指纹**,⛔ 不用记录字段。已沉淀 **PLAYBOOK §22.5**。
- **待拍板(唯一一项)**:建库权限怎么给 —— **A** `ALTER ROLE dshs CREATEDB`(最简、任意库名)/**C** 改「一插件一 schema」(零扩权、today 可跑,但降级隔离且改定稿)/**D** `SECURITY DEFINER` 建库函数 + `GRANT EXECUTE`(扩权最小、库名强约束、不动定稿)。**我选 D**。
- **顺带修正**:工作区 MEMORY 旧口径「`ls`/`cat` 也 not found ⇒ export 救不回、改走 PowerShell」**不成立** —— 本机 09-22 复测 `export PATH=<PortableGit 1.2.0 的 usr/bin:bin>:$PATH` **一次即恢复**;真判据 = **每条 bash 命令都要前置**(工具调用间不共享 shell 状态)。已回改 MEMORY + 新增 **PLAYBOOK §22.6**。
---
## 18:2x · IM 线(第 2 棒 · 执行棒 · automation `b656009b`)—— 交接单 §八 步 1–4 完成
- **锁**:抢到并已释放(17:5x–18:2x 持锁)。⛔ 47 只读、未改一行;⛔ 未 commit/push/scp;⛔ 未改 `src/**`。
- **前置核实 4/5**:`src/im/` 不存在 ✅|`schema.ts` 最新 = **v13**(⇒ 不触发「改去 A 单」)✅|upgrade 回调 **`proxy.ts:759`**(规划读数 718 已漂移)✅|`npm test` = **248 / 过 247 / 败 0 / 跳过 1**(53.5 s)✅|🔴 第 4 条(47 上 `dshs` 的 `\dt`)**未核实**。
- 🔴 **实测环境漂移(两条,都会打穿既有脚本/文档)**:① **47 的 sshd 已从 32022 漂到 22 端口**(32022 双方向 refused;`~/.ssh/config` 的 `bt-server` 别名**已失效**,需改 `Port 22`)。② 47 上 **`docker ps` 无任何容器**、`dshs-pg` 容器不存在 —— PG 是**本机进程**(`127.0.0.1:15432`,pid 662048),且 `su postgres` 无 unix socket 可用、`/root/.pgpass` 不存在 ⇒ 凡「`docker exec dshs-pg psql …`」写法的取数命令**全部失败**。⛔ 未取用凭据,故该前置项如实记「未核实」+ 替代证据(`schema.ts` 内三表 DDL 命中数 = **0**)。
- **E-3 = 能**:ejabberd 26.07 可用既有 `users` 作外部认证源,且**不落地自己的账号表**(干净 mnesia 起点下 `passwd` 表不存在;探测库全程只有既有 `users`)。**协议真源 = `src/extauth.erl`**:`open_port({spawn,Prog},[{packet,2}])` ⇒ 双向 2 字节大端长度前缀;请求 `auth:user:server:password`;**应答载荷必须 2 字节 `[0,N]`**(端口 list 模式,匹配 `{data,[0,N]}`)—— 只回 1 字节 `[N]` 会得 `unexpected_response`。🔴 **附带发现**:ejabberd 对 `check_password` **有进程内缓存**(改既有表的口令**不立刻生效**、`systemctl restart ejabberd` 后立刻生效)⇒ 案 2 需追加「查缓存开关/TTL」前置项。
- **E-1(106 实测 · 零依赖手写帧自研 WS)**:依据 = 平台 `dependencies` **无 `ws`**(只有 fastify/pg/better-sqlite3 等)⇒ 自研形态即零依赖。1k/5k/10k 三档 **upgrade 全成功、fail 0、errors 0、心跳超时率 0**;RSS 58.6→70.7 / 95.6 / **131.8 MiB**(边际 **7.5 KiB/连接**);**P99 = 45.276 / 256.314 / 421.109 ms**;**P99 ≈ 事件循环最大滞后**(34.7/258.6/405.2)⇒ 瓶颈 = **单进程全量扇出的排队**,⛔ 不是连接数供给。协议另用 **Node 内建 `WebSocket`(undici,独立实现)**交叉验证通过。
- **新机制判据(补充探针)**:客户端进程 SIGKILL 后 **≤3 s** 全部回收,走**写失败**路径(`errors=200`)而**非**心跳(`hbTimeouts=0`);观测窗口短于扇出间隔时 TCP 半开**不产生 `close` 事件** ⇒ **判离线必须有心跳兜底**(支撑机制 A1/A4)。
- **产出**:交接单 `IM群组-稳定性机制与框架选型.md` 追加 **§十二 执行回报**(八段全)+ **§十三 断言名清单草案(18/18,A5+B7+C6)**;入口 `接续入口_IM线_20260922.md` §0/§2 已推进。⚠️ **单外缺陷(只报告未动手)**:该单 §八 步 2 写「20 条」与 §九「18 条」矛盾,「20」为笔误。
- **106 足迹(已停用未删除,待拍板)**:`/opt/ejabberd`(4M) + `/opt/ejabberd-26.07`(53M) + `/var/lib/pgsql/im-e3probe`(47M) + `/root/im-e1`(212K) + `/root/im-e3`(22M) + 系统用户 `ejabberd` + 单元 `ejabberd.service`(已 stop+disable)≈ **126 MB**。⚠️ 厂商 `.run` 安装器**越过声明的 `/root/im-e3` 范围**装到 `/opt` 并建系统用户/单元(`--help` 本身即触发安装,事前无法预知)。
- **脚本落点(⚠️ 仅工作区 `tmp/`,未进代码仓)**:`tmp/im-e1/`(server / wsworker / run / crosscheck / postkill / sum)+ `tmp/im-e3/`(extauth.py / users.sql / mkhash.mjs / e3_*.sh / probe47.sh)。
- **下一棒**:automation **`21443815-acec-4069-94ad-6be3242c9d5d`**(一次性 · 18:23)= 第 3 棒(E-2 / E-4 / 二次判定 / 步 8 回填)。
- **顺带**:本机 bash shim 本轮**整体可用**(`ls`/`cat`/`head`/`grep`/`md5sum`/`tail` 均正常)⇒ 入口 §5「shim 已坏」应改为「**时好时坏**」。
---
## 19:0x · IM 线第 3 棒(执行棒续)收口 ✅ —— E-2 实测 + 二次判定 + E-4 + 步 8 回填
- **本轮动作** = 选型单 §八 步 5–8(步 1–4 属第 2 棒)。⛔ 未改 `src/**`、未动 47、未部署、未 commit/push/scp。实验全落 106。
- **E-2 结论(Centrifugo v6.9.6,106 同机同脚本)**:10k 连接 upgrade 全成功、**零掉线**(服务端权威 `POST /api/channels` 稳态 `num_clients` = 1000/5000/10000 = 目标值;`open_fds` = 1009/5008/10009)。客户端观测 **P99 = 28.9 / 68.1 / 111.9 ms**、扇出离散中位 **17.2 / 57.9 / 97.8 ms** ⇒ 均优于自研(**1/2.7、1/1.9**);代价 = **边际每连接 70.1 KiB vs 自研 5.9 KiB(11.9×)**、稳态总量 **750.2 vs 116.0 MiB(6.5×)**、goroutine 数 = 2N+89。
- 🔴 **保活方向相反(实测,非文档推断)**:Centrifugo **不发服务端 WS ping**(`wsPingRecv=0`,含单连接 25 s 探针);判别实验 ① 客户端**不发** ping ⇒ **26 s 内 10000/10000 全被断开**(`tcp_est` 10000→0)② 客户端**按连接计时**发 ping ⇒ 连续 66 s `num_clients` 恒 10000、`closed=0`。
⚠️ **踩坑留痕**:最初客户端用**整进程统一计时**发首 ping(固定第 10 s)与认证竞争 ⇒ t≈10 s 一次掉 **2773** 条 ⇒ **首轮 6 轮读数全废**(改成"认证成功后才起计时"即零掉线)。教训 = **A1 判据必须把保活计时起点绑到该连接自己的认证完成时刻**。
- **二次判定(陈述句)= 本期不引入外部连接层组件**(维持案 3 形态 + 案 1 路径,连接层写成**可替换接口**)。依据三条:① E-1 三档 `服务端 P99 ≈ 事件循环最大滞后`(10k **340.3 vs 342.5 ms**)⇒ 瓶颈是**可就地解决的扇出排队**,⛔ 不是连接供给(10k 只多 57 MiB);② E-2 的延时代价换不来"再加一个进程/端口/配置/JWT-APIKey/Prometheus/升级面 + 认证生效时机不由我们控制"(E-3 的 `check_password` 进程内缓存)—— ⚠️ 按「机器可换」口径**内存不是否决项**;③ 保活计时语义有硬要求。**回退判据(可证伪)**:扇出改造后 10k 档 P99 > **150 ms** 或离散中位 > **120 ms** ⇒ 引入。**建议 E-5** = 自研扇出分片批写,同机同脚本复测。
- **E-4 结论=可行**:E2EE 最小路径 = 三层密钥(设备身份 Ed25519 / 房间 `DK(room_id, epoch)` / **成员公钥包裹的密钥包**)+ 两张新表(`im_room_keys` / `im_agent_grants`)+「**加人不换钥、移除必换钥、轮换先于消息可见**」+ 客户端只接受 `epoch >= 本地最大`;出 **8 条可断言路径**(E4-1…E4-8);**relay 零改动**(结构性只见密文)。⚠️ 形态档位(工程务实档 vs 双棘轮)**仍未拍板**。
- **步 8 回填**:§四 **25 条机制全部有归属** —— A 单 16 + B 单 5 + C 单 3 + D 单 3 + E 单 2(共 29 行;组 E 与 A3 跨"实现层/契约层/客户端层"两端各写一条并注明同源);⛔ 未改 A–E 任一单既有结论。
- **产出**:交接单 `IM群组-稳定性机制与框架选型.md` 追加 **§十二 执行回报(IM线-第3棒)**(含 E-2 对照表 + 归因结论 + 二次判定 + E-4 + 回填行号表 + 遗留 7 条);A–E 五单各新增 `### 六.1 追加判据`;入口 §0/§2 已推进。
- **106 足迹新增(已停用未删除,待拍板)**:`/root/im-e2` ≈ **90 MB**(centrifugo 二进制 67 MB + tar.gz 23.5 MB + 读数与脚本);`ss -lntp` 无 180xx 监听。本机副本 = 工作区 `tmp/im-e2/`(`e2worker.mjs` 双协议客户端 / `e2pub.mjs` / `e2run.mjs` / `e2sum.py` / `config.json` / `hb.sh` / `tl.sh`)。
- ⚠️ **两条环境事实**:① 登记门禁 `handoff-guard.sh` **必须带 `ME="<线名>-第N棒"`** —— 不带会因「锁被自己占用」**误报不可放行**;② `dsh-server-docs/交接单/` 在 `.gitignore:45` 内 ⇒ 该目录文件**不会**出现在 guard 的「我声明的」列表(属正常,非缺陷)。③ 106 上 Centrifugo v6 配置键已改位:`client.token.hmac_secret_key` / `http_api.key` / 端口用 `--http_server.port`(顶层 `token_hmac_secret_key`、`api_key` 均**不被识别**且会连带禁用 HMAC)。
- **下一棒**:automation **`db244c60-db22-4d7d-87a9-bced514bbea4`**(一次性 · 19:13)= 第 4 棒(E-5 扇出分片对照 + 补跑 §九 判据 6/7 平台回归)。
## 19:2x · IM 线第 4 棒(执行棒)收口 ✅ —— E-5 扇出分片对照(**回退判据触发**)+ 判据 6/7 补跑
- **本轮动作** = 主任务 **E-5(自研扇出分片对照实验)** + 补跑选型单 §九 判据 6/7。⛔ 未改 `src/**`、⛔ 未改 `/root/im-e1/*.mjs`、未动 47、未部署、未 commit/push/scp。实验全落 106。
- **E-5 实现**:106 `/root/im-e2/server-shard.mjs`(**新建** 268 行;蓝本 `/root/im-e1/server.mjs`)。改造 = ①**分片扇出**(连接按 `id % SHARDS` 分片,一轮 = `SHARDS` 个独立 `setImmediate` 轮次)②**同片批写**(同片共享同一 frame Buffer + 每连接 `cork/uncork`;`BATCH=0` 可关)。`/metrics` 原字段逐条不变(新增只有 `shard*` 前缀诊断字段),`msgsSent` 语义核对 = 6×N ✅。本机副本 `tmp/im-e5/`(脚本 + `out/e2-raw-*.json` 十份)。
- 🔴 **核心结论(回退判据被触发)**:**同会话**内 E-1 基线复跑 10k = 客户端 P99 **268.6** / 离散中位 **171.8** / 滞后 349.7 / RSS 115.9;分片版两跑 = P99 **337.6 / 359.4**、离散中位 **227.6 / 225.5**、滞后 **27.0 / 19.9**、RSS 107.9 / 132.6 ⇒ **判据(P99 ≤150 / 中位 ≤120)两项均未达线,且相对基线变差**。1k 也变差(49.8 > 44.7);5k P99 略好(196.1 < 215.6)但离散中位变差(123.2 > 100.7)。
- 🔴 **被证伪的是「分时」这一手法(重要,接手别重复)**:滞后 349.7 → **13.0–46.9 ms**(13–27×)**但端到端整圈反变长** —— 整圈墙钟由 `write()` 的**绝对调用总量**决定、不由事件循环是否阻塞决定;拆成 S 段后每段之后要先让出 loop 抽干该片客户端的回包(ack/pong)才轮到下一段 ⇒ 最后一段到达更晚。**分片数 4/8/16 与批写开/关四个变体全部无改善**(P99 330.2–359.4、中位 213.8–230.1)⇒ **A 候选(自研)的正确形态是「并行(多进程)」,⛔ 不是继续切细"分时"**。
- ⚠️ **RSS 判为噪声内**(10k 五变体 107.9–132.8 vs 基线 115.9 ⇒ 跨跑方差 **±12 MiB / ±10%**)⇒ 后续内存判据**必须重复 ≥3 次取中位**,⛔ 单次不定论。**基线本身也有 ±20% 跑间方差**(5k P99 182.4→215.6、10k 299.7→268.6)⇒ **跨会话直接相减会把方差当效果,对照必须同会话**。
- 🔴 **门禁执行**:改造判为**负向**(有档位 P99 变差)⇒ 停下复盘、⛔ 未硬推;引入外部组件属**影响面扩大** ⇒ **⛔ 未擅自引入**,三条候选(A 就地继续优化(多进程并行)/B 引入 Centrifugo 类外部连接层/C 混合)各带优缺点**竖排成段**交用户拍板(收在选型单 §十二 第4棒 段末「待拍板」)。
- **判据 6 / 7**:判据 6 ✅ `npm test`(Node v22.22.2)= **248 / 247 过 / 0 败 / 1 跳过 / 53.5 s**,与第 2 棒基线一致;判据 7 ⚠️ **测试集内无该用例 ⇒ 未覆盖** —— `grep -rin "101" test/` 零命中、`grep -rln "proxy" test/` 零命中(`proxy.js` 只由 `scripts/verify-inject.cjs` 做静态校验);最接近的既有用例 = `test/relay.test.mjs:1383 rawWsProbe(...)`(**中继侧** WS upgrade,⛔ 不是"用户→自己实例"子域 upgrade);⛔ 未自造用例。
- **产出**:选型单新增 **§十二 执行回报(IM线-第4棒)**(改造点对齐表 / 三档同会话对照表 / 10k 六变体归因表 / 回退判据判定 / 判据 6-7 / 遗留 6 条 / 待拍板三候选)+ 第 3 棒 §十二-5「回退判据」处追加 🔻 块(⛔ 原文未改);入口 §0/§2 已推进。
- ⚠️ **新发现单内不一致(只报告未动手)**:A 单 §五-1 验证句与 §六 判据 1 写「新库 `schema_migrations` 最新 = 12」,而 §〇-5 定本单取 **v14** ⇒ 疑为旧值残留(已写进第 5 棒 prompt 让其按 v14 判断并在回报里指出)。
- **106 足迹**:本轮仅在 `/root/im-e2/` **新增 1 个脚本 + 10 份读数 JSON**;全部进程已停(`ss -lntp` 无 180xx、`pgrep` 无实验进程);是否清理 `im-e1`/`im-e2`/`ejabberd` 遗留足迹仍属**待拍板项**(未执行)。
- **下一棒**:automation **`e1c1b3a5-0c11-4544-8b28-6fa80684d9de`**(一次性 · 19:33)= 第 5 棒(**开工 A 单**:房间内核与 DB ⇒ `src/db/schema.ts` v14 + `src/im/**` + `test/im-store.test.mjs`)。
---
## 19:3x · 插件投放与分库线 —— 建库权限**已拍板 = D**(用户原话「D」)
- **拍板内容**:不自研 sudo helper、不给 `dshs` 角色 `CREATEDB`,改用 **`SECURITY DEFINER` 建库函数 + `GRANT EXECUTE`** —— 扩权面最小且库名被白名单强约束,同时保住定稿「一插件一库」。
- **产出**:`交付物/插件建库权限-D档落地Runbook-20260922.md`(前置实测 · 幂等 DDL · 7 条验收 · 回滚 · 必须同步落的三处文档 · 平台侧错误码映射)。
- **新取到的前置读数**:
- 超级用户通路 **可用** —— `su postgres -c 'cd /tmp && psql -h /var/run/postgresql -p 15432 -d dshs …'` ⇒ `current_user=postgres`(`local all all peer` + socket `/var/run/postgresql/.s.PGSQL.15432`)。⚠️ **必须先 `cd /tmp`**,否则 `su` 切目录被拒 ⇒ 误报「No such file or directory」。
- `pg_hba` = `host all all 127.0.0.1/32 scram-sha-256` ⇒ `dshs` 对**新库** `dshs_pl_*` 也能连(不必改 hba)。
- `dshs` 角色 **无任何角色成员**(无旁路);既有 `dshs_%` 函数 **0 条**。
- 🔴 **PG13 默认 `public` schema 上 PUBLIC 有 CREATE**(ACL `{postgres=UC/postgres,=UC/postgres}`)⇒ **函数不得放 `public`**,改放 `postgres` 拥有的私有 schema `dshs_int`(PUBLIC 已 REVOKE、只给 `dshs` USAGE);函数体 `SET search_path = pg_catalog`。
- PG 数据目录 = `/var/lib/dshs-pg`(非 `/etc/postgresql/13/main` —— 旧写法找不到文件)。
- **🔴 未执行**:全局执行锁已被 **IM线-第5棒**(19:33 起)接手占用(该线在连续跑棒:第2棒 17:53 → 第5棒 19:33)⇒ 动服务器受 R9 管辖 ⇒ Runbook 只备不跑。
- **未登记下一棒**(自决):本线剩余动作**全部**依赖「文档/代码/服务器」三类,撞他人锁必空跑 ⇒ 登记只烧钱;Runbook 已到「照抄即可执行」程度,锁一释放即可跑。
## 19:4x · IM 线第 5 棒(执行棒)收口 ✅ —— **A 单落地**(v14 房间内核 + `src/im/**`),五单合流第一单开工
- **本轮动作** = 开工 **A 单**(`交接单/IM群组-A-房间内核与DB.md`):5 条只读前置核实 ⇒ 步骤 1–7 ⇒ §六(含 §六.1)验收 ⇒ §八 格式回填(新建 §十 执行回报)。
- **交付**(`D:\github\dsh_shenxian`,⛔ 未 commit / push / 部署):`src/db/schema.ts` 追加 **v14**(三表 + 索引,**192 增 / 0 删**)|新建 **`src/im/{types,db,store,clock}.ts`(963 行)**|新建 **`test/im-store.test.mjs`(552 行 / 23 用例)**|`package.json` 的 `test`/`verify` 各登记 1 处。
- **判据读数**:`npm run build` rc=0 | `node --test test/im-store.test.mjs` = 23/22 过/0 败/1 跳过 | `npm test`(Node 22)= **271/269 过/0 败/2 跳过**(基线 248 ⇒ +23 全为本单)| `grep -c "^ { version:"` = **14** | 全仓改动 72 → 74(增量恰为本单 2 个已跟踪文件)。
- 🔴 **两条执行口径(已写进该单 §十.7)**:
- **`src/im/db.ts` 是本单为「只经 db 适配层」立的窄端口** —— 既有 `DbAdapter` 是表级接口、⛔ 无通用 SQL 面,而 §三 把改动面限死为「`schema.ts` + `src/im/**`」⇒ 不能往 `adapter.ts`/`sqlite.ts`/`pg.ts` 加方法。端口只有 `all`/`run`/`exec`(SQL 用 `?`),SQLite 绑定走既有 `openDatabase`(顺带 WAL + 外键 + 迁移),PG 绑定走既有依赖 `pg`(`?`→`$n` 翻译,跳过字符串字面量)。**换后端只换该文件,`store.ts` 一行不动。**
- **判据 3「不同到达序 ⇒ 同一数组」在 DB 层的分界**:§四-6 定「`seq` 由 DB 按到达序分配」⇒ 两种到达序的 `seq` **本来就不同**(既定设计,非缺陷)。故拆成两条可机械断言的:① `listMessages` 恒按 `seq` 严格递增;② 规范序函数 `orderDrafts` 对同一集合的 3 种到达序输出同一数组(**含落库往返版**)。
- 🔴 **对 §四-5 的一处自决(可推翻,已写成显式用例)**:`id = sha256(authorId ‖ canonical(payload))` —— **含作者**。纯内容哈希会让同房不同作者的同文消息互相吞掉(agent 的「收到 / 好的」很常见)= 静默丢消息;改后同作者同内容仍恰好 1 行(判据 2/9 全保持)。`canonicalJson` 递归排序键 ⇒ 键序不同内容相同仍去重。
- ⚠️ **卡点(既有、与本单无关)**:`npm run verify` rc=**1**,卡在 `scripts/verify-platform-admin-section.mjs`(`require()` + 顶层 await ⇒ `ERR_AMBIGUOUS_MODULE_SYNTAX`;该文件未经改动)⇒ 链路其后 6 步逐条补跑 **rc 全 0**,`check-layering` **✅ 无新增违规**(仅 ⚠️ `src/im/*` 4 文件未归类,不影响退出码)。⛔ 未顺手修(超 §三 改动面)—— ⚠️ **后续任何棒别再把它误判成本次改动引起的**。
- ⚠️ **待报告未改**:① A 单 §五-1/§六-1 的「最新 = 12」是 09-20 旧值残留,**实测 14**;② `交接单/README.md §一` 里 A 单仍「⏳ 待执行 / ⛔ 未登记」(同类也见于已收口的选型单)⇒ 依「只做被明确要求的事」只报告。
- **下一棒(唯一)** = 第 6 棒 · 执行棒 = **开工 B 单**(平台 API 与用户端 IM WS 通道),`scheduledAt` = 19:4x + 5~8 min;⚠️ B 单 §五-5 含房间上限与发言预算的**标定**(A 单只落待标定初值 2000/10/30 s)。
---
## 20:0x · 插件投放与分库线 —— 试抢执行锁失败(IM 线连续接力占锁)
- **用户问「可以继续处理了吗」** ⇒ 答案:**还不能**。全局执行锁仍在 IM 线手上,且又换手一次:**第6棒 19:53 起**(该线 17:53 → 19:33 → 19:53 连续接力)。
- **试抢结果**:`--claim-exec` ⇒ `✗ 抢锁失败:已有执行会话在跑 —— 占用者:IM线-第6棒`。按 R9 **停手**:⛔ 未删锁、⛔ 未接管。
- **本轮新增可执行件**:`交付物/插件建库权限-一次性执行.sh`(已过 `bash -n`,纯 LF)——
一条命令完成:**抢锁 → 落地 D 档 DDL → 跑 7 条验收 → 自动放锁**,占锁窗口约 **40 秒**。
设计意图 = 把不可用的"长占锁"改造成可插入 IM 线**棒间收口间隙**的短任务。退出码:`0`=全绿 / `2`=DDL 或验收失败 / `3`=抢锁失败(立刻退出,不空转)。
- **锁饥饿的根因**:单一全局执行锁 + 两条线都要长独占 ⇒ 连续接力的一方永远赢。**长任务(S4 代码实现,小时级)无法靠抢间隙完成** ⇒ 需用户定排期。
---
## 20:2x · IM 线第 6 棒(执行棒)收口 ✅ —— **B 单落地**(平台 API + 用户端 IM WS 通道),五单合流第二单
- **本轮动作** = 开工 B 单(`交接单/IM群组-B-平台API与用户端IM-WS通道.md`):5 条只读前置核实 ⇒ §五 步 1–6 ⇒ §六 + §六.1 验收 ⇒ §九 执行回报(**新建独立小节追加**,原文未改)。
- **交付**(`D:\github\dsh_shenxian`,⛔ 未 commit / 未部署):新建 `src/im/hub.ts`(372) + `src/im/ws.ts`(542) + `src/web/routes/im.ts`(333);改 `src/supervisor/proxy.ts`(+30/−0:**upgrade 委托钩子** `setUpgradeDispatch` + 回调首行分流)+ `src/web/server.ts`(本单 +7)。
- **判据读数**:`npm run build` rc=0 | `check-layering` ✅ 无新增违规(rc=0)| `npm test`(Node 22)= **271 / 269 过 / 0 败 / 2 跳过** | **本机端到端验收脚本 15 PASS / 0 FAIL / rc=0** | `npm run smoke:subdomain` = `OK: subdomain routing + auth flow passed`。
- **标定(§五-5)**:A 单初值 `2000 / 10 / 30 s` **建议不回填** —— 投递增量严格线性(10/100/1000/2000),2000 连接 hub 内扇出 P99 = **0.023 ms**;presence 1000 成员房每人每分钟一次变化 ⇒ **5.0 帧/秒**(C=5,低 3333×)/ **1000.0 帧/秒**(C=1000,低 17×);预算三档具名码逐条命中、人类不受限频。
- **关键设计(可复用的坑)**:① **不能挂第二个 `app.server.on('upgrade')`** —— Node 把同一 upgrade 事件**并发投给所有监听器** ⇒ IM 路径会被子域判定判失败并静默 destroy;正解 = 在既有回调首行加**委托钩子**(答 true 立即返回)。② `/api/im/` 下非 WS upgrade **fail-closed 404**(落到隧道会被静默销毁 ⇒ 排查时看不出是路径写错)。③ 实例凭据 `x-dsh-im-instance-token` 两处 **501 fail-closed**(留给 C 单)。
- **实测教训(写进第 7 棒 prompt 了)**:`fetch()` 带自定义 `host` 头**不生效**(假 404)⇒ 多 Host 场景必须 `http.request`|本机脚本的 Node 进程会因伪实例子进程持句柄**不自退** ⇒ 显式 `process.exit()`|假时钟驱动 hub 时**每轮必须重置 `now`**(时钟倒退 ⇒ 1 s 批次闸门整窗失效 ⇒ 读数假 0)。
- **未完成项**:nginx `/api/im/ws` location **未应用**(⛔ 不部署,草案在回报 §九.6)|§六.1-9 的「跨进程重启」半程未实测|`GET /api/im/rooms` 为 `O(房间数)` 查询(需 A 单 `store.ts` 加下推查询)。
- **下一棒(唯一)** = 第 7 棒 · 执行棒 = **开工 C 单**(Agent 接入插件),automation `d2161e2a-bb50-47e2-9a26-50f291fc1d0b`,`scheduledAt` = 20:26。
## 20:0x–20:3x · 锁机制模块化:域锁可行性设计与门禁草案(会话 `锁机制模块化-域锁设计`)
**用户需求**:「后续想多个会话并行开发,全局锁导致无法并行」→ 追问「网络层/数据库层/IM层/插件管理层是否独立模块可单独锁定」→ 定调「分层判定 + 秒级独占,检查遗漏与误判」→ 最终指令「会话执行任务前先判断涉及范围是否都可锁定,确认锁定后开始处理」。
### 交付
- **交接单**:`dsh-server-docs/交接单/锁机制模块化-域锁与任务分级.md`(设计定稿 · 待执行;含 16 条实测前置、6 条执行坑、E1–E7 验收、回滚)
- **门禁草案**:`dsh-server-docs/tmp/锁机制草案-20260922/preflight-lock.sh`(已实测两用例:IM 线判"可锁"、混含 schema/server 判"走秒级独占")
- 台账 `交接单/README.md §一` 已加本单一行。
### 核心结论(★ = 新判据,纠正前两轮误判)
1. ★ **判据从"扇入次数"换成"层号"** —— 仓库早有权威分层:`docs/architecture.md` R1–R4(层①`web/`·`cli.ts`|②`domain/`|③`supervisor/db/fs/worker/net/nginx`|④`config/crypto/isolation/index`)+可机检的 `scripts/check-layering.mjs`。前轮用"引用次数"把 `net/relay`(扇入 24)误判为"半独立";按 R1 它是层③,改动只向下向内 ⇒ **可全程域锁**(⛔ 只要不动 export 签名)。
2. ★ **`src/im` 扇入仅 1**(1877 行 = A 单 963 + B 单 914),文件边界天然不重叠(`store/types/clock/db` ∥ `hub/ws`)⇒ 台账原写「A→B 必须串行」**是全局锁逼出来的,不是代码结构要求** —— 域锁价值的最强实证。
3. ★ **`CODEBUDDY_SESSION_ID` 与 hook payload 的 `session_id` 同源**(实测 env 值 = 转录文件名)⇒ 抢锁可自动绑定会话,脚本能认领自己的锁、能判持有者活性。
4. ★ **实验证实同文件两处并发文本替换无覆盖丢失** ⇒ 共享文件(台账/MEMORY/INDEX)靠 Edit 天然冲突检测即可,不必锁。
5. **layerOf() 未登记 `src/im`**(返回 null ⇒ 分层检查对它**静默不判**);**R2 不可机检**("同层依赖前先问它属哪层");**`src/domain/` 目录不存在**(层②为空 ⇒ 基线 5 条 ④→① 违规无处可去,是"缺领域层接口"的实证)。
6. **本机代码仓包管理 = npm**(`package-lock.json`,无 `.pnpm`)⇒ `CODEBUDDY.md §7`「一律走 pnpm」仅适用图形/实例侧,对代码仓不适用。
7. 构建全局独占:`outDir=lib` 共享 + `npm test` 先全量 build + 27 个测试文件全量跑 ⇒ 两会话同时测试互相覆盖且信号污染。定了先落 B2(只跑自己域的测试)。
### 技术附录(自证 + 坑)
- 坑:`grep -c "a\|b\|c"` 在 BRE 下 `\|` 是**字面竖线**,21 这个数不是挂载点个数 ⇒ 计数类判据一律换精确命令。
- 坑:本机 bash PATH 缺失(`dirname/ls/head` 全 not found,rc=127)⇒ 每条命令前置 `export PATH=<PortableGit 1.2.0>/usr/bin:.../bin:$PATH`(PB §22.6 有全文)。
- 本会话两次抢锁:20:0x 抢失败(IM线-第6棒 持锁)⇒ 按 R9 只读分析;20:22 抢成功(该棒已收工)⇒ 落盘。**收尾已 `--release-exec`**。
---
## 20:2x · IM 线第 7 棒(执行棒)收口 ✅ —— **C 单落地**(agent 接入插件),五单合流第三单
**动作** = 开工 C 单(`交接单/IM群组-C-Agent接入插件.md`):5 条只读前置核实 ⇒ §五 步 1–7 ⇒
§六 + §六.1 验收 ⇒ §九 执行回报(新建独立小节追加,⛔ 原文未改)。
**交付**(`D:\github\dsh_shenxian`,⛔ 未 commit / 未部署 / 未动 47·106):
新建 `src/im/instance-token.ts`(197) + `src/im/agent-bridge.ts`(341) +
`poc/im-agent-bridge/{package.json,cordis.patch.yml,lib/index.js}`(368) + `test/im-agent.test.mjs`(29 用例);
改 `src/web/routes/im.ts`(+78) · `src/im/ws.ts`(+12) · `src/web/server.ts`(+14) ·
`src/supervisor/orchestrator.ts`(+55) · `src/supervisor/remote-spawner.ts`(+30) · `src/worker/agent.ts`(+3)。
**判据读数**:`npm run build` rc=0 | `node --test test/im-agent.test.mjs` = **29/29 过 0 败** |
`npm test`(Node 22)= **300 / 298 过 / 0 败 / 2 跳过**(第 6 棒基线 271 ⇒ +29 全为本单)|
`check-layering` rc=0 ✅ 无新增违规(仍提示 `src/im/*` 未归类,不影响退出码)| 登记门禁 rc=0。
**两处 501 预留点已换真校验**:`imAuth`(REST)与 `doUpgrade`(WS)—— 正确 token ⇒ 通过;
不认 / 过期 / 跨区 ⇒ **401 且不升级**(⛔ 不再 501,"功能没做" vs "你没被认出来"要分得开)。
**关键设计(可复用的坑)**:
1. 🔴 **`src/im/ws.ts` 的 `doUpgrade` 用注入钩子而非直接 import 登记簿** —— 直接在 `src/im/**`
里 import `web/*` 类型会**分层反向**(`check-layering` 就是查这个)。改法 = 加 `resolveInstance`
钩子(与既有 `authenticate` 同形),由 `im.ts` 注入。判据一字不变。
2. 🔴 **登记簿必须"发放侧与校验侧同簿"** ⇒ 由 `web/server.ts` 建**唯一一份**
`app.imInstanceTokens`,**同时**交给 `LocalSpawner` 与 `RemoteSpawner` 的 hooks。
只挂 `LocalSpawner` 的后果 = 一换集群形态就"一条 IM 凭据都不产生"(**缺省失效,长得像功能没做**
—— 与序47 S4 踩过的坑同型)。⇒ 按 R11 补了 `remote-spawner` → `worker/agent` 的投递腿
(Worker 侧没有登记簿,签不了也校验不了,只能原样注入 env)。
3. ⚠️ **插件包 `lib/index.js` 是纯 JS,⛔ 不许带 TS 类型标注** —— 写 `import { connect, type Socket }`
会让 ESM 直接 `SyntaxError: Unexpected identifier 'Socket'`(本机 `node --test` 立刻炸)。
4. ⚠️ **往 md 追加长文本别用 bash heredoc** —— 单引号会炸(本轮踩过:`unexpected EOF while
looking for matching '`)⇒ 改成 Write 一个 `.py` 再跑。
5. 🔴 **两处"明示未接"(⛔ 别当缺陷重做)**:① 本地 LLM 会话对接留成可注入接口
`opts.askLocalSession`(`defaultAsk` 是占位)—— 属 D 单扩展点契约面;② "标识截图"因 E 单未开工
而给的是消息 JSON。⇒ 本棒保证的是**触发 / 上下文 / 预算 / 回写 / 隔离**五条判据可验。
6. ⚠️ **凭据用内存登记簿的代价(已写进 C 单 §九.6-4)**:平台 `dshs` 重启后,**已签发但实例还在跑**
的 token 会全部失效(那批 agent 桥 401,直到实例重启)。按 §四-5 口径可接受。
**五条只读前置**:5/5 已核(插件 server 侧 21 行 vs client 4001 行 · client 靠 session cookie 跨子域 ·
env 注入块在 `orchestrator.ts` 的 `baseEnv` · 插件形态 = `dsh.bundle.patch` 走候选池 ·
worker/实例只拨出)⇒ ⛔ 未触发停手。
**卡点 / 未完成**:`check-layering` 的 `src/im/*` 未归类(改它超改动面 ⇒ 只报告)|
「30 s 内自动恢复」只验了退避参数与 resume 路径,**未做真 socket 端到端**(属部署面)|
`npm run verify` 仍必红(卡在**既有** `verify-platform-admin-section.mjs`,与本线无关)。
**下一棒(唯一)** = **第 8 棒 · 执行棒** = automation `c57c0577-f2ab-496a-91dd-24d90bb405fa`
(一次性 · `scheduledAt = 2026-09-22T20:53`)= **开工 D 单**(`交接单/IM群组-D-插件SDK与扩展点契约.md`)。
---
## 21:1x · IM 线第 8 棒(执行棒)收口 ✅ —— **D 单落地**(插件 SDK 与扩展点契约),五单合流第四单
**本轮只做一件事**:开工 D 单。⛔ 未 commit / 未 push / 未 scp / 未部署 / 未动 47·106 / 未引入新依赖。
### 交付(本机代码面)
- **新建 `src/im/sdk/{types.ts(458), host.ts(695), index.ts(38)}`**(共 1191 行)= 七个扩展点的契约层 + 宿主实现(注册面 / `ImCircuitBreaker` / 声明式建表 / 数据端口 / 审计)。
- **新建三个示例插件** `poc/business-plugins-im/{office-tasks, mud-rate, trpg-turn}/`(`package.json` + `cordis.patch.yml` + `lib/index.js`,共 475 行)—— 按 §四-7 拍板口径**三类场景都给**。
- **新建 `test/im-sdk.test.mjs`(1019 行 · 66 用例)**;`package.json` 的 `test`/`verify` 各登记 1 处。
- **新建说明页** `dsh-server-docs/docs/IM插件SDK与扩展点契约.md`(面向插件作者)。
### 判据读数
| 项 | 读数 |
|---|---|
| `npm run build` | rc=0 |
| `node --test test/im-sdk.test.mjs` | **66 / 66 过 / 0 败 / 0 跳过** |
| `npm test`(Node 22) | **366 / 364 过 / 0 败 / 2 跳过**(第 7 棒基线 300 ⇒ +66 全为本单) |
| 内核零改动 | `git diff` 对 `src/im/store.ts`/`hub.ts`/`web/routes/im.ts` **空**;`git status --short src/im` **无 `M` 标记** |
| `check-layering` | rc=0 · **✅ 无新增违规**(仍提示 `src/im/*` 未归类,不影响退出码) |
| `npm run verify` | ⚠️ 必红且与本线无关(既有 `verify-platform-admin-section.mjs` 的 `ERR_AMBIGUOUS_MODULE_SYNTAX`) |
### 三条硬判据
1. **七点齐备**:`EXTENSION_KEYS` 恰为 142 §3.6 的七个(顺序一致);三插件合起来全覆盖,缺一即 fail。
2. **三类场景各跑通一个**:office(聊天档)/ mud(实时档 + 限速)/ trpg(实时档 + 回合制),各落一行到自己的表,**内核一行未改** ⇒ 142 §五-6 通过。
3. **失败模式齐备**(§六.1-8):每个扩展点条目都带 `timeoutMs` + `circuit`,缺一注册不通过。
### 本轮新踩的坑(已固化,⛔ 别重踩)
1. 🔴 **「内核零改动」不能用 `git status --short` 判** —— `src/im/**` 在 A–D 四单落地后**仍未 commit** ⇒ 整块显示 `??`(未跟踪,不是改动)⇒ 首版判据假红。**正确判据 = 内容级**:每个模块单独 `git diff` 为空 + `status --short src/im` 里无 `M` 标记。
2. 🔴 **文档库的 `Edit`/`Write` 要求先持锁** —— 先释放锁再追加入口/文档会被钩子拦(报「无锁 ⇒ 不允许直接修改」)。⇒ 追加类操作**分批抢锁**:抢锁 → 写文档 → 放锁 → 过门禁 → 再抢锁写入口。
3. 🔴 **写库测试的桩必须做「表全名归一」**(`p_<pluginId>_<name>`)—— 否则「裸名 vs 全名」被当成两张表 ⇒ 房间 ACL / 跨前缀判据**假绿**。
4. ⚠️ **熔断降级方向 = 放行**(不是拒绝):「插件故障不拖垮房间」若判成"拒绝发言",插件一挂整个房间就哑了 —— 与 §四-2 约束 2 正相反。已写成用例钉住。
### 口径更正(本棒新增,⛔ 未改 A–E 任一单的既有结论)
D 单 §九 表格里的 **`im.data`** 是 09-20 规划态命名 —— 执行落成的是 **`ImDataPort` 注入式端口**(真库端口要接 `src/db/**` 迁移器与 `plugin_data_audit` 表,属**建库面** = 「插件投放与分库线」,本棒不越界)。⇒ 已在 D 单 §九 就地加**只标注的落点说明块**(⛔ 正文未改)。
### 🔴 停手等拍板(本轮**未登记**任何接续棒)
**E 单开工前必须定「UI 落点」**,三案**各有优有劣、无客观优劣**(属 §1 边界外第 ⑤ 类)⇒ ⛔ 不替用户拍:
- **候选 A · 集成进门户**(`web/portal.html` 新面板):优点 = 与账号/成员/管理面同域同一次登录,成员与权限天然对齐平台用户;缺点 = 门户与「实例内聊天」是两套线程,房间消息要在平台侧渲染,页面变重。
- **候选 B · 独立页**(`web/im.html`,可挂子域):优点 = 与门户解耦、可按全屏消息流设计,改动面小、回滚干净;缺点 = 多一个入口要维护(登录态/导航/i18n 各一套),用户要记住「聊天在这里」。
- **候选 C · 实例内工作台面板**(走 dsh 既有 slots):优点 = **与 agent 同一屏**(代答与上下文都在自己实例里),复用既有面板与插件分发面;缺点 = 房间是跨用户平台级资源 ⇒ 成员表/消息仍走平台 API,同一房间在**每个用户实例里各渲染一份**(UI 状态不共享),交付面落进实例内插件链。
**E 单原倾向**(可被推翻):**候选 A**。⇒ 拍板到手后再登记 E 单接续棒。
⚠️ 另有一项仍悬:E-5 实测「分时扇出」无效 ⇒ 连接层三候选(A 就地优化(多进程并行)/ B 引入 Centrifugo 类 / C 混合)—— 拍板前 ⛔ 不引入任何外部组件。
### 五单合流进度
A ✅(房间内核 + v14 三表)→ B ✅(平台 API + 用户端 WS)→ C ✅(agent 接入插件)→ **D ✅(插件 SDK 与扩展点契约)** → **E ⏸ 待拍板(UI 落点)**。
---
## 锁机制模块化 — 域锁落地(会话 `锁机制模块化-域锁落地`|22:0x–23:0x)
**起点**:用户「检查下全局锁的规则,看是否能优化为模块锁,后续想多个会话并行开发,全局锁导致无法并行」→ 四轮追问 → 拍板「会话执行任务前先判断,当前任务涉及范围是否都可锁定,确认锁定后开始处理」→ 选 **A**(直接落地脚本改造)。
### 落地清单(全部完成)
| 项 | 文件 | 状态 |
|---|---|---|
| 域锁三件套 + `.gate` 临界区 + 骨架/发布锁 + `--locks` | `scripts/handoff-guard.sh` | 完成 |
| 无锁拒写钩子改域判定(三段分支) | `scripts/lock-guard-hook.py` | 完成 |
| 三态锁展示(空闲/域锁/旧全局锁) | `state.py` | 完成 |
| `src/im` 定层(未归类 12→1,层③ 63→74) | `scripts/check-layering.mjs` | 完成 |
| 开工门禁转正 | `scripts/preflight-lock.sh` | 新建 |
| 规则层四处同步 | `CODEBUDDY.md §6`/技能 `dsh-auto-handoff-chain` 铁律②/`docs/会话与接续/会话接续规范_20260916.md §3.4`/`交接单/README.md` | 完成 |
| 台账状态行 | `交接单/README.md §一` | 改「已落地」 |
### 本轮实测挖出的 4 个真缺陷(都是「假绿」型,静默失效)
1. **逗号分隔多域被压成 1 个** —— `--domains "a,b"` 被 shell 当**一个** token ⇒ `cut -d/ -f1-2` 把 `a,b` 当路径切。修:`norm_domain` 先 `//,/ /` 再逐段处理。
2. **两侧域键算法不一致** —— shell 取「前两段」、Python 取「首个锚点段」⇒ 同一文件算出不同键。修:**统一为「首个锚点段/下一段」**,且锚点表顺序**先长后短**(`dsh-server-docs` 在 `scripts` 前),两侧**逐字一致**(`_ANCHOR_SEGS` ⇄ `_DOMAIN_SEGS`)。
3. **`.gate` 临界区被 SIGTERM/SIGPIPE 打断 ⇒ 泄漏** —— 管道 `| head` 提前关流 ⇒ trap 没跑 ⇒ 后续所有抢锁卡满 10 s。修:加 `trap _gate_guard EXIT INT TERM HUP`。
4. **`ids` 并集导致假绿(最严重)** —— `ids = {payload_session_id, env_CODEBUDDY_SESSION_ID}` 取**并集** ⇒ 伪造/过期 payload id 叠加进程 env 真实 id ⇒ 假会话被判成「我」⇒ **放行**。修:**payload 有 session_id 就只用它**,env 仅缺省兜底。
+ **同会话二次抢锁 `rm -rf` 重建把前一把域声明冲掉** ⇒ 别人能抢进来。修:改为**合并**(追加去重到 `DOMAINS`)。
### 验证(全部通过)
- 域隔离四场景:B `src/im` 成功 ∥ C `src/net` 成功 ∥ D 抢 `src/im` **拒 rc=1** ∥ E `src/db` 成功
- 同会话合并:两把域锁并成 `src/im`+`src/net`;异会话抢已占域 **拒 rc=1**
- hook 四场景:我改我域 放行 0 字节|无锁会话 拒(分支③)|别线改我域 **拒 + 精确冲突域(分支②)**|别线改自己域 放行
- `state.py` / `preflight-lock.sh`(【A】/【B】/【E】三类判定)全通
### 关键判据(写给后续会话)
- **域键算法两侧必须逐字一致** —— 改一侧必须同步改另一侧,否则域锁**静默失效**。
- **`--domains` 用逗号一次给**:`--claim-exec "<会话名>" --domains "src/im,src/net"`。
- **无 `--domains` ⇒ 退化全局独占**(旧 `.exec-lock` 兼容分支保留 —— 在跑会话用旧 hook 快照)。
- **机制层(`config`·`crypto`·`isolation`·`index`·`scripts`·`CODEBUDDY.md`·锁与钩子本身)仍须独占**。
- 排期铁律② 粒度从「全库」改「**每条线**」:不同线可各挂一棒(域不重叠真并行),同线仍只许一棒。
- ⚠️ **改 `settings.json` 的 hook 后需完全重启会话**才加载(旧快照仍用旧逻辑)。
### 收尾
测试残留已清;`tmp/锁机制草案-20260922/` 已删(转正到 `scripts/preflight-lock.sh`)。本会话结束前释放域锁。
---
## 锁机制模块化 — 域锁落地(会话 `锁机制模块化-域锁落地`|22:0x–23:0x)
**起点**:用户「检查下全局锁的规则,看是否能优化为模块锁,后续想多个会话并行开发,全局锁导致无法并行」→ 四轮追问 → 拍板「会话执行任务前先判断,当前任务涉及范围是否都可锁定,确认锁定后开始处理」→ 选 **A**(直接落地脚本改造)。
### 落地清单(全部完成)
| 项 | 文件 | 状态 |
|---|---|---|
| 域锁三件套 + `.gate` 临界区 + 骨架/发布锁 + `--locks` | `scripts/handoff-guard.sh` | 完成 |
| 无锁拒写钩子改域判定(三段分支) | `scripts/lock-guard-hook.py` | 完成 |
| 三态锁展示(空闲/域锁/旧全局锁) | `state.py` | 完成 |
| `src/im` 定层(未归类 12→1,层③ 63→74) | `scripts/check-layering.mjs` | 完成 |
| 开工门禁转正 | `scripts/preflight-lock.sh` | 新建 |
| 规则层四处同步 | `CODEBUDDY.md §6`/技能 `dsh-auto-handoff-chain` 铁律②/`docs/会话与接续/会话接续规范_20260916.md §3.4`/`交接单/README.md` | 完成 |
| 台账状态行 | `交接单/README.md §一` | 改「已落地」 |
### 本轮实测挖出的 4 个真缺陷(都是「假绿」型,静默失效)
1. **逗号分隔多域被压成 1 个** —— `--domains "a,b"` 被 shell 当**一个** token ⇒ `cut -d/ -f1-2` 把 `a,b` 当路径切。修:`norm_domain` 先 `//,/ /` 再逐段处理。
2. **两侧域键算法不一致** —— shell 取「前两段」、Python 取「首个锚点段」⇒ 同一文件算出不同键。修:**统一为「首个锚点段/下一段」**,且锚点表顺序**先长后短**(`dsh-server-docs` 在 `scripts` 前),两侧**逐字一致**(`_ANCHOR_SEGS` ⇄ `_DOMAIN_SEGS`)。
3. **`.gate` 临界区被 SIGTERM/SIGPIPE 打断 ⇒ 泄漏** —— 管道 `| head` 提前关流 ⇒ trap 没跑 ⇒ 后续所有抢锁卡满 10 s。修:加 `trap _gate_guard EXIT INT TERM HUP`。
4. **`ids` 并集导致假绿(最严重)** —— `ids = {payload_session_id, env_CODEBUDDY_SESSION_ID}` 取**并集** ⇒ 伪造/过期 payload id 叠加进程 env 真实 id ⇒ 假会话被判成「我」⇒ **放行**。修:**payload 有 session_id 就只用它**,env 仅缺省兜底。
+ **同会话二次抢锁 `rm -rf` 重建把前一把域声明冲掉** ⇒ 别人能抢进来。修:改为**合并**(追加去重到 `DOMAINS`)。
### 验证(全部通过)
- 域隔离四场景:B `src/im` 成功 ∥ C `src/net` 成功 ∥ D 抢 `src/im` **拒 rc=1** ∥ E `src/db` 成功
- 同会话合并:两把域锁并成 `src/im`+`src/net`;异会话抢已占域 **拒 rc=1**
- hook 四场景:我改我域 放行 0 字节|无锁会话 拒(分支③)|别线改我域 **拒 + 精确冲突域(分支②)**|别线改自己域 放行
- `state.py` / `preflight-lock.sh`(【A】/【B】/【E】三类判定)全通
### 关键判据(写给后续会话)
- **域键算法两侧必须逐字一致** —— 改一侧必须同步改另一侧,否则域锁**静默失效**。
- **`--domains` 用逗号一次给**:`--claim-exec "<会话名>" --domains "src/im,src/net"`。
- **无 `--domains` ⇒ 退化全局独占**(旧 `.exec-lock` 兼容分支保留 —— 在跑会话用旧 hook 快照)。
- **机制层(`config`·`crypto`·`isolation`·`index`·`scripts`·`CODEBUDDY.md`·锁与钩子本身)仍须独占**。
- 排期铁律② 粒度从「全库」改「**每条线**」:不同线可各挂一棒(域不重叠真并行),同线仍只许一棒。
- ⚠️ **改 `settings.json` 的 hook 后需完全重启会话**才加载(旧快照仍用旧逻辑)。
### 收尾
测试残留已清;`tmp/锁机制草案-20260922/` 已删(转正到 `scripts/preflight-lock.sh`)。本会话结束前释放域锁。
### 🔴 收尾验收又抓到一个真缺陷(机制层误判为「可锁定」)
**现象**:`preflight-lock.sh "会话" scripts/handoff-guard.sh`(纯机制层)⇒ 文件正确落【E】,但结论仍是「**可以锁定**」+ `rc=0`。
**危害**:门禁对本任务**自己的目标文件**给出假绿 —— 机制层本应「全平台共用 ⇒ 不得并行」,却判成可开工。等于门禁在最需要拦的地方放行。
**根因**:结论块**只 gate `unknown`(【D】)**,`mechanism`(【E】)只打印提示、**不参与退出码**。
**修复**:结论块改为 `rc=1` 当 `【D】>0` **或** `【E】>0`;【E】命中时输出「不得与其他会话并行 ⇒ 仅当确认无其他会话在跑才允许独占开工」。
**回归**:A 纯可锁定 `rc=0` ✅|B 机制层 `rc=1` ✅|C 未归类 `rc=1` ✅(三场景全通)。
**同步点**(2 处,原只写「【D】非空 ⇒ 拒开工」):`CODEBUDDY.md §6` 门禁段 + `交接单/README.md` 第 13 条。
**⚠️ 教训(写进流程)**:**`--release-exec` 必须放在所有文件改完之后** —— 提前释放域锁后,`Edit preflight-lock.sh` 被 hook 以「没有持任何锁」**硬拦**,只能重新抢锁再改。这是 hook 按设计工作(fail-closed),不是故障。⇒ 收尾顺序:**改完 → 回归 → 才放锁**。
### 🔴🔴 收尾验收连环抓出两个「假绿级」缺陷(本平台必现)
**缺陷 1:非 ASCII 会话名全部塌成同一个锁目录(最严重)**
- **现象**:`tr -c 'A-Za-z0-9._-' '_'` 把**每个非 ASCII 字符**压成 `_` ⇒ 「域锁-甲」「域锁-乙」「域锁-丙」全映射成 `______-___` ⇒ **同一锁目录**。
- **危害**:本平台**会话名全是中文** ⇒ 必现。两会话共用一把锁 ⇒ ① 后到者走"合并"分支(以为是自己)② 域声明被并进同一份 `DOMAINS` ③ hook 侧 `_name_owner_of_id` 也难区分 ⇒ **域隔离整体失效**。
- **修复**:ASCII 安全名直接留用(可读+兼容旧目录);含非 ASCII ⇒ 附 `cksum` 摘要 ⇒ `______-___-1601335095` / `-3564051901`。
**缺陷 2:trap 单槽位覆盖 ⇒ `.gate` 泄漏(我自己加固时引入)**
- **现象**:我给临时文件加了一个 `trap`,但**下面原来还有一个 `trap _gate_guard`** ⇒ bash 的 trap 是**单一槽位**,后者**覆盖**前者 ⇒ 必然漏一个。
- **实证**:`--claim-exec` 全部返回 `rc=1`「临界区 .gate 被长时间占用」——`.gate` 孤儿残留。
- **修复**:改成**一个** `_cleanup_guard` 同时清两样(临时文件 + `.gate`),全流程只注册一次。
- **⚠️ 附带修掉一个更危险的隐患**:`.gate` 可能是**别人的**(我们在 while 里等别人的临界区)⇒ 无脑 `rmdir` 会**删掉别人的互斥** ⇒ 加 `_GATE_MINE` 标志,只有"确实由本次调用创建"才允许清理(守 R9 精神)。
**回归**(中文会话名,全绿):
- 门禁:可锁定 `rc=0` ✅|机制层 `rc=1` ✅|未归类 `rc=1` ✅
- 域锁:甲占 `src/im` `rc=0` ✅|乙占 `src/net` 并行 `rc=0` ✅(**关键:中文名不再串目录**)|丙抢已占 `rc=1` ✅|甲追加合并 `rc=0` ✅(域数=2)
- hook 四场景:我改我域 放行 ✅|我改他域 拦截 ✅|他改我域 拦截+精确冲突域 `src/im` ✅|他改他域 放行 ✅
- 泄漏:失败路径/SIGTERM/SIGPIPE/成功路径 **四种全为 0 残留** ✅
**⚠️ hook 验收的判据前提(重要,⛔ 别踩)**:`_is_mine()` 要求**会话名与 session_id 同时吻合**。若测试时**不设** `DSH_SESSION_NAME`,且多把锁写着**同一个** `CODEBUDDY_SESSION_ID`(同进程批量抢锁必然如此)⇒ `my_name` 为空 ⇒ 退到「只看 id」分支 ⇒ **把别人的锁认成自己的** ⇒ 假绿放行。⇒ **验收 hook 必须带 `DSH_SESSION_NAME=<会话名>`**,否则测的是错的东西。
### ✅ 本单最终收口(域锁落地 + 收尾四缺陷全修)
| 项 | 结果 |
|---|---|
| 四脚本语法 | `handoff-guard.sh` / `preflight-lock.sh` / `lock-guard-hook.py` / `state.py` **全过** |
| 分层 ratchet | `check-layering.mjs` **✅ 无新增违规** |
| 开工门禁 | 可锁定 `rc=0` ✅|机制层 `rc=1` ✅|未归类 `rc=1` ✅ |
| 域锁隔离(中文名) | 甲 `src/im` `rc=0` ✅|乙 `src/net` 并行 `rc=0` ✅|丙抢已占 `rc=1` ✅|锁目录 **2 个互不串** ✅ |
| hook 四场景 | 我改我域放行 ✅|我改他域拦 ✅|他改我域拦+报冲突域 ✅|他改他域放行 ✅ |
| 泄漏(四路径) | 成功 / 冲突拒绝 / **SIGTERM×5** / **SIGKILL** → **全 0 残留** ✅ |
| 技能三处 md5 | 本机 = 文档库 = 服务器 **`5a6844ec…`** ✅ |
| 技能新增章节 | `dsh-auto-handoff-chain §3.5` 域锁四个「假绿」坑 + 验收前置条件 + 收尾顺序铁律 |
| 登记 | `README.md` / `INDEX.md` 已跟改并推服务器(md5 三处一致) |
| `MEMORY.md` | **7,796 / 7,800** ✅ 回上限内 |
| 域锁 | **已释放,锁总览空闲** ✅ |
**本单共修 4 个缺陷**(全部是「假绿」性质 —— 该拦的没拦):
① 【E】机制层未参与退出码 ⇒ 门禁对自己的目标文件假绿放行
② 非 ASCII 会话名塌成同一锁目录 ⇒ 中文会话名并行时域隔离失效
③ `trap` 单槽位互相覆盖 ⇒ `.gate` / 临时文件泄漏
④ `.gate` 清理未区分归属 ⇒ 可能删掉**别人的**互斥
## 14:0x · 回复 carbon 线对接文档:两处阻塞判定 + 最新接入方式(⛔ 本轮零代码改动)
**用户原话**:「插件接入和数据接入方案有更新,确认最新接入方式后,回复到对接文档中」
### 做了什么
把平台侧回复**追加**到 `E:\ProgramData\AIProject\dsh-plugin-carbon\对接文档_carbon插件-平台侧两处阻塞_20260922.md`
(原文 §0–§6 共 186 行**完整保留**,新增 `# 【平台侧回复】` §A–§G;27643 B / md5 `814f337e619ab0ee0e949b39b15763b2` / U+FFFD=0)。
### 🔴 本轮关键发现:接续入口已跑到第 9 棒(我上轮摘要里没有的)
读 `接续入口_插件投放与分库线_20260922.md` 才看到 —— **原计划里"要等拍板"的几项全都已落地**:
共享只读包库(S1/S2 上线)· 建库口接线(B 档 `ALTER ROLE dshs CREATEDB`)· 跨机装配(`plugin-assembly.ts` + worker `/plugins/apply`)· S5-a 兼容矩阵 · 迁移前 `pg_dump --schema-only` 备份 · S6 三段式 UI · 部署 47/106 + 真机 E2E 全绿。
**⇒ 教训:接手前必须读入口最新行,⛔ 别拿摘要里的旧状态当既成事实。**
### 判定(写进了回复)
- **事项一(用户自助开通被权限墙挡住)⇒ 在本平台不成立**。真缺陷 = **开源导出物 `dsh_ai1net/install.sh:275`** 的 `chmod 700`;本平台 `bootstrap-worker.sh:139-148` 幂等把 `users/` 设 **711** ⇒ 归**开源导出线**,本线不动。
- **事项二(handoff 停写)⇒ 属实,但注释前提②「handoff.json 无人消费」写错**(消费者 = `orchestrator.ts:65` 的 `WATCHDOG_TASK`;平台自己在 `restartProbe` 也用,只是写空命令)。
- 🔴 **两条都不必再等**:平台已改成「**admin 侧 root 物化依赖树 → 共享只读包库;用户侧只写依赖引用**」(`materializeShared()` + `plugin-assembly.ts:407` `file:${target}`)⇒ 事项一的失败路径(降权 uid 穿越 users/ 时 EACCES)**已不可达**;装配走 worker `/plugins/apply`、**刻意绕开 handoff** ⇒ 事项二不再是阻塞。**carbon 原文提的「乙案」正是平台已采纳的方向。**
### 路径口径(回应 carbon §5 的 ⚠️)
**两套命名 = 两个不同部署物,不是"生产 vs testlocal"**:源仓/本平台 = `dshs` / `DSHS_*` / `/opt/dshs` / `/etc/dshs.env`;开源导出物 = `dsh_ai1net` / `DSH_AI1NET_*` / `/opt/dsh_ai1net` / `/etc/dsh_ai1net.env`。**权威 = 前者**。
### 本轮另核实的(供后续引用)
- 装配协议**仍是 `file:`** —— 用户 09-23 已拍板改用 `link:`,但**尚未落地**(落地件列在入口 §5「已拍板待落地」4 处)。回复里已按"即将变更"告知 carbon。
- 可见面开关 `DSHS_SHARED_CATALOG_PUBLIC` **默认 false**(`config.ts:687`),`/api/plugins/shared/catalog` 关着返 **404**。
- 数据面权威 = `DB-03-插件数据面规范.md`(含 §三·补 谁建库 / §四·补 兼容矩阵 / §八·补 端侧边界)。