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

7.0 KiB
Raw Permalink Blame History

投递「状态窗口死结」定位与治本 +「跟进会话」职责收敛

  • 日期:2026-10-01(23:0x–23:1x)
  • 触发:用户两条指令 ——①「跟进会话 只负责 ,创建协作会话(1、跟进上报后判断是否创建 2、被唤醒后 跟进目标情况 判断是否创建)」②「跟进会话 执行完了 在上报没问题,执行中就等待上报」+报障「队列一直没有上报」
  • 结论:已找到真因并治本(判据换形态、语义不变);回归全绿;残留一项是"真状态"不是缺陷(见 §4)

1. 真因:判据恒真,投递不存在可到达的放行状态

collabd.py 里投递前的「目标忙不忙」判据是:

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