Files
dsh_ai1net_server/.workbuddy/memory/2026-09-22.md
T
admin ce8e6ceed9 chore(工作区): 纳入版本控制基线(回收 411 MB 过程产物)
回收 411 MB(470 M → 58.8 M),全部经回收站,可恢复:
- 待清理/(146.2 M,含 relay 分片 128 M 与 42 项过程目录)
- tmp/(32.4 M,按接续棒命名的过程临时区)
- .workbuddy/tmp/(39.5 M)
- 4 份 workbuddy.db 冗余副本(101 M,09-23 事故的坏副本 / 抢救产物)
- tmp/im16/gw/centrifugo 二进制(63.9 M,可重下)+ 缓存残留

入库范围:常驻规则(CODEBUDDY.md / README.md / state.py)、在途接续入口与
接续包、docs/、交付物/、交接单/、归档/、scripts/、.codebuddy/、
.workbuddy/memory/;共 398 件,其中 >60 KB 的 26 件全为文档。

排除(.gitignore):tmp/、待清理/、运行态日志与缓存、*.db 与 DB 备份整目录、
打包二进制(*.tar.gz / *.tgz)、记忆修复前备份。
2026-09-24 07:51:03 +08:00

133 KiB
Raw Blame History

工作区日志 · 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/AI技能/dsh-ai1net-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/AI技能/{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\AI技能\aliyun-dsh-server\.workbuddy\stop-dialog-guard.log(2496 B,最后写入 09-22 06:17 = 当天)。

  • 文件名 = hook 自己的名字;
  • E:\e\ProgramData\AI技能\ 下只有 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/AI技能/aliyun-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/AI技能/aliyun-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/AI技能/aliyun-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//') 且命中数必须 = 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//AI技能//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/AI技能/_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/AI技能/aliyun-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/AI技能/aliyun-dsh-server/归档/humanize-原文件备份-20260922\dsh-opensource-release\03-实测坑.md.pre-坑20去重;⛔ 未 commit / push。

08:37 技能重组线 · 第 5 棒(dsh-opensource-release/references/01-保留移除与脱敏口径.md)

  • 备份:E:/ProgramData/AI技能/aliyun-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/AI技能/aliyun-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/AI技能/aliyun-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/AI技能/aliyun-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/AI技能/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/AI技能/aliyun-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/AI技能/aliyun-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\AI技能\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(含 §三·补 谁建库 / §四·补 兼容矩阵 / §八·补 端侧边界)。