101 lines
7.6 KiB
Markdown
101 lines
7.6 KiB
Markdown
# 唤醒 · 「上报给主会话」到底是不是唤醒 + 门控该归谁(2026-09-30)
|
||||
|
|
|
|||
|
|
> 起因(用户原话):**「会话协作项目中有任何会话在执行时,唤醒定时任务跳过处理(应该是通过 hook),无任何协作队列无任务,且无任何会话执行时,检查目标状态(完成:同时暂停,阻碍:反馈给协作程序 同时暂停,未完成:反馈给协作程序),这样等队列信息上报给主会话时 是不是就唤醒了」**
|
|||
|
|
> 判据:文中每条都标了源码位置(`~/.workbuddy/skills/multi-session-collab/scripts/collabd.py`)。
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 0. 结论(三行)
|
|||
|
|
|
|||
|
|
1. **是** —— 但前提是**主会话此刻活着**。因为在这套机制里,**「上报」不是写文件,是投递**;投递落地 ⇒ 主会话多跑一轮 ⇒ 这就是唤醒。
|
|||
|
|
2. **主会话已退出 ⇒ 投不出去 ⇒ 不会唤醒**。那一格只能由**唤醒轮**(自动任务起新会话)覆盖。
|
|||
|
|
3. 你这条规则里,**"有会话在执行就跳过""无队列才查目标""目标三态判定"大半已经是现成实现**(协作程序 `supervise()` / `_deliver_str`);真正**缺**的是两格:**「完成/阻碍 ⇒ 自动暂停」** 与 **「有队列但无会话执行 ⇒ 派棒」**。
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 1. 把「上报」拆成三段 —— 只有第二段会唤醒
|
|||
|
|
|
|||
|
|
| # | 段 | 具体动作 | 会不会唤醒谁 |
|
|||
|
|
|---|---|---|---|
|
|||
|
|
| ① | **内容** | 写 `tasks.json`(队列本体)、`TO_MAIN.md`(投影)、`blocked.json`、`NEED-USER.md` | ❌ **一个人都叫不起来**(纯落盘) |
|
|||
|
|
| ② | **投递** | 钩子 → `POST /api/v1/sessions/<主会话>/reply {text}`(`collabd.py:1058`) | ✅ **这才是唤醒**:主会话多跑一轮 |
|
|||
|
|
| ③ | **起会话** | 自动任务(唤醒轮) | ✅ 主会话不在时的**唯一**通道 |
|
|||
|
|
|
|||
|
|
> 判据:`supervise()` 先写 `TO_MAIN.md`(`collabd.py:1770`),**紧接着**才 `_deliver_str(txt, "上报·单条", st)`(`collabd.py:1777`)。
|
|||
|
|
> ⇒ **只做了 ① 没做 ②,等于没上报。** 你问的"上报时是不是就唤醒了",答案取决于走没走 ②。
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 2. 你的表述 vs 现有实现(逐格对照)
|
|||
|
|
|
|||
|
|
| 你的表述 | 现有实现(位置) | 状态 |
|
|||
|
|
|---|---|---|
|
|||
|
|
| 有会话在执行 ⇒ 跳过 | `_session_status(目标)=="working"` ⇒ `target-busy` **延后**(`collabd.py:1050`) | ✅ 已有 |
|
|||
|
|
| 队列无任务 | `tasks.json` 四态 `pending/running/done/blocked`,**唯一权威**(`collabd.py:852`) | ✅ 已有 |
|
|||
|
|
| 无任何会话执行 | 宿主库 `sessions.status='working'`(`_main_state` `collabd.py:1616`;`reconcile` `collabd.py:1644`) | ✅ 已有 |
|
|||
|
|
| 检查目标状态 | `goals_open()`:台账非 done **∪** 任务图非 done **∪** `goal.json.acceptance_state` 任一非 pass(`collabd.py:1403`,三路取并集) | ✅ 判据已有 |
|
|||
|
|
| 完成 ⇒ 同时暂停 A、B | 现有只有「任务图全 done ⇒ `skipped=all-done` 不再投递」(`collabd.py:754`)——**它⛔不会去暂停自动任务** | 🔴 **缺** |
|
|||
|
|
| 阻碍 ⇒ 反馈协作程序 + 暂停 | `task_report(state="blocked", reason=…)`/`need_user()`(`collabd.py:878`/`728`) | ⚠️ 接口有;⛔ 见 §3-② |
|
|||
|
|
| 未完成 ⇒ 反馈协作程序 | `goals_open()==True` ⇒ 写差距;现有只在"有活可派/僵住"时才报(`collabd.py:786-798`) | ⚠️ 部分 |
|
|||
|
|
| **有队列任务 且 无会话执行** | —— | 🔴 **你的规则没定义这一格** —— 而这正是**最该动**的那一格(有活没人干) |
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 3. 落地必须先知道的三个硬约束(源码级)
|
|||
|
|
|
|||
|
|
1. **`reply` 只认 live** ⇒ 主会话退出后投不出去;程序此时**拒绝盲投**(不投给任意会话),只按**工作区**解析,解析不出就 `need_user("找不到主会话…")`(`collabd.py:1038-1049`)。
|
|||
|
|
⇒ **"上报即唤醒"对已退出的主会话结构性不成立**,且程序的处置是**喊人**,不是自己想办法。
|
|||
|
|
2. **队列只有「投递方」才配改**(`mutate`,`collabd.py:1690-1696`):`--tick`(宿主钩子)是唯一推进方;`--once`/常驻只做投影。⛔ 唤醒轮**不许手改 `tasks.json`**,要用 `task_report` 口径上报,否则会重演「把待反馈项消费掉却不投递」的静默丢件。
|
|||
|
|
3. **"跳过"是零 token 的**:投递前的否决(`same-item` 内容哈希去重 / `too-soon` 最小间隔 300s / `target-busy`)都在**钩子进程内**完成,不碰会话、不烧 token(`collabd.py:960-967`)。⚠️ 但钩子**拦不住自动任务起会话**——automation 无条件触发,所以唤醒轮"起来后发现有人在跑就立刻退"仍靠**域锁**。
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 4. 完整闭环:两半合起来才闭合
|
|||
|
|
|
|||
|
|
| 主会话此刻 | 谁把它叫起来 | 延迟 | 成本 |
|
|||
|
|
|---|---|---|---|
|
|||
|
|
| **活着 · 空闲** | 钩子**投递**(`reply`) | 秒级~分钟 | 到投递前零 token |
|
|||
|
|
| **活着 · 执行中** | **不打扰**(`target-busy` 延后,等它空闲) | 等它空 | — |
|
|||
|
|
| **已退出**(worker 已回收) | **唤醒轮**(自动任务起新会话) | ≤30 分钟 | **一个新会话** |
|
|||
|
|
|
|||
|
|
> ⇒ 所以「等队列信息上报给主会话时就醒了」——**对"活着"那行成立,对"已退出"那行不成立**;后者靠唤醒轮。
|
|||
|
|
|
|||
|
|
⚠️ **代价(源码里写着的一句话,必须知道)**:`collabd.py:857-860` 注释——
|
|||
|
|
> 「每次投递都会唤起主会话跑一轮 ⇒ 用户发消息撞 busy ⇒ 体感"卡"」
|
|||
|
|
⇒ **"上报即唤醒"=会占用主会话 ⇒ 必须节流**。现有的四道节流:**单条+握手**(一次只反馈一条,等它执行完才发下一条)/**内容哈希去重**/**最小间隔 300s**/**真停滞门槛 15 分钟**(`HEARTBEAT_MIN_IDLE`,有进展一条都不发)。⛔ 落地时不许把这四道拆掉。
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 5. 落地:要补的两格(其余照抄现成)
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
唤醒轮起来 → ① 抢域锁(抢不到 = 有人在推进 ⇒ 立即结束,零成本跳过)
|
|||
|
|
→ ② 读队列 tmp/supervise-inbox/tasks.json
|
|||
|
|
→ ③ 读会话:宿主库 sessions.status='working'(cwd=本工作区,排除自己)
|
|||
|
|
→ ④ 读目标:tasks.json 非 done ∪ taskgraph.json 非 done ∪ goal.json.acceptance_state 任一非 pass
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
| 格 | 条件 | 动作 |
|
|||
|
|
|---|---|---|
|
|||
|
|
| A | 有队列任务(pending/running)**且** 无会话执行 | **派一棒**(起一个会话去干)← 🔴 **你原规则缺的这格** |
|
|||
|
|
| B | 无队列 **且** 无会话执行 · 目标**未完成** | 把差距写进 `tmp/supervise-inbox/`(⛔ 不手改 `tasks.json`) |
|
|||
|
|
| C | 无队列 **且** 无会话执行 · 目标**有阻碍** | 写清"要用户做什么" + **A、B 两条排期一起置 `PAUSED`** |
|
|||
|
|
| D | 无队列 **且** 无会话执行 · 目标**已完成** | **A、B 两条排期一起置 `PAUSED`** ← 🔴 **现成实现没有这一格** |
|
|||
|
|
| E | 判不准 | 按 B 处理(宁可空转一轮,⛔ 不许造活) |
|
|||
|
|
|
|||
|
|
**A/B 排期 id**(自我暂停时用;⛔ 只改 `status`,不动 `rrule`):
|
|||
|
|
- 唤醒轮 A `caca9a89-fa82-48e1-a55c-7e0f3a06bd48` | 唤醒轮 B `16bec5ce-98c0-470c-afa9-6f5a06218068`
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 6. 一句话速记
|
|||
|
|
|
|||
|
|
> **上报(写文件)≠ 唤醒;投递(钩子把文字推进活着的会话)才=唤醒。**
|
|||
|
|
> 主会话活着 ⇒ 秒级唤醒(但要节流,否则把它顶"卡");主会话退出 ⇒ 钩子也没了,只剩唤醒轮起新会话(≤30 分钟)。
|
|||
|
|
> 判据一句:**"有会话在执行就跳过"= `sessions.status=='working'` 或 域锁被占。**
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 附:图
|
|||
|
|
|
|||
|
|
见同目录 `唤醒-上报即唤醒与门控归属-20260930-图.html`(① 三段拆解图:标出哪一段才唤醒;② 门控矩阵图:标红两格缺失)。
|