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

130 lines
9.4 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 多会话协同 · **顶层设计**(宿主能力模型 → 最小形态)
> ## ⛔ 已退役(2026-09-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)③ **用文件队列替代水位指针**(造第二状态源)。