- 变更规模:新增 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/ 知识文件,按口径入库)
6.4 KiB
6.4 KiB
投递链路真因定位 · 「投不进跟进会话」到底卡在哪
- 定位时间: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)
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是唯一由人写入的输入源,所以只有它必须还原。
四、对「给换新会话和跟进都挂一个后台任务」的判断
- 原理成立:投得进去的窗口 = 目标会话当时正在运行(=它有自己的网关口、是那个口的 live 会话)。历史上真投成过 3 次(10-02 01:48→
cbf76e13@60346、02:56→ba7b24e8@57555、04:06→19e6e204@49765,http=200 ok=true),三次端口全不同。⇒「一直在跑」确实「一直在 live」。 - 但你 2026-10-01 已明令禁止(
architecture.md:142):⛔ 禁止把长跑服务放进会话后台任务(理由:每轮输出唤醒宿主 ⇒ 会话永不空闲 ⇒ 你看到"卡死";历史已复现 6 次)⇒「让唤醒会话自己挂后台时钟」也在禁区,⛔ 不是备选方案。 - 且今天已换成不需要它的做法:时钟不再由会话承担,改由常驻程序(
collabd --supervise)承担,宿主那条[唤醒]小时排期降级为「引擎全静默时的复活兜底」(architecture.md:139-141,S8 治本)。常驻被回收后,下一次事件由--tick顺手ensure_supervise()补起 —— 本文件写就期间实测过一次自愈(停掉 pid 48000 ⇒ 09:54:07 自动起 pid 10448)。
⇒ 净结论:给会话挂后台任务既被禁止、也已不必要;当前病灶(第 4 层)要治的是「那条 [跟进] 会话为什么不在跑」,而不是「没人给它挂时钟」。
五、待办
- 🔴 修
_deliver_str的分档:把follow_for_topic的why透传进skipped,让follow-not-live与no-follow-session分开记档,并让need_user文本与之一致(仅改可读性,⛔ 不改行为)。改前先查board.py/selftest.py/ 各判读脚本对该字符串的依赖。 - 追踪
goal.json被清空的来源:_collabd.log零记录 ⇒ 外部所为;建议给tmp/supervise-inbox/的删除动作加一道留痕。 - 跟进会话的"活着"机制(S9 复核单已提):要么长期活(=挂后台任务,被禁),要么「收完一件能被续起」(=排期,但只新建不复用 ⇒ 会话堆叠)。两条各撞一条硬约束,⛔ 按现口径不动,等一句话。
取证脚本(只读,均在 tmp/):_tick_instrument.py(同路径插桩)、_tick_trace.py(全链路中间量)、_gt_probe.py(goal.json 与类别表)、_gt_probe.py 等。