92 lines
7.0 KiB
Markdown
92 lines
7.0 KiB
Markdown
# 投递「状态窗口死结」定位与治本 +「跟进会话」职责收敛
|
||||
|
|
|
|||
|
|
- 日期:2026-10-01(23:0x–23:1x)
|
|||
|
|
- 触发:用户两条指令 ——①「**跟进会话 只负责 ,创建协作会话(1、跟进上报后判断是否创建 2、被唤醒后 跟进目标情况 判断是否创建)**」②「**跟进会话 执行完了 在上报没问题,执行中就等待上报**」+报障「**队列一直没有上报**」
|
|||
|
|
- 结论:**已找到真因并治本**(判据换形态、语义不变);回归全绿;**残留一项是"真状态"不是缺陷**(见 §4)
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 1. 真因:判据**恒真**,投递不存在可到达的放行状态
|
|||
|
|
|
|||
|
|
`collabd.py` 里投递前的「目标忙不忙」判据是:
|
|||
|
|
|
|||
|
|
```python
|
|||
|
|
if _session_status(目标) == "working": # 忙 ⇒ 延后
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
而 `sessions.status` 的**取值域**实测只有 —— **`working` / `completed` / `error` / `archived`**(当天分布:`completed 123 / working 2 / archived 2 / error 1`):
|
|||
|
|
|
|||
|
|
| 时刻 | 库里 status | 结果 |
|
|||
|
|
|---|---|---|
|
|||
|
|
| 会话**活着**(含跑完一轮、停在等下一轮) | `working` | 判**忙** ⇒ 不投 |
|
|||
|
|
| 会话**跑完** | `completed` | **不 live** ⇒ 也不投 |
|
|||
|
|
|
|||
|
|
⇒ **不存在任何一个能投进去的时刻**。现场读数:跟进会话 `e2ccdea3` 22:39:26 创建、`last_activity_at` 冻在 22:39:27,协作程序**连打 6 分钟** `延后投递:跟进会话 e2ccdea3 正在执行 ⇒ 等它空闲` + `投递未成(target-busy)⇒ 保留 M5=done 在队首`。
|
|||
|
|
|
|||
|
|
**旁证**:`wakeups.jsonl` 里**唯一投成的那一次**(08:55 `http:200 ok:true`)目标是 `f8a792ab` —— 一条**日志已冻结的"死"会话**。⇒ **只有"死"会话才投得进去。**
|
|||
|
|
|
|||
|
|
## 2. 正解:宿主自己写了权威判据,但**只写日志、不进数据库**
|
|||
|
|
|
|||
|
|
宿主工作区日志(`<config>/logs/<日期>/<工作区>__*.log`)里有 `[SessionRunStateMachine]` 记录:
|
|||
|
|
|
|||
|
|
| 事件 | 结果 |
|
|||
|
|
|---|---|
|
|||
|
|
| `event=AGENT_STARTED` / `RUN_ACCEPTED` | `lifecycle=running` **`busy=true`** |
|
|||
|
|
| `event=AGENT_ENDED` | `to=idle` `lifecycle=idle` **`busy=false`** `queueBusy=false` |
|
|||
|
|
|
|||
|
|
⇒ 这就是"**本轮在跑 / 轮间空闲**"的分界,而 `sessions` 表里**永远查不到**。
|
|||
|
|
|
|||
|
|
## 3. 治本(语义不变、只换判据形态)
|
|||
|
|
|
|||
|
|
`collabd.py` 新增 `_session_busy()` + `_target_busy()`,替换三处判据(第 994 / 1231 / 1364 行):
|
|||
|
|
|
|||
|
|
1. **库里 `status` ∈ `SID_DEAD`(已终结)⇒ 直接判"没在跑"**,⛔ 不看日志。
|
|||
|
|
🔴 **必须有这一道**:实测**自动化拉起的会话跑完不写 `busy=false`** —— 当天整份日志里 `busy=false` 只出现 **12 次、全是主会话的**;`e2ccdea3` 末条状态机记录是 `22:42:10 … busy=true`,而库里 `22:45:53` 已是 `completed` ⇒ 只看日志会把**早就结束的会话**再"忙"上十几分钟。
|
|||
|
|
2. **`status == working` ⇒ 才**读日志:取该 sid 的**最后一条**状态机记录,`busy=true` **且新鲜** ⇒ 忙;否则 ⇒ 空闲。
|
|||
|
|
🔴 「新鲜」是**必须**的一半:真在跑时状态机**每几百毫秒写一行**,所以"末条 `busy=true` 却已是 N 分钟前"**只可能**是它早停了 ⇒ **不必依赖"正好抓到那条 `AGENT_ENDED`"**(它会被挤出尾部窗口)。
|
|||
|
|
⚠️ `SRSM_FRESH = 900 s`:长工具调用期间状态机**全程静默**(实测一次 8 MB 日志扫描静默 ~6 分钟)⇒ 窗口取太短会把**正在跑**的误判成**空闲**(方向相反的错:往正在跑的会话里插话)。多等 ≤15 分钟不算损失。
|
|||
|
|
3. 读不到 ⇒ **按"没在跑"处理**并**留一行日志**(⛔ 不按"忙"处理 —— 那只是把死结换个形状)。
|
|||
|
|
|
|||
|
|
## 4. 残留(如实报):拦截原因变成了**真状态** `no-follow-session`
|
|||
|
|
|
|||
|
|
判据换对之后,协作程序 `--tick` 的输出立刻从 `deliver=target-busy` 变成 **`deliver=no-follow-session`** —— 这不是新缺陷,是**在此之前一直被假判据盖住的真事实**:
|
|||
|
|
|
|||
|
|
> **此刻一条活的 `[跟进]` 会话都没有**(`e2ccdea3` 22:45:53 跑完变 `completed`,随即退出网关的活会话集;活会话只剩主会话与常驻容器两条)。
|
|||
|
|
|
|||
|
|
⇒ **"推"只在目标活着时可用**;目标不在线时靠**排期到点拉起一条**:
|
|||
|
|
|
|||
|
|
| 排期 | 下一跳 | 状态 |
|
|||
|
|
|---|---|---|
|
|||
|
|
| `[唤醒]-会话协作自检-脉冲` | 23:14:20 | ACTIVE |
|
|||
|
|
| `[跟进]-会话协作自检-队列上报` | 23:21:35 | ACTIVE |
|
|||
|
|
|
|||
|
|
## 5. 用户口径落地:跟进会话**只负责创建协作会话**
|
|||
|
|
|
|||
|
|
- **职责收敛**:第④类只做一件事 —— **判断「要不要创建协作会话」,要就建一条**。两条触发(**收到队列上报** / **被唤醒**)走同一个动作;⛔ 它自己**不做具体活**(不改台账 `state`、不写 `blocked.json`、不派活、不抢锁)—— 那些归**它建出来的那条协作会话**。
|
|||
|
|
- **落点**:① 生产排期 `[跟进]-会话协作自检-队列上报` **提示词已改**(改后 `nextRunAt` **复核未变**=23:21:35)② `references/architecture.md §2.3.0c` ③ `SKILL.md §2` 四类行。
|
|||
|
|
|
|||
|
|
## 6. 本轮我自己踩的三处「判据看着在、其实恒假」
|
|||
|
|
|
|||
|
|
| # | 形状 | 后果 | 修法 |
|
|||
|
|
|---|---|---|---|
|
|||
|
|
| (a) | 正则写成 `(?:\.(\d+))?` —— 内层 `(\d+)` **仍是捕获组** | `m.group(8)` 整体**偏移一位** ⇒ `busy` **恒读 `False`** ⇒ **恒判"空闲"**(方向相反:会往正在跑的会话里插话) | 改**命名组** `(?P<busy>…)` + 加**源码级反回归断言** |
|
|||
|
|
| (b) | 只比 `t > last[0]` | **同一秒内"后出现的没胜出"** ⇒ 取到旧状态 | 改比 `(时间, 行序)` 二元组 |
|
|||
|
|
| (c) | 变异法对照脚本的过滤词是 `-k 忙判据` | **没覆盖** `t_target_busy` ⇒ **变异体根本没被试**、却报"全绿" | 换过滤词 ⇒ M4/M5 双双报红 |
|
|||
|
|
|
|||
|
|
⇒ 🔴 **判据**:**变异法跑完必须回读"这个变异体到底被哪几条用例跑了"**,⛔ 不看"合计 PASS"就当证毕。
|
|||
|
|
|
|||
|
|
## 7. 回归(全部现算,⛔ 不写死期望值)
|
|||
|
|
|
|||
|
|
| 项 | 读数 |
|
|||
|
|
|---|---|
|
|||
|
|
| `selftest.py` | **PASS 45 / FAIL 0**(新增 `t_srsm_busy` 6 项;旧 `t_target_busy` **同批改**并**补反向**「目标空闲 ⇒ ⛔ 不延后,往下走到 live 判」) |
|
|||
|
|
| 变异法(5 组) | 打掉新鲜度 ⇒ ③ 红|打掉"先判终结态" ⇒ ⑤ 红|复现错位 bug ⇒ ①⑥ 红|判据恒真 ⇒ ② 红|判据恒假 ⇒ ① 红 —— **逐一按预期报红,`finally` 还原后复绿** |
|
|||
|
|
| `install.py --verify` | **全绿**(含 `selftest` 45/0) |
|
|||
|
|
| `py_compile` | `collabd.py` / `selftest.py` 均过 |
|
|||
|
|
|
|||
|
|
## 8. 未处理(留着,不擅自动)
|
|||
|
|
|
|||
|
|
- `[跟进]-[会话协作自检]-点火验证投递`(一次性,已跑完,`nextRunAt` 为空 ⇒ 不会再触发)仍留在排期表里。
|
|||
|
|
- 常驻投递载体(任务图节点 **S8**)仍**未重起** —— 载体应是专用容器会话。
|
|||
|
|
- 待投队列仍压着 `notify_pending = ["M5=done","M7=running"]`(**旧线「唤醒机制」残留件**)。
|
|||
|
|
- `.workbuddy/memory/MEMORY.md` **超上限约 330 字符**(约 7,980 / 上限 7,650)⇒ **按"只减不增"⛔ 未擅自压缩,等用户表态**。
|