Files

76 lines
6.4 KiB
Markdown
Raw Permalink Normal View History

# 投递链路真因定位 · 「投不进跟进会话」到底卡在哪
- 定位时间:2026-10-02 09:45–09:56
- 定位会话:本会话(用户直呼)
- 一句话:**真因不是「没有跟进会话」,而是「名册里的跟进会话都没在跑」** —— 而机制把这句记成了 `no-follow-session`(字面像"没有"),**这个错名把两天的排查带偏了**。
---
## 一、真因(六层证据链,同一路径插桩实测)
| # | 环节 | 实测读数 | 判读 |
|---|---|---|---|
| 1 | 目标类别表 | `_goal_topics()` = `['会话协作自检', '机制排查与修复']` | 正常 |
| 2 | 跟进名册 | `_scan_follows()` = 6 条;`by_topic` 两个类别都有 | 正常 |
| 3 | 候选解析 | `follow_for_topic(...)` ⇒ `cand=['57f58ecf']` | 正常 |
| 4 | **活会话** | `discover_gateways()` 只有 **1** 条:主会话 `3f43ce71` | **候选不在其中** |
| 5 | 判读 | `why='follow-not-live'` | **准确** |
| 6 | 落库 | `_deliver_str` 记 `skipped='no-follow-session'` | 🔴 **错名** |
插桩原文(`tmp/_tick_instrument.py`,与 `--tick` 同一条调用路径):
```
[SCAN] arg=None -> topics=['会话协作自检', '机制排查与修复'] sids=6 by_topic={两类别都有} err=''
[FFT] topic='' live=['3f43ce71'] -> sid=- why='follow-not-live' cand=['57f58ecf']
[RESULT] deliver={'skipped': 'no-follow-session'}
```
⇒ **卡点在第 4 层**:名册里的 `[跟进]`(`57f58ecf`、`6ab1463e`)**全部是 `completed` 状态**,不在活会话里。
## 二、错名的来源(`collabd.py:1545-1558`)
```python
if g is None:
if _want == "": # ← 只看"有没有解析出 sid",⛔ 不看 why
_n = len(_rft.get("all") or [])
if _n: need_user("…找到 %d 条 `[跟进]-…`,都不活…")
else: need_user("…一条 `[跟进]-…` 都没有…")
info["skipped"] = "no-follow-session" # ← 两种情况都记成这个名字
else:
need_user("跟进会话 %s 此刻不在活会话里…")
info["skipped"] = "follow-not-live"
```
`follow_for_topic()` 本来**算得准**(`:2674` 会返回 `why ∈ {在, no-follow-session, follow-not-live}`),
但 `_deliver_str` **丢弃了 `why`**,只按 `_want == ""` 分档 ⇒ 把两种完全不同的情况合并成一个名字:
- **(a) 真·一条跟进会话都没有**(候选集空 ⇒ `why='no-follow-session'`)
- **(b) 有跟进会话,但都不在跑**(候选非空 ⇒ `why='follow-not-live'`)← **当前就是这种**
⚠️ **代价**:日志与 `NEED-USER.md` 都写成「一条都没有/请建一条」,与事实(**有 6 条,只是没在跑**)相反 ⇒ 排查方向被引向"再多起一条跟进会话",而**多起不管用**。
## 三、叠加故障:`goal.json` 被批量清空(已还原)
- **现象**:`tmp/supervise-inbox/` 下 `goal.json`、`queue.json`、`queue.md`、`NEXT.md`、`ledger.jsonl`、`status.md`、`needs-ai.json`、`runs.watermark`、`_collabd.log` **全部消失**;`NEED-USER.md` 由 16473 B 被重写成 419 B。目录 mtime 09:45→09:49。
- **判定**:**`_collabd.log` 里一行清理记录都没有** ⇒ **不是常驻程序干的**,是外部(一条会话或人);清空发生在 S9 那条协作会话交活的时间窗内(S9 09:40:25 报 done)。
- **后果**:类别表一空,判读从**准确的** `follow-not-live` **恶化**成真·`no-follow-session`,`NEED-USER.md` 的解像话术也跟着错(喊「请把「(默认类别)」那条拉起来」)。
- **已处置**:按逐字副本还原 —— 源 `.workbuddy/collab/bak-goalctl-20261001/goal.json`(10-01 22:29),`md5 = 459bd66138b9069870a34acfc0e3208f`(两侧一致);还原后 `_goal_topics()` 立即恢复两类别、`follow_for_topic` 三问全部回到 `follow-not-live`。现场证据留 `tmp/_goal_restore_20261002/`。
- **说明**:`queue.json` / `NEXT.md` 由常驻程序每轮重算,**无需**恢复;`goal.json` 是**唯一由人写入**的输入源,所以只有它必须还原。
## 四、对「给换新会话和跟进都挂一个后台任务」的判断
1. **原理成立**:投得进去的窗口 = **目标会话当时正在运行**(=它有自己的网关口、是那个口的 live 会话)。历史上真投成过 **3 次**(10-02 01:48→`cbf76e13`@60346、02:56→`ba7b24e8`@57555、04:06→`19e6e204`@49765,`http=200 ok=true`),三次端口全不同。⇒「一直在跑」确实「一直在 live」。
2. **但你 2026-10-01 已明令禁止**(`architecture.md:142`):⛔ 禁止把长跑服务放进**会话后台任务**(理由:每轮输出唤醒宿主 ⇒ 会话**永不空闲** ⇒ 你看到"卡死";**历史已复现 6 次**)⇒「让唤醒会话自己挂后台时钟」**也在禁区,⛔ 不是备选方案**。
3. **且今天已换成不需要它的做法**:时钟不再由会话承担,改由**常驻程序**(`collabd --supervise`)承担,宿主那条 `[唤醒]` 小时排期降级为「引擎全静默时的复活兜底」(`architecture.md:139-141`,S8 治本)。常驻被回收后,下一次事件由 `--tick` 顺手 `ensure_supervise()` 补起 —— **本文件写就期间实测过一次自愈**(停掉 pid 48000 ⇒ 09:54:07 自动起 pid 10448)。
**⇒ 净结论:给会话挂后台任务既被禁止、也已不必要;当前病灶(第 4 层)要治的是「那条 `[跟进]` 会话为什么不在跑」,而不是「没人给它挂时钟」。**
## 五、待办
1. 🔴 **修 `_deliver_str` 的分档**:把 `follow_for_topic` 的 `why` 透传进 `skipped`,让 `follow-not-live` 与 `no-follow-session` 分开记档,并让 `need_user` 文本与之一致(**仅改可读性,⛔ 不改行为**)。改前先查 `board.py` / `selftest.py` / 各判读脚本对该字符串的依赖。
2. **追踪 `goal.json` 被清空的来源**:`_collabd.log` 零记录 ⇒ 外部所为;建议给 `tmp/supervise-inbox/` 的删除动作加一道留痕。
3. **跟进会话的"活着"机制**(S9 复核单已提):要么长期活(=挂后台任务,**被禁**),要么「收完一件能被续起」(=排期,但**只新建不复用** ⇒ 会话堆叠)。两条各撞一条硬约束,⛔ 按现口径不动,等一句话。
---
**取证脚本**(只读,均在 `tmp/`):`_tick_instrument.py`(同路径插桩)、`_tick_trace.py`(全链路中间量)、`_gt_probe.py`(`goal.json` 与类别表)、`_gt_probe.py` 等。