- 变更规模:新增 514 / 修改 62 / 重命名 155 / 删除 4(归档重组与文档轮次) - .gitignore 修:`归档/**/db-cwd归一-备份-*/` —— 原规则写绝对层级(归档/db-cwd归一-…), 目录搬进 归档/配置与备份/ 后**静默失效**,43 MB 的 DB 备份又变成未跟踪 - .gitignore 补:嵌套 git 内部数据(归档/内嵌git-20261008/、归档/skills-git-旧线-20261007/dotgit-原样移出/) - .gitignore 补:运行态与部署副本(.workbuddy/collab/、.workbuddy/tools/、.workbuddy/.load-pending、.workbuddy/tmp-*) - .gitignore 补:备份件(*.bak-*) - 未跟踪文件从 2190 降到 890(其余为 归档/ 归档件与 .workbuddy/memory/ 知识文件,按口径入库)
130 lines
9.4 KiB
Markdown
130 lines
9.4 KiB
Markdown
# 多会话协同 · **顶层设计**(宿主能力模型 → 最小形态)
|
||
|
||
> ## ⛔ 已退役(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 由地基**直接推出**的三条约束(不是发明,是推论)
|
||
|
||
1. **无常驻** ⇒ 一切"等待"只能靠**宿主排期**(自动化)或**宿主事件**(钩子)。
|
||
2. **执行体无状态** ⇒ 状态必须落 **DB / 文件**;会话只做"读—判—写"。
|
||
3. **互斥用既有锁** ⇒ ⛔ **不要 `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 写一行 —— 不"叫会话",只"写计划";宿主自己去拉起
|
||
② 收结果(声明式)= 读 automation_runs —— 宿主早就写好了;⛔ 不用催、不用问、不用轮询
|
||
③ 规则(纯数据) = 靶点定义 TARGETS + 判据 V1–V7 + 派活模板
|
||
```
|
||
|
||
⇒ **在这个形态里,"监管者"不存在** —— 判定与派活是**动作**,不是**角色**。
|
||
**谁做这两个动作**:两个场合,**各管一段、互不重叠**:
|
||
|
||
| 场合 | 谁 | 判什么 | 为什么是它 | 成本 |
|
||
|---|---|---|---|---|
|
||
| **收尾自判** | **刚干完活的这个会话**(**已经在跑!**) | **只判「我这条线」还有没有缺口** ⇒ 有则写下一行 `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)③ **用文件队列替代水位指针**(造第二状态源)。
|