回收 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)、记忆修复前备份。
133 KiB
工作区日志 · 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.parseOK
② 词表: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 才生效
修掉的真缺陷(自己引入 + 顺带发现)
- 🔴
NameError: self_refresh is not defined(我自己引入):改sync-scoped-guards.py加native_multi快路径时,_HEADER(含self_refresh()定义)只拼在 else 分支 ⇒ 新分支生成的副本调用不存在的函数 ⇒ 副本整个崩掉(RC=1)。- 发现方式:副本独立真跑 3 个表内用例全 FAIL ⇒ 不猜、直接看 stderr 原文
- 修复:
head提到分支外、两个分支都拼
- 变换失配时误报"副本均为最新" + RC=0:源脚本改形态后
_RE_DEF必然失配 ⇒ 旧逻辑同时打印❌ 变换失配和副本均为最新(自相矛盾),且退出码 0 让调用方以为成功。已修为「有错误时不报最新」。 - 同步成功后仍提示"待同步":
stale是"源变过"的过程标记,未区分"已重建"。已修为重建成功即不再计入 pending。 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-rulesv1.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.jsonUserPromptSubmit = 2 条(仅源脚本);PreToolUse3 组 /SessionStart1 组未受影响- 全局执行锁已释放
- 桌面
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 处描述词盲区,均为净改进)
- 🔴
dsh-instance-diagnose缺「实例起不来」 —— 用户最自然的说法命不中,会误落到dsh-desktop-dev-shell(后者声明了"dsh 实例起不来")。 ⇒ 补触发词(实例起不来 / 挂了·崩了·一直重启 / 报 503·502)+ 与 desktop-dev-shell 双向指认边界(服务器用户实例 vs 本机源码实例)。v1.1.0 → v1.2.0 - 🔴
dsh-desktop-dev-shell—— 给「dsh 实例起不来」加「本机」限定,并写明作用域。v2.0.0 → v2.0.1 - 🔴
dsh-plugin-diagnose缺「装了插件但界面没变化」 —— 同因(只有对方声明了同一说法)。⇒ 补词 + 双向指认。v1.2.0 → v1.3.0 - 🔴
agent-operating-rules补 8 类高频点名场景触发词(这是最重要的一条): 实测其 description 没有「只做/明确要求·抢锁·法规·解决问题·妥协·D盘·直入主题·代词·成本·token·积分」任何一个词, 而这些恰是用户反复强调、且已写进本技能正文的规则 ⇒ 规则写在文件里 ≠ 在正确时机被取用(与skill-load-guard.py同族问题)。 v1.3.0 → v1.4.0 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)
dsh-change-workflow的 version =2.9.2-playwright-banned—— 非标准 semver(版本号里塞了策略标记)。已污染下游:README 登记行现也带此值。倾向改成2.9.3并把该信息留在last_change;但它可能是别的会话有意设的标记 ⇒ 未擅自改。last_change字段 12 个里缺 5 个(architecture-lifecycle / auto-handoff-chain / decision-method / env-bootstrap / knowledge-upkeep)—— 字段规范不一致。⛔ 未批量补("只做被明确要求的事")。- 体量不均:
dsh-opensource-release181KB +dsh-change-workflow116KB = 全量 46%;且 change-workflow 的last_change堆成 3000+ 字符的巨型变更日志("此前…此前…"反复)⇒ 维护成本集中在这两个身上。 - 独立型盲区 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.mdmd5e5d20a959ed83f2d…/ 1052 行 / 21 个一级章节dsh-opensource-release/SKILL.mdmd586f3dcc0c1233a07…/ 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 内不含任务细节(细节唯一来源 = 接续包)
- ⚠️ 登记后本会话不再改接续包 / 不再改关键判断(若改了必须回来撤销或重登记)
本轮全程(三条线,一次会话内完成):
- 技能加载闸门扩作用域(B)+ 扩词表(B)+ 清退桌面副本(4 条 hook → 2 条)
- 技能可用性 10 层取证 → 修 3 个真缺陷(触发词盲区)+ 7 条规则盲区 → 覆盖率 95%→100%、真实撞点 2→0
- 跨工作区隔离性(无真冲突)+ 精简评估(≈1%,且不该动)+ 12 技能 version 统一 1.0.0 + README 补登 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-workflow1052 → 319 行 + 9 档详情(867 行)⇒ 达标 ≤350 ·dsh-opensource-release1089 → 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.md2 处、INDEX.md4 处(精确串替换器,命中各 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.mdL19/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 §待定 既有的「⛔ 不提前分库」,理由按硬度排:
- 双后端同构(最硬):sqlite 没有 database/schema 命名空间对等物(
ATTACH是连接级、不持久)⇒ 分库 / 分 schema 破坏「任何结构在 sqlite 与 pg 上都建得起来」的既有承诺;表前缀在两边都只是名字,天然同构。 - 收益为零:插件不持有连接、不能写 SQL、数据面全走内核 API ⇒ 分库能给的权限隔离对插件用不上;平台侧隔离诉求已由前缀校验 + 房间 ACL + 三重配额 +
plugin_data_audit覆盖。 - 成本随插件数线性涨:PG 连接池是 per-database 的 ⇒ N 插件 = N 套 pool;47 是小机(实例硬顶 1024 MiB),backends 内存实打实。单库 = 1 个 pool。
- 把不可逆操作常态化:插件可启停/卸载 ⇒ 分库要动态
CREATE/DROP DATABASE;现行卸载 = 停用 + 数据保留 30 天可恢复。 - 堵死跨域事务:既有「跨前缀要原子 ⇒ 走内核事务口」在分库后无法实现。
🔑 关键认知:「独立库」这条路已经存在 —— 插件的「只属于某用户自己的数据」落实例 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/(frontmattername: 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 三线的过期
ACTIVE7 条 +PAUSED4 条)scheduledAt都早于 12 小时窗口 ⇒ 物理上不会再跑;是否一并删除待拍板。 - 协议加固(已写进第 8 棒 prompt):每棒必须
mode=update复用自己那条 automation 记录,⛔ 禁止mode=create新建 —— 残留的「已跑过仍 ACTIVE」旧记录正是补跑风险源;收尾再mode=list清本线过期残留。 - 当前在挂的唯一接续棒 =
dd0d1db8(第 8 棒,scheduledAt10: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(:65WATCHDOG_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 分库定稿)
- 库归属未点名 ⇒ 钉死 = 控制面库
dshs(DB-03 §〇明列rooms/room_members/messages/files为内核表、不适用插件数据面规范)。 - 联邦形态缺失 ⇒ 房主 = 建房那个区的 Manager;跨区本期不做(依赖 149 §七 F6 跨区可见性,待拍板)+ G3 用户无区字段;但三表须留可扩字段(148 §G4:
overlay_devices主键已含network)。 - 142 §3.5「relay / DIAL / presence 可直接复用」—— 整体错 ⇒ relay 与 DIAL 不在 IM 依赖链上(IM 走平台侧 HTTPS/WS);presence 只复用机制口径(1 s 批合并 / 只订阅可见成员),通道须平台 IM hub 自建(网络层跨用户隔离是结构性的
dialers默认拒绝,接不出跨用户在线态)。 - A 单 §九-1 / D 单 §九-1 的插件数据落点已作废 ⇒ 改 一插件一库
dshs_pl_<pluginId>+ 归属列强制;删掉「用户自己的 ⇒ 实例 home」(实例本地只留缓存/临时,权威必须在库)。 - C 单卡在一条今天不存在的通路 ⇒ 插件要装进用户所在节点的实例,而跨机投放腿
writeHandoff因DSHS_ENABLE_PATCH默认 false 空转(147 G 系列)⇒ 已登记依赖 = 「插件投放与分库线」P3,或把验收限定在 47 本机实例。 - 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.md04-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」误判成「锁被占」(本次实测是mkdirnot 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-store8K 是旧位置残留)⇒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」的实证 + 版本漂移实证。
方案主干(写进单的判定)
- 🔑 技能共享层已把 D1+D2+「同名替换(=P2 更新语义)」全落过一次(
skills.ts:429-499上传/替换+:539-548用户面合并三类)⇒ 本线不是新设计,是复用该形态,只多「依赖树」与「数据面」两件事。方案可行性由此大幅上调。 - 卡点不在部署,在"谁在哪台机上动手":装插件那步(
runPnpmAsbusiness-plugins.ts:454-461)是 Manager 本机setpriv pnpm⇒ 跨机必ENOENT;但重启探活那条腿已经通了(supervisor.restartAndProbe→remote-spawner.ts:388按hostIdFor→ workeragent.ts:459)⇒ 只需补POST /plugins/apply(模型 = 既有/restart-probe/:userId),⛔ 不新开入站口。 - D1 其实已是现状:
GET /api/plugins/mine(:578,requireAuth)走listBusinessPlugins(),实测 SQL =SELECT … FROM business_plugins ORDER BY name ASC(pg.ts:420/repo.ts:677)无任何过滤 ⇒ 任何登录用户今天就能看到候选池全量。⛔ 别"顺手加过滤"。 - 三个"今天不存在"要新建:
/var/lib/dshs/bundled-plugins(47/106 都没有)·dshs_pl_<pluginId>(p_*零命中)· D4 的「用户插件文件夹」。 - 前置缺口 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.tsenv 注入 +--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):mountinforo,nosuid,nodev+ 实例内ls可见 +touch=Read-only file system+grep -c bundled-plugins= 2。 - 零回归:
npm test248/247 pass/0 fail/1 skip(与开工基线同)。
🔴 必须记住的四个坑(详见 PLAYBOOK-实例与插件坑.md §22,⛔ 别重踩)
- 部署真源 =
/opt/dshs/lib/(lib/被 gitignore);/opt/dshs的 git HEAD 是噪声。 - 两节点
lib/会静默分叉 —— 实测 106 缺 14 个.js、33 个文件内容不同 ⇒ 新config.jsimport 缺失文件 ⇒dshs-worker崩溃循环。已整包同步修好(106 现与 47 逐字节一致);⛔ 部署后必须全量对账(is-active看一次不算)。 - 沙箱内验收
nsenter挑错 PID = 读的是宿主(假阳性) —— bwrap 的父监控进程在外面;必须用ps -eo pid,comm,args | awk '$2=="node" && /dsh --profile/'那个 pid,且先确认/proc/<pid>/mountinfo里有bundled-skills。 - 共享层物化四个必修:
--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-serverssh 配置与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-suiteversion=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 可跑,但降级隔离且改定稿)/DSECURITY 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_est10000→0)② 客户端按连接计时发 ping ⇒ 连续 66 snum_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.tsv14 +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 默认
publicschema 上 PUBLIC 有 CREATE(ACL{postgres=UC/postgres,=UC/postgres})⇒ 函数不得放public,改放postgres拥有的私有 schemadshs_int(PUBLIC 已 REVOKE、只给dshsUSAGE);函数体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 buildrc=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 verifyrc=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 buildrc=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/wslocation 未应用(⛔ 不部署,草案在回报 §九.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 §一已加本单一行。
核心结论(★ = 新判据,纠正前两轮误判)
- ★ 判据从"扇入次数"换成"层号" —— 仓库早有权威分层:
docs/architecture.mdR1–R4(层①web/·cli.ts|②domain/|③supervisor/db/fs/worker/net/nginx|④config/crypto/isolation/index)+可机检的scripts/check-layering.mjs。前轮用"引用次数"把net/relay(扇入 24)误判为"半独立";按 R1 它是层③,改动只向下向内 ⇒ 可全程域锁(⛔ 只要不动 export 签名)。 - ★
src/im扇入仅 1(1877 行 = A 单 963 + B 单 914),文件边界天然不重叠(store/types/clock/db∥hub/ws)⇒ 台账原写「A→B 必须串行」是全局锁逼出来的,不是代码结构要求 —— 域锁价值的最强实证。 - ★
CODEBUDDY_SESSION_ID与 hook payload 的session_id同源(实测 env 值 = 转录文件名)⇒ 抢锁可自动绑定会话,脚本能认领自己的锁、能判持有者活性。 - ★ 实验证实同文件两处并发文本替换无覆盖丢失 ⇒ 共享文件(台账/MEMORY/INDEX)靠 Edit 天然冲突检测即可,不必锁。
- layerOf() 未登记
src/im(返回 null ⇒ 分层检查对它静默不判);R2 不可机检("同层依赖前先问它属哪层");src/domain/目录不存在(层②为空 ⇒ 基线 5 条 ④→① 违规无处可去,是"缺领域层接口"的实证)。 - 本机代码仓包管理 = npm(
package-lock.json,无.pnpm)⇒CODEBUDDY.md §7「一律走 pnpm」仅适用图形/实例侧,对代码仓不适用。 - 构建全局独占:
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 "你没被认出来"要分得开)。
关键设计(可复用的坑):
- 🔴
src/im/ws.ts的doUpgrade用注入钩子而非直接 import 登记簿 —— 直接在src/im/**里 importweb/*类型会分层反向(check-layering就是查这个)。改法 = 加resolveInstance钩子(与既有authenticate同形),由im.ts注入。判据一字不变。 - 🔴 登记簿必须"发放侧与校验侧同簿" ⇒ 由
web/server.ts建唯一一份app.imInstanceTokens,同时交给LocalSpawner与RemoteSpawner的 hooks。 只挂LocalSpawner的后果 = 一换集群形态就"一条 IM 凭据都不产生"(缺省失效,长得像功能没做 —— 与序47 S4 踩过的坑同型)。⇒ 按 R11 补了remote-spawner→worker/agent的投递腿 (Worker 侧没有登记簿,签不了也校验不了,只能原样注入 env)。 - ⚠️ 插件包
lib/index.js是纯 JS,⛔ 不许带 TS 类型标注 —— 写import { connect, type Socket }会让 ESM 直接SyntaxError: Unexpected identifier 'Socket'(本机node --test立刻炸)。 - ⚠️ 往 md 追加长文本别用 bash heredoc —— 单引号会炸(本轮踩过:
unexpected EOF while looking for matching ')⇒ 改成 Write 一个.py再跑。 - 🔴 两处"明示未接"(⛔ 别当缺陷重做):① 本地 LLM 会话对接留成可注入接口
opts.askLocalSession(defaultAsk是占位)—— 属 D 单扩展点契约面;② "标识截图"因 E 单未开工 而给的是消息 JSON。⇒ 本棒保证的是触发 / 上下文 / 预算 / 回写 / 隔离五条判据可验。 - ⚠️ 凭据用内存登记簿的代价(已写进 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) |
三条硬判据
- 七点齐备:
EXTENSION_KEYS恰为 142 §3.6 的七个(顺序一致);三插件合起来全覆盖,缺一即 fail。 - 三类场景各跑通一个:office(聊天档)/ mud(实时档 + 限速)/ trpg(实时档 + 回合制),各落一行到自己的表,内核一行未改 ⇒ 142 §五-6 通过。
- 失败模式齐备(§六.1-8):每个扩展点条目都带
timeoutMs+circuit,缺一注册不通过。
本轮新踩的坑(已固化,⛔ 别重踩)
- 🔴 「内核零改动」不能用
git status --short判 ——src/im/**在 A–D 四单落地后仍未 commit ⇒ 整块显示??(未跟踪,不是改动)⇒ 首版判据假红。正确判据 = 内容级:每个模块单独git diff为空 +status --short src/im里无M标记。 - 🔴 文档库的
Edit/Write要求先持锁 —— 先释放锁再追加入口/文档会被钩子拦(报「无锁 ⇒ 不允许直接修改」)。⇒ 追加类操作分批抢锁:抢锁 → 写文档 → 放锁 → 过门禁 → 再抢锁写入口。 - 🔴 写库测试的桩必须做「表全名归一」(
p_<pluginId>_<name>)—— 否则「裸名 vs 全名」被当成两张表 ⇒ 房间 ACL / 跨前缀判据假绿。 - ⚠️ 熔断降级方向 = 放行(不是拒绝):「插件故障不拖垮房间」若判成"拒绝发言",插件一挂整个房间就哑了 —— 与 §四-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 个 ——
--domains "a,b"被 shell 当一个 token ⇒cut -d/ -f1-2把a,b当路径切。修:norm_domain先//,/ /再逐段处理。 - 两侧域键算法不一致 —— shell 取「前两段」、Python 取「首个锚点段」⇒ 同一文件算出不同键。修:统一为「首个锚点段/下一段」,且锚点表顺序先长后短(
dsh-server-docs在scripts前),两侧逐字一致(_ANCHOR_SEGS⇄_DOMAIN_SEGS)。 .gate临界区被 SIGTERM/SIGPIPE 打断 ⇒ 泄漏 —— 管道| head提前关流 ⇒ trap 没跑 ⇒ 后续所有抢锁卡满 10 s。修:加trap _gate_guard EXIT INT TERM HUP。ids并集导致假绿(最严重) ——ids = {payload_session_id, env_CODEBUDDY_SESSION_ID}取并集 ⇒ 伪造/过期 payload id 叠加进程 env 真实 id ⇒ 假会话被判成「我」⇒ 放行。修:payload 有 session_id 就只用它,env 仅缺省兜底。 + 同会话二次抢锁rm -rf重建把前一把域声明冲掉 ⇒ 别人能抢进来。修:改为合并(追加去重到DOMAINS)。
验证(全部通过)
- 域隔离四场景:B
src/im成功 ∥ Csrc/net成功 ∥ D 抢src/im拒 rc=1 ∥ Esrc/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 个 ——
--domains "a,b"被 shell 当一个 token ⇒cut -d/ -f1-2把a,b当路径切。修:norm_domain先//,/ /再逐段处理。 - 两侧域键算法不一致 —— shell 取「前两段」、Python 取「首个锚点段」⇒ 同一文件算出不同键。修:统一为「首个锚点段/下一段」,且锚点表顺序先长后短(
dsh-server-docs在scripts前),两侧逐字一致(_ANCHOR_SEGS⇄_DOMAIN_SEGS)。 .gate临界区被 SIGTERM/SIGPIPE 打断 ⇒ 泄漏 —— 管道| head提前关流 ⇒ trap 没跑 ⇒ 后续所有抢锁卡满 10 s。修:加trap _gate_guard EXIT INT TERM HUP。ids并集导致假绿(最严重) ——ids = {payload_session_id, env_CODEBUDDY_SESSION_ID}取并集 ⇒ 伪造/过期 payload id 叠加进程 env 真实 id ⇒ 假会话被判成「我」⇒ 放行。修:payload 有 session_id 就只用它,env 仅缺省兜底。 + 同会话二次抢锁rm -rf重建把前一把域声明冲掉 ⇒ 别人能抢进来。修:改为合并(追加去重到DOMAINS)。
验证(全部通过)
- 域隔离四场景:B
src/im成功 ∥ Csrc/net成功 ∥ D 抢src/im拒 rc=1 ∥ Esrc/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/imrc=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:407file:${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(含 §三·补 谁建库 / §四·补 兼容矩阵 / §八·补 端侧边界)。