多会话协同 · 顶层设计(宿主能力模型 → 最小形态)
⛔ 已退役(2026-09-30)
本件(顶层设计)的内容已并入 → E:/ProgramData/.workbuddy/skills/multi-session-collab/references/architecture.md(协作机制的唯一权威文档)
⇒ ⛔ 别再引用本件做判断(曾因它把旧结论当现状);本件只作历史沿革留档,⛔ 已不再维护。
依据:用户 2026-09-29 定案「只有一份架构文档」「一切迭代都在 skill 内」「⛔ 不在工作区或别处另开平行文档」。
2026-09-29 · 本件是对前几份(定稿/实施方案/SOP)的收口与纠偏:前几份在"加组件",本件先看清地基再做减法。
🔴 自评(用户指出,属实):此前一直在局部打补丁(advance-watch → 心跳 → 去抖 → 看板 → 角色),
没有先在更大的范围里整体思考。本件即为补齐这一步。
1 先把地基看准:宿主能力模型(这是我之前没做的)
| 宿主提供 |
载体 |
我们能做的 |
⛔ 做不到 / 不许做 |
| 状态 |
SQLite:automations(计划)/automation_runs(运行与结论)/sessions |
只读(mode=ro) |
自建队列、自建库、写别人的表 |
| 调度 |
自动化排期(一次性/周期)→ 到点由宿主拉起会话 |
写一行 automations = 排一个活 |
自建调度器、自建常驻 |
| 事件 |
hooks(SessionStart/SessionEnd/UserPromptSubmit…),宿主起子进程 |
事件时跑本地只读脚本 |
🔴 钩子开不了新会话;⛔ 钩子不联网、不读令牌、不起子进程 |
| 执行体 |
会话 = 一次性 / 可替换 / 跑完即退 |
在里面"读—判—写" |
把状态放进会话(会话死了状态就没了) |
| 互斥 |
域锁 / 全局锁(handoff-guard) |
拿它当单例 / 串行 |
🔴 自造第二把锁 / 第二套去抖 |
2 由地基直接推出的三条约束(不是发明,是推论)
- 无常驻 ⇒ 一切"等待"只能靠宿主排期(自动化)或宿主事件(钩子)。
- 执行体无状态 ⇒ 状态必须落 DB / 文件;会话只做"读—判—写"。
- 互斥用既有锁 ⇒ ⛔ 不要
mkdir / flag 自造去抖 —— 域锁就是单例。
3 我原来错在哪(打转清单,逐条给根因)
| # |
我加的件 |
它为什么存在 |
根因 |
现在 |
| 1 |
"协同监管棒"这个角色 |
我想"得有人盯着" |
🔴 凭空造角色 ⇒ 于是必须接着解决"谁叫它/多个并行怎么办/它死了怎么办"——这一串问题全是为了维护我自己造的角色 |
退役:拆成"收尾自判(棒内)"+"心跳巡检(全局)"两个动作 |
| 2 |
mkdir .dispatch-claim 去抖 |
防 N 个监管并行 |
🔴 重复造轮子 —— 项目本来就有域锁,它干的就是去抖/单例 |
删,改用域锁 |
| 3 |
4 档"每 20 分钟"自动化 |
想让监管更密 |
用"加密轮询"解决延迟 ⇒ 越加越贵 |
已删(rrule 也支持不了) |
| 4 |
advance-watch.py |
机械判定(文件/rc/哈希/锁) |
✅ 判断正确:无状态、只读、零 token |
留 |
| 5 |
每小时心跳 |
兜底 |
✅ 对:唯一排期源、不依赖任何会话 |
留(收敛自停) |
| 6 |
看板 / SOP / §1.5 |
给人看 / 给 AI 读 |
✅ 对 |
留 |
4 正确形态:2 个声明式操作 + 1 份规则(最小集)
⇒ 在这个形态里,"监管者"不存在 —— 判定与派活是动作,不是角色。
谁做这两个动作:两个场合,各管一段、互不重叠:
| 场合 |
谁 |
判什么 |
为什么是它 |
成本 |
| 收尾自判 |
刚干完活的这个会话(已经在跑!) |
只判「我这条线」还有没有缺口 ⇒ 有则写下一行 automations |
🔴 它本来就在跑 ⇒ 不需要再开一个会话 |
零额外会话 |
| 心跳巡检 |
心跳(每小时 · 宿主排期) |
全局限:V1–V7 是否全过 / 是否有线停滞 |
全局信息只有它能周期性拿到 |
1 会话/小时 |
串行/单例(零自造协议):两者都先抢域锁(--domains ai1net-dsh-server/);
抢不到 ⇒ 什么都不做(说明已有人在推进)⇒ 天然去抖,且没有任何"需要记得清理"的东西。
5 新旧对比(一句话)
|
旧(打转态) |
新(最小态) |
| 一棒收尾后 |
叫一个"监管会话" → 它判、它派 |
自己抢锁 → 判本线 → 写下一行 |
| 额外角色 |
1 个(监管棒) |
0 |
| 额外会话/棒 |
1 个 |
0 |
| 自造协议 |
.dispatch-claim(忘删即整链停) |
0(用域锁) |
| 必须活着的进程 |
0(✅ 已做到) |
0 |
6 落点改动(具体到文件)
| 文件 |
改什么 |
CODEBUDDY.md §1.5 D⑤ |
删掉 mkdir 去抖;改为「先抢域锁当单例;抢到则判本线缺口并写下一行 automations;抢不到 ⇒ 什么都不做」 |
协同监管棒-SOP.md §9.2 |
同上;并把"监管棒"术语退役,改为「收尾自判(棒内)」+「心跳巡检(全局)」 |
| 术语 |
⛔ 不再出现"协同监管棒"这一角色;只保留两个动作的名字 |
7 怎么证明这次没有再打转(判据)
| 判据 |
目标 |
现状 |
| 自造件数 |
≤3(advance-watch / 看板 / 规则文档) |
3 ✅ |
| 自造协议数("需要记得清理"的) |
0 |
改后 0(去抖用域锁) |
| 必须活着的进程数 |
默认 0 |
⚠️ 1(用户 2026-09-29 明确选择加"薄消费者" thin-consumer.py)—— 它只读/零令牌/不派活/输出不接回任何会话 ⇒ 换来秒级可见性;⛔ 它不提升"推进"的及时性(派活仍受宿主排期与口令约束) |
| 必须存在的会话数 |
0(心跳随便哪个会话跑) |
0 ✅ |
| 额外会话 / 棒 |
0 |
改后 0(收尾自判在已存在会话内完成) |
🔑 判据的用法:以后凡是给这套系统加东西,先过这张表 —— 只要让"必须存在的东西"或"需要记得清理的东西"变多,就要停下来重新想,而不是继续补。
8 常驻的精确边界 + 对「独立消费者进程 + 队列」方案的评审(2026-09-29 用户提出)
8.1 先纠正我自己的过度概括
- 我此前:把"常驻"一刀切禁了。
- 实际判据(项目原文):⛔ 禁的是**「会话的后台任务」** —— 它每轮输出都会唤醒宿主会话 ⇒ 永不空闲 ⇒ 用户看到"卡死"。
✅ 独立进程(独立窗口 / 计划任务,输出不接回任何会话)是项目明列的"正确用法"。
- ⇒ "让自己获得持续监控能力"用独立进程拿,不违反硬约束。这一点用户纠正得对。
8.2 方案逐条评审
| 方案要素 |
评审 |
改法 |
| "开发一个程序"给我持续监控能力 |
✅ 形态正确(独立进程:不占会话、不唤醒宿主) |
但要过 §7 判据(会让「必须活着的进程 0→1」) |
| "通过 hook 把结果放队列" |
⚠️ hook 不联网、不读令牌(七条纪律)⇒ 它只能"抄"本地已有数据 |
hook 侧只做本地只读:把宿主的 automation_runs 结论抄进队列 即可 |
| "用一个队列(文件)" |
🔴 冗余:结论已在宿主表里(append-only + rowid 有序)⇒ 再造一份文件 = 第二个状态源(双写/去重/清理义务 —— 正是我刚删掉的那类) |
队列 = 宿主表本身;"逐条处理" = 维护 rowid 水位(已实现 runs.watermark)⇒ 幂等、零清理 |
| "一条条去处理"(消费者=独立进程) |
🔴 撞口令墙:独立进程不是 WorkBuddy 后代 ⇒ 读不到口令 ⇒ 不能派活(同 G-C) |
拆两半:判定归独立进程(只读、零令牌);派活归能拿口令的一方(会话/自动化) |
| "处理"= 推进 |
⚠️ 独立进程再快也只能写"待办",开不了会话 |
派活仍受宿主排期约束 ⇒ 它换来"更早发现",换不来"更早推进" |
8.3 结论(可选增益,非必需)
- 它能拿到:秒级发现/告警("完成即看见")。
- 它换不到:秒级推进 —— 派活那一步被两道墙夹住(① 只有宿主能开会话 ② 独立进程无口令)。
- ⇒ 建议:先不加。若确有"秒级可见性"需求,再加薄消费者:零令牌、只读 DB、写
needs-dispatch.md,⛔ 不派活,并先过 §7 判据。
- ⛔ 不要做的三件:① 独立进程直写宿主表(改宿主状态,schema/缓存风险)② 去偷读 WorkBuddy 进程 env 拿口令(= 口令导出,违 T3/T4)③ 用文件队列替代水位指针(造第二状态源)。