Files
dsh_ai1net_server/交付物/投递死结定位与治本-20261001.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

93 lines
7.0 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-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)⇒ **按"只减不增"⛔ 未擅自压缩,等用户表态**。