Files
dsh_ai1net_server/交付物/任务图-会话协作自检.json
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

157 lines
35 KiB
JSON
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.
{
"goal": "检查会话协作是否运行正常 + 协作机制问题排查与修复",
"updated": "2026-10-03T11:20:00",
"_为什么新开一份": "🔴 原 `交付物/任务图.json` 是**手机接入线**的(goal 写着「手机经 ai1net 覆盖网络操作电脑上的 WorkBuddy 会话」,节点 M1–M7 **全 done**)。那条线 2026-09-30 已退役 ⇒ `goal.json.taskgraph` 指着它 ⇒ **可派集合恒空** ⇒ 队列 0 件 ⇒ `NEXT.md` 永不产生 ⇒ 四道闸门第一道就断。本文件是**当前目标**的任务图。⛔ 不删原文件(它是那条线的证据)。",
"rules": {
"dispatch": "只派「依赖已满足」的节点;优先派**关键路径**上的节点;一条线同时只挂一个,多条线可同时挂",
"domain": "域锁按**节点涉及的具体文件/子目录**声明 —— 不重叠即可并行。⛔ 禁止再用整工作区域粗域(自造串行瓶颈)",
"handoff": "收口即派(+3~4 分钟),⛔ 不要 +5~8 分钟空窗(🔴 2026-10-01 用户口径:基准=收口+3~4 分钟)",
"waste": "若「可派集合」非空且没有任何棒在跑 ⇒ 属**可派未派(浪费)** ⇒ 立即派"
},
"nodes": [
{
"id": "S1",
"title": "排查并修掉机制缺陷(本轮三处:删「负责类别」假话行/install.py 括注套娃/看板角色二分)",
"line": "机制排查与修复",
"status": "done",
"deps": [],
"evidence": "三处均已修并回归:① `board.html` 删假话行 + `R2.h` 回 84 + 「当前」块改按 `topics_source.kind`(渲染桩 0/47、几何 0/7);② `install.py --manifest --note` 括注套娃(292→155 字符);③ `board.py::_role_label()` 四类标签 + `board.html` 第三层判据改 `role==='协作会话'`。取证:`.workbuddy/memory/2026-10-01.md` §20:3x"
},
{
"id": "S2",
"title": "第④类「队列上报的跟进会话」角色落地(四处同款 + 对账用例 3→4 项)",
"line": "机制排查与修复",
"status": "done",
"deps": [
"S1"
],
"evidence": "`collabd.py::parse_session_name()` + `board.py::_role_of_title()` 加 `\"跟进\":\"follow\"`;两边排除元组 → `(\"worker\",\"waker\",\"follow\")`;`selftest.py` 逐样本对账加 `[跟进]-…`(实现测 `{'role':'follow','topic':'会话协作自检','ok':True}`);`install.py --verify` 全绿(PASS 39/FAIL 0)"
},
{
"id": "S3",
"title": "开工清单规则写进五个载体(skill §2/architecture §2.3.0c/0d/collab §4.1/CODEBUDDY §F/MEMORY 行)",
"line": "机制排查与修复",
"status": "done",
"deps": [
"S1"
],
"evidence": "用户原话「开始会话完成需求的时候,主会话需要创建 唤醒会话 以及根据分工类别 创建 协作会话 和 跟进会话」⇒ 落五处;MEMORY.md 净减 33 字符(仍超上限 330,既有超限,登记 P2)"
},
{
"id": "S4",
"title": "修:起常驻投递(验收 V1-常驻投递)",
"line": "机制排查与修复",
"status": "done",
"deps": [
"S1"
],
"evidence": "2026-10-01 21:0x 队列实读([跟进]-会话协作自检-队列上报):queue.json head=S4 / doing={} / pending=3 / gate=free;NEXT.md 已生成这一条 ⇒ 待执行·队首。⚠️ 但无人在做、且 advance.md 记「未来 1 小时内没有任何排期」⇒ 命中本图 rules.waste「可派未派」。⛔ 未开工 ⇒ status 仍 todo(⛔ 不为占位改 running)。真因读数:goal.json acceptance_state.V1「未过(无 --supervise 进程;pid3116@20:52:08 已 DEAD;tick 新鲜来自前台/钩子)」。⚠️ 另测:文档库**全局执行锁已被占** —— `05-交接单/.exec-lock/OWNER` = `会话协作-机制排查与修复-20261001`(开始 10-01 20:41,**未声明单号**);S4 改的是机制层(`collabd.py`/部署配置)⇒ 按开工第 0 步须**占【E】独占** ⇒ 接棒前先 `--claim-exec`,抢不到 ⇒ 停手(R9:⛔ 不得删锁/接管)。|✅ 核对 ⇒ 置 done([跟进]-[会话协作自检]-点火验证投递 @2026-10-01 22:4x):产物成立,逐项实测 —— ① `_collabd.log` 节拍连续:22:18:57「supervise loop start」起每约 22 s 一轮、连续无中断至 **22:30:50**(该段共 42 行),比主会话记的 22:22:10–22:28:59 更长;② `wake.lock` 存在、内容 `free`、mtime 22:36 在走 ⇒ 零删除成立;③ `_collabd.log` 内 `SAFE_DELETE` 命中 **0** 条 ⇒ >22:18:57 零新增成立;④ 前置探针最后一条停在 22:12:07(旧代码),之后 0 条。⚠️ **但「常驻」没守住**:进程表实测(Get-CimInstance Win32_Process)已无常驻进程 —— 只剩 `board.py --serve 8788`(pid16428);宿主库会话表 `3309bb55 [协作]-机制排查与修复-常驻投递容器` **completed @22:30:55** ⇒ 节拍停 22:30:50、进程随之被带走。⛔ 不推翻 V1 原判据(>10 min 存活),但证明「一直运行」未达成 ⇒ 另立 S8 追踪。"
},
{
"id": "S8",
"title": "修:常驻投递的载体(容器会话 completed ⇒ 常驻进程被带走)",
"line": "机制排查与修复",
"status": "done",
"deps": [
"S4"
],
"evidence": "✅ **2026-10-02 08:0x 修毕并取证([协作]-[机制排查与修复]-S8 常驻投递载体·重派)**。\n🔑 **根因(实测,⛔ 非推断)**:① **`CREATE_BREAKAWAY_FROM_JOB` 被宿主作业对象拒绝**(`PermissionError(13,'拒绝访问。')`)⇒ 本机**不存在**能脱离宿主回收的子进程;② 普通子进程**能**活过**工具调用边界**(三探针跨调用持续打点 40 s+,父进程 `8676` 早已消失)但**迟早被回收**(历次 8 / 12 / 20 分钟)⇒ 「单个进程一直活着」在本机不成立 —— 旧形态把载体押在「容器会话别关」上,**结构上兑现不了**。\n🔴 **修法=事件驱动的常驻自愈**(`collabd.py`):`--supervise` 每轮写**心跳**(`.workbuddy/collab/logs/supervise-heartbeat.json`,**原子替换/零删除**,承接 P0-6 判据)+ 节拍 `…-heartbeat.log`(超 512 KB **重写**保留末 1500 行)+ 启动时**单例让位**(防双写);**`--tick`(宿主钩子每次事件都会跑)顺手续命** `ensure_supervise()`(幂等 · 30 s 节流 · **无口令不起** · 无配置不起);新增 `--ensure`;**存活唯一机读判据=`pid 活 ∧ 心跳新鲜(<90 s)`** —— 此前**无人写心跳** ⇒ `session-rules-check` 第 ⑩ 项**恒 warn**,现可绿。\n📊 **三条判据真读数**:\n ① **节拍连续时长**:当前实例 `pid=47156`(起 2026-10-02 07:55:18),节拍段 `2026-10-02 07:55:19 → 2026-10-02 08:01:19`,**37 行 / 连续 6.0 分钟**(本轮内为做自愈取证多次主动杀掉 ⇒ 段长被切断,见下)。\n ② **进程表读数**:全量 `Win32_Process` 扫 `*collabd*` ⇒ **`collabd.py --supervise` 在**(pid 47156;另 `board.py --serve 8788` 一份)。\n ③ **`wake.lock`**:稳态存在、内容 `free`(=P0-6 语义「释放 = 原地改内容、永不删除」)、mtime 随投递轮同步前进。\n🧪 **自愈实证(本棒现算,最关键的一条)**:07:55:08 `Stop-Process` 杀掉 `pid 48248`(回读 `AFTER_COUNT=0` ⇒ 常驻真的没了)⇒ 07:55:17 跑一次 `--tick`(**=钩子走的那条路**)⇒ 07:55:18 **新实例 `pid 47156` 自动起来**(心跳 `round=1`)。`_collabd.log` 三次续命记录**全部 `why=tick`**(07:46:49 pid=38692 / 07:54:02 pid=48248 / 07:55:18 pid=47156)。\n🔴 **教训(已入 `pitfalls.md P0-22`)**:「进程还活着」**⛔ 不能从「日志/戳在动」推** —— 钩子每轮也写同一个日志,**四棒都被这条骗过**。同轮另一个同族坑:`_pid_alive` 第一版走 `tasklist`(输出 **GBK** ⇒ `text=True` 抛 `UnicodeDecodeError` ⇒ 判据**静默变假**)⇒ 改内核句柄(`OpenProcess` + `STILL_ACTIVE`;`ACCESS_DENIED` **保守判「在」**)+ 单点四读数验证通过。\n✅ **回归**:`selftest` **PASS 46 / FAIL 0**(新用例 `t_supervise_ensure` 8 项)|`install.py --manifest` 35 份 / 语法失败 0|`py_compile` 全过。\n⚠️ **残留(如实报)**:本机**不存在**「跨全静默期仍活着」的进程 ⇒ **引擎全无事件**时最长空窗 = 下一条 `[唤醒]` 排期的间隔(≤1 h;它的身份=**复活兜底**,⛔ **不是时钟**)。⇒ 若判据要求「连全静默期也算连续」,那是**平台边界**,只能在「加排期密度」与「接受空窗」之间取舍。",
"done_at": "2026-10-02 08:01",
"done_by": "[协作]-[机制排查与修复]-S8 常驻投递载体(重派·锁已清)"
},
{
"id": "S9",
"title": "修:投递「目标忙」判据(活会话恒 working ⇒ 投递恒被推迟)",
"line": "机制排查与修复",
"status": "done",
"deps": [
"S4"
],
"evidence": "🔴🔴 2026-10-01 22:4x 源码级取证([跟进]-[会话协作自检]-点火验证投递):「目标忙 ⇒ 不投」这条判据在本机**恒真**,投递到「活的跟进会话」**永远投不出**。证据链(⛔ 全为逐行读数,非推断):① `_session_status()`(`collabd.py:1403`)只回 `sessions.status` 小写串;② `SID_DEAD = {\"completed\",\"error\",\"archived\"}`(`:512`,注释即写「实测取自 sessions.status 取值域」)⇒ **「活的」判据 = `status ∉ SID_DEAD`**(`:548`);③ 投递延后判据 = `if _tgt and _session_status(_tgt) == \"working\": skipped=\"target-busy\"; return`(`:1235`)。⇒ 「活」与「忙」**是同一根字符串 `working`** ⇒ 活 ⇒ 忙 ⇒ 恒 `target-busy`,逻辑上**没有可到达的放行状态**。④ 取值域实测(只读宿主库 `E:/ProgramData/.workbuddy/workbuddy.db`,`select status,count(*) from sessions group by status`):`completed` 123 / `working` 2 / `archived` 2 / `error` 1 ⇒ **没有「空闲」这一档**(旁证:`--tick` 那句「等它空闲」在本机无对应状态)。⑤ 现场读数:`_collabd.log` 22:39:27 / 22:42:46 / 22:43:06 / 22:43:43 连续为「延后投递:跟进会话 e2ccdea3 正在执行 ⇒ 等它空闲」+「投递未成(target-busy)」。⚠️ 这是用户报障「协作会话完成后不投递队列、也不上报给跟进会话」的**根因之一**:把排期从 once 改 recurring 只能让收件人**被找到**(`no-follow-session` → `target-busy`),⛔ 不能让它**收得下**。⇒ 后果:本轮「点火验证投递能到达」在现判据下**不可能通过**,⛔ 别再靠「多起一条跟进会话」重试。修法方向(接棒自选):给「忙」一个**可让路 / 可超时**的判据 —— 例如「最近 N 秒内有实际活动才算忙」或「该会话有 pending/running 后台任务才算忙」,⛔ 不得再看 `status` 字符串。⚠️ 同族证据已由同伴会话记入 `.workbuddy/memory/2026-10-01.md` §三·一,本条=把它**落进队列**(此前只在日志里,队列派不到)。\n\n|✅ **核对 ⇒ 置 done(2026-10-02 09:3x–09:4x,[协作]-[机制排查与修复]-S9 投递目标忙判据复核与放行验证 · sid 6a501f20)**。**两层都做了现场复核,判据层 1 已解开(有端到端实证)、卡点已前移到层 2(无活跟进会话)。**\n🔑 **层 1「目标忙是否仍恒真」=已不恒真**(三路证据):① **源码形态**:`_target_busy()`(`collabd.py:1698`)两道判 —— 库 `status ∈ SID_DEAD` ⇒ 直接「没在跑」;库 `working` ⇒ 才读宿主状态机日志 `[SessionRunStateMachine] … busy=`,且要求**末条 `busy=true` 且距今 ≤ `SRSM_FRESH=900 s`**(`:1656`;正则用命名组防「数括号错位」),读不到按「没在跑」并留日志。② **时间序列(`_collabd.log` 全文 09-30 07:41:56 → 10-02 09:38:22)**:`target-busy` **最后一次=2026-10-02 00:35:24**(被拦的 `b11c099d` 是真·`[跟进]-会话协作自检-队列上报`,当时 `status=working` ⇒ 即旧判据恒真现场);**新判据首次现身=01:48:10**(「忙判据读不到(cbf76e13)」);**00:35:24 → 09:38:22 ≈ 9h03m 区间 `target-busy` = 0 条**。③ **端到端放行(真投递,非合成样本)**:新判据生效后 `wakeups.jsonl` 有 **3 次投成**,目标全是 `[跟进]-…` 会话、全 `http=200 ok=true` —— 01:48:22 → `cbf76e13`@60346 / 02:56:25 → `ba7b24e8`@57555 / 04:06:52 → `19e6e204`@49765。④ **合成对照+自测**:`_target_busy` 对假 sid/3 条终结会话/名册 6 条(5 completed + 1 error)**全 False**,对本棒 `6a501f20`(真在跑)**True**;自测「投递忙判据…」(6 项,含「末条 busy=true 但 2 小时前 ⇒ idle」「库已终结 ⇒ 不判忙」「命名组零位置引用」)**PASS**、「目标会话在跑 ⇒ 必须延后」(含「伪造 idle ⇒ 必须不再延后」)**PASS**。\n🔑 **层 2「当下卡点在哪一层」=「无活跟进会话」**(09:37 现场读数,只读宿主库):`sessions.status` 取值域仍为 `completed 148 / working 2 / error 2 / archived 2`(**无「空闲」档**);`_live_sids()` = `['3f43ce71', '6a501f20']` —— 两条活会话都不是 `[跟进]-` 角色;跟进名册 `by_topic` 两条(`机制排查与修复 → 6ab1463e` completed / `会话协作自检 → 57f58ecf` completed);`follow_for_topic()` 三问全 `sid=\"\"`、`why=follow-not-live` ⇒ `_deliver_str()` 落 `skipped=\"no-follow-session\"`(`:1558`)。⇒ 件有(`S8=done` 待上报)、类别登记有,**但名册里全是终结态** ⇒ 「多起一条跟进会话」不能治。\n⚠️ **未过项(如实报)**:**「端到端放行」此刻无法现场复现** —— 现场不存在活跟进会话 ⇒ 前置条件不成立;3 次投成是 01:48–04:06 窗口的实证,要再验一次须先有一条活着的 `[跟进]-…` 会话(该前置属 S6 的持续态,⛔ 不属 S9 缺陷)。\n📄 产物(可核对):`交付物/S9-目标忙判据复核-20261002.md`(含四路证据、现场读数、复跑命令)。",
"artifact": "交付物/S9-目标忙判据复核-20261002.md",
"done_at": "2026-10-02 09:45",
"done_by": "[协作]-[机制排查与修复]-S9 投递目标忙判据复核与放行验证"
},
{
"id": "S5",
"title": "修:投递可达(验收 V2;原「主会话可响应」判据随口径作废 ⇒ 新判据=投递能到达跟进会话)",
"line": "机制排查与修复",
"status": "done",
"deps": [
"S1"
],
"evidence": "2026-10-01 21:0x 队列实读([跟进]-会话协作自检-队列上报):🔴 **受阻·需用户介入** —— 主会话 `f8a792ab` 会话日志 10,485,715 B(≈10 MiB 硬上限)、mtime 冻结在 2026-10-01 08:08(约 13 h 零增长)⇒ 界面停更、投给它的通知全落空 ⇒ 验收 V2 无从进行。已登记 `blocked.json`(键 S5,含解除条件)+ `NEED-USER.md` 喊话。解除条件:① 用户新建一条主会话(跟进会话建不了主会话);② 治本=修「宿主 ~10 MiB 上限把会话永久变哑且无告警」。⚠️ status 仍 `todo` —— 受阻属**排队状态**(`blocked.json`),按设计⛔ 不写进本图。|🔄 口径复核([跟进]-[会话协作自检]-点火验证投递 @2026-10-01 22:4x):goal.json `acceptance_state.V2` 已记「判据随口径变更作废 —— 投递不投主会话、改投跟进会话」⇒ 本节点原判据**已失效**;`blocked.json` 的 S5 登记沿用,真实缺口=**当前无活主会话**(宿主库最近 14 条里最后一条主会话 `f8a792ab` 已 `completed`)。新判据=投递能到达跟进会话,本轮实测**已能识别**活的跟进会话(22:39:27「延后投递:跟进会话 e2ccdea3 正在执行」)。|✅ **2026-10-02 10:3x–10:5x 修毕([协作]-[机制排查与修复]-S5 修主会话可响应)**。🔑 真因(实测,⛔ 非推断):**候选收窄发生在活会话过滤之前** —— `follow_for_topic()` 先取 `by_topic` **单条**(=最近活动那条)再交给 `_pick_live()` ⇒ **那一条死了/哑了 ≡ 整个类别投不出去**;且 `_deliver_str` 的**粗判**(`live_sids=[]`,而 `_pick_live` 按设计**回退 `c[0]`**)拿它的 sid 做**终局短路**(`target-deaf` + `need_user`,**还在取锁之前**就 return)⇒ 每轮都短路、**永远走不到**「拿锁后用活网关列表重算」那一步(两条判据自相矛盾,而矛盾结果是「不投」)。📊 实证:`_collabd.log` 里 `target-deaf` 共 **81 次**(末次 2026-10-01 21:07:53,队列 `M5=done` 被它压住);2026-10-02 10:33–10:41 实测粗判给 `57f58ecf`、精确给空(同一批候选,相反结论)。🔧 修法(两条同批):`_scan_follows()` 新增 `by_topic_all`(该类别候选**全表**,按 `last_activity_at` 倒序);`follow_for_topic()` 把全表**整体**交给 `_pick_live()`(`live` 为空 ⇒ 回退 `cands[0]` = 最近活动那条 ⇒ **与旧写法逐字等价**,向后兼容不破;⛔ 只在同类别内放开);粗判的「哑」判据改成**候选全都哑**才成立。🧪 钉死用例 `selftest.py::t_coarse_deaf_not_terminal`(5 项,真实判据链全程在位);**回退反证**:把 `cand, src = _lst, …` 换回 `[_lst[0]]` ⇒ **5 项全红、rc=1**(① 现 = target-deaf)。📈 回归:`selftest.py` **PASS 48 / FAIL 0(rc=0)**。🔁 已**重启常驻**(`--supervise` 长驻进程内存里是旧代码 —— 实测旧常驻仍报已被修掉的 `no-follow-session`、新起的 `--tick` 报正确的 `follow-not-live`)⇒ 新读数连续 `follow-not-live`(**真值**),⛔ 不再出 `target-deaf`。⚠️ **剩余缺口(属环境,⛔ 本棒未处置)**:现场 6 条 `[跟进]` **全 `completed`** ⇒ 投递/唤醒仍到不了人;「把跟进会话带回来」**只有自动化做得到**(见 `architecture.md §2.3.0e ③`)。"
},
{
"id": "S6",
"title": "建齐三类会话:唤醒 + 每个分工类别的协作 + 跟进(用户开工清单)",
"line": "会话协作自检",
"status": "done",
"deps": [
"S2",
"S3"
],
"evidence": "2026-10-02 00:4x 本棒实测 ⇒ **判定=三类会话已齐,未新建任何排期**(⛔ 不重复建)。取证两路(⛔ 不复用同一份读数得出结论):① 只读宿主库 `sqlite3 file:E:/ProgramData/.workbuddy/workbuddy.db?mode=ro`(⛔ 无任何文件级 cp/mv)—— `sessions` 按标题前缀过滤 + `automations` 全表看 name/status/schedule_type/rrule/next_run_at;② 独立读数=调技能 `collabd.parse_session_name()` 与 `board._role_of_title()` 逐条解析角色。逐项:**① 唤醒会话 ≥1 ✅** —— 会话 `[唤醒]-会话协作自检-脉冲`(945bb847 · completed @00:33:50)← 排期 `7fe0fe82` **ACTIVE / recurring / FREQ=HOURLY;INTERVAL=1**,next=2026-10-02 01:33:50(时钟在走:本条会话就是它 00:33:50 开出来的);角色 collabd=waker、board=waker(非空)。**② topics 每类别各 ≥1 条协作会话 ✅** —— `会话协作自检`:`[协作]-[会话协作自检]-S6 建齐三类会话`(96585643 · working · 即本条)+ `[协作]-会话协作自检-验收`(448cb6eb · completed);`机制排查与修复`:`[协作]-[机制排查与修复]-S8 常驻投递载体(跨 turn 存活)`(87407781 · completed)+ `[协作]-机制排查与修复-常驻投递容器`(3309bb55 · completed)。共 4 条,均 role=worker、`ok=True`(第 2 级带方括号、值取 `goal.json.topics` 原值,⛔ 非 `short`)。**③ 跟进会话 ≥1 ✅** —— 会话 `[跟进]-会话协作自检-队列上报`(b11c099d · completed @00:36:58)← 排期 `055ffdd1` **ACTIVE / recurring / FREQ=HOURLY;INTERVAL=1**,next=2026-10-02 01:36:58(同证:本条也是它 00:36:58 开出来的);角色 collabd=follow、board=follow。**④ 排期(时钟)**:唤醒=recurring、跟进=recurring,两条 ACTIVE 且 `next_run_at` 均在未来、且 00:33/00:36 各有实开会话为证 ⇒ 时钟真活;**协作按既定设计=「一棒一件」的一次性排期**,由第④类跟进会话的 recurring 时钟按需派生(口径 ⇒ `references/architecture.md §2.3.0d` + 跟进排期 prompt 首段「本会话的职责只有一件:创建协作会话」)⇒ 整链 唤醒(推)→跟进(派)→协作(干) 闭合,⛔ 无需另建周期排期(另建=与跟进会话职责重复且每小时多烧两条会话)。⚠️ 副产物(P2·不在本件验收项内、本轮**未改**):两条时钟排期的标题第 2 级**未带方括号**(`[唤醒]-会话协作自检-脉冲`、`[跟进]-会话协作自检-队列上报`)⇒ `parse_session_name().ok=False`(`topic=\"\"`)—— 但归属仍成立(`_topic_in_title()` 走**子串**,2026-10-01 已放宽;`collabd.py` 生成的 NEXT.md 第 695–697 行有明文),故**不静默漏管**;要不要统一成 `[唤醒]-[会话协作自检]-脉冲` 属机制层命名口径的取舍,留给 `机制排查与修复` 线裁。"
},
{
"id": "S7",
"title": "全量验收 V1–V7 并如实报告(未过的照实说)",
"line": "会话协作自检",
"status": "done",
"deps": [
"S4",
"S8",
"S9",
"S5",
"S6"
],
"evidence": "✅ 2026-10-02 2026-10-02 13:58 全量重测([协作]-[会话协作自检]-S7 全量验收 V1–V7,会话 053acd9e):**6 过 1 不过**。\n过:V1 常驻投递(pid 8024 活 + 心跳 12 s 新鲜)|V3 闸门齐全(head/pending/gate 三键 + NEXT.md 在)|V4 看板可服务(board.py --serve 8788 起得来,/healthz=200,快照 age=1.4 s)|V5 自测全绿(selftest PASS 48 / FAIL 0,与基线持平)|V6 follow 角色落地(名册 11 条 [跟进] 在册)|V7 三类会话在册(会话协作自检:唤醒 9/协作 3/跟进 10)。\n🔴 不过:V2 跟进会话这条链接得住 —— 目标解析得到(本类别 10 条 [跟进]),但**零条活着** ⇒ `_collabd.log` 连续 168 次 follow-not-live、今日投递成功 0 次 ⇒ 链路最后一段无收件人。⛔ 已按新口径判定(不再是「主会话能否被投」)。下一步=用排期把跟进会话带回来(常驻开不了会话)。\n⚠️ 点名不判 fail 的缺口:类别「机制排查与修复」唤醒会话在册 0 条。\n产物:goal.json acceptance_state 已全量覆盖写回;TO-MAIN.md 已上报;queue.json 已出队。",
"done_at": "2026-10-02 13:58",
"done_by": "[协作]-[会话协作自检]-S7 全量验收 V1–V7"
},
{
"id": "S10",
"title": "排期「哑火」真因取证与堆积盘点(含体检假红订正)",
"line": "机制排查与修复",
"status": "done",
"deps": [
"S1"
],
"evidence": "2026-10-02 14:16–14:2x 只读宿主库取证(`[协作]-[机制排查与修复]-承接队列` e0b9622a)。① 「到点未触发」≠哑火:一次性排期靠主机轮询补跑,运行记录标 `runKind=missed`,实测延迟 2.0/5.2/5.8/8.8/9.7 min(e0b9622a 计划 14:11 → 实跑 14:16:12,延迟 5.2 min)。② 体检 14:16 点名的 `717cd210`(计划 13:42)**实际跑了**:实跑 13:42:15、`runKind=scheduled`、产出会话 `053acd9e`(completed)⇒ 体检「哑火」判据**假红**(缺补跑容差)。③ 真哑火只 1 条:`87d55715`(计划 10:52,210 min 无任何 run),且同名任务已被 717cd210 覆盖完成 ⇒ 无损失。④ `ACCEPTED` 的判据:以 `automation_runs.metadata_json.conversationId` 反查 `sessions`,三条 run 全部命中 ⇒ `ACCEPTED` 代表「真跑了」;`started_at`/`finished_at` 本机恒空,⛔ 不可作证。⑤ 堆积:31 条在册一次性排期中 26 条已跑完仍 ACTIVE,`next_run_at=None` 意为「没有下一次」,主机不回收。⑥ 唤醒会话 0 条=正常态(看板口径「随需求确定时创建,还没建=正常态」,V7 亦不判 fail)⇒ 不建。📄 全文 `交付物/排期哑火真因与堆积治理-20261002.md`;脚本 `tmp/_misfire_probe_20261002.py`、`tmp/_misfire_probe2_20261002.py`。"
},
{
"id": "S11",
"title": "修:体检「哑火」判据(加补跑容差)",
"line": "机制排查与修复",
"status": "done",
"deps": [
"S10"
],
"evidence": "2026-10-02 17:4x–17:5x `[协作]-[机制排查与修复]-S11哑火判据`(ac46c525,机制层独占锁)。改 `collabd.py health()`:① 补跑容差硬编码 15min → 可配 `misfire_grace_min`(默认 **30**,覆盖 S10 实测最坏 9.7min);② **有 run 记录(含 `runKind=missed` 补跑)⇒ 改写为「延迟 N 分钟跑完」进 `notes`,⛔ 不再喊哑火**;③ 零 run 记录且超容差 ⇒ 才是哑火;④ `d[\"have\"]` 从 set 升级为 `{aid: min(updated_at)}`,延迟按「首次运行 − 计划」算 —— ⛔ 不可拿「距今」冒充「延迟」(首版这么写会把 30 小时前跑完的排期报成「延迟 1954 分钟」)。`notes` 落进 `体检报告.md` 新段「到点后跑完(补跑 · ⛔ 不是哑火)」。**六档验收 6/6 绿**(`tmp/_verify_misfire_fix.py` → `tmp/_verify_misfire_fix.out`):A 延迟补跑/B 真哑火/C 未到点/D 容差内/E busy 跳过/F 时刻读不出。**真实库端到端**(`collabd.py --once` 后读 `tmp/supervise-inbox/体检报告.md`):issues 由「满屏假红」收敛为 **2 条真哑火**(`87d55715`、`5e335e01`),另 29 条转成 notes 且**真实延迟 0~11 分钟**,与 S10 取证的 2.0~9.7 分钟吻合 ⇒ 判据判对了。备份 `collabd.py.bak-s11-misfire-20261002-1750`。⛔ 排期清理未含在本节点 → 见 S12。"
},
{
"id": "S12",
"title": "清理已跑完的一次性排期(29 条)",
"line": "机制排查与修复",
"status": "done",
"deps": [
"S11"
],
"evidence": "2026-10-02 22:0x–22:1x【本棒 · 走 SQL 直连通道,收尾完成】改前只读盘点 = **9 条**(⛔ prompt 说的 29 条不成立:那是 09-18 点前旧读数,那批多数已在 18:0x 那一棒被 deleted_at 软删,只剩这 9 条漏网)⇒ 判据 deleted_at IS NULL ∧ status='ACTIVE' ∧ schedule_type='once' ∧ next_run_at IS NULL。**9 条逐条单发 UPDATE(status→PAUSED,⛔不删行)+逐条回读四元组 (PAUSED,once,None,None) + rowcount=1 ⇒ 9/9 OK**;改后回读计数 **= 0** ✅。总行数 141→141(未删行);PRAGMA integrity_check = ok(改前也先跑基线)。**误伤回归**:recurring ACTIVE 3→3、待跑 once(next_run_at 非空)2→2 ⇒ 周期排期与 follow/waker 时钟一条没少。脚本内置 assert(盘点=列表、且拒改本会话排期 45d4e2d4)⇒ 不一致即停手。⛔ 全程未用 automation_update(P0-26 属主边界会假成功),只走 sqlite3 SQL + busy_timeout=10000,⛔ 未做任何文件级 cp/mv(避 WAL 三件套)。⛔ **状态=待核对(await-verify),不标 done**:产物只有「9→0」这一条读数,需下一次 collabd.py --once 的体检报告确认这 9 条不再出现在 notes 段才可转 done;⚠️ 本轮体检大概率仍 skipped(本自动化会话在跑 ⇒ health() busy 恒跳过,另有 300s 节流戳)。产物:机制排查与修复/S12_清理报告_20261002.md + 两个可复跑脚本(盘点/停用,幂等)。⛔ queue.json/queue.md/NEXT.md 是 collabd 派生产物,未手改。|✅ 2026-10-03 11:14–11:2x【S12 收尾核对 · [协作]-[机制排查与修复]-S12 收尾核对转 done】**状态判据本轮判不出来 ⇒ 维持 await-verify,⛔ 未标 done**。现跑 collabd.py --once(⛔ 未带 --supervise)rc=0,但 体检报告.md 的 mtime 仍是 2026-10-02 20:50:22 ⇒ **内容没刷新**;而磁盘上那份**早于**清理动作(20:50 < 22:0x),仍把 5e335e01 列在 issues 段 ⇒ 现在标 done 等于与磁盘报告直接矛盾。**跳过原因=busy(⛔ 不是 300s 节流)**:health() 第 727–729 行 if V[\"busy\"]: return out 直接早退;实测 V.busy=True,根因=fetch() 取到 1 条 working 会话 a80f300d「分析定时任务创建会话机制」(⛔ 本轮未处置它:不得对宿主在跑的会话做 load/改配置);busy_(d)=True、_all_sessions_idle()=False。节流不是原因:health_at 年龄 61,945 s(≈17.2h)远大于 health_every=300 ⇒ 若不 busy 本轮就会落盘。**计数判据现读(只读 mode=ro)**:目标 9 条逐条回读四元组 9/9 全为 (PAUSED,once,None,None) ⛔ 无回归;另核 87d55715(S7)同为 PAUSED。PRAGMA integrity_check=ok;总行数 148(清理时 141 ⇒ 增的 7 行是清理后新建的排期,⛔ 不是删了又补)。**关于「计数 0→4」不是回归**:逐条查 created_at,4 条全部晚于清理动作 —— b6ce8822(10-02 23:41:16)、99f803b0(23:47:12)、056c427a(23:47:31)、00bf17bd(10-03 11:06:03,即本轮目标检查排期) ⇒ 属清理后新建的排期,⛔ 不在 S12 清理范围,本轮不处置。**只读模拟(⛔ 旁证,未落盘)**:内存里置 V[\"busy\"]=False 跑 health() ⇒ issues=[],notes 3 条全是新排期(b6ce8822/99f803b0/056c427a),目标 9 条+87d55715 共 10 个 id 逐条「不在候选集」。结构性原因(读源码非推断):fetch() 第 466–467 行候选 SQL 为 where status='ACTIVE' ⇒ PAUSED 行在数据源层就进不了 d[\"due\"],不可能出现在 issues/notes。⚠️ **但模拟不算判据过**(判据原文要求「体检报告确认」,磁盘那份没刷新;否则即成自己判自己过)。**解锁只需一件事**:在无 working 会话窗口(V.busy=False ∧ _all_sessions_idle=True)跑一次 collabd.py --once 让报告真刷新。⛔ 本轮未改 collabd.py、未改任何排期状态、未碰 queue.json/queue.md/NEXT.md。台账 tasks.json 的 S12 本就已是 done(10-02 由 [主会话]-记录常驻起法 写入)⇒ 沿用 done(台账语义=活干完了吗;更严的读数门禁由本节点 await-verify 承担,两者分工不同非矛盾),本轮只更新 artifact 指针。产物:目标-本机协作-3e3182/S12_收尾核对_20261003.md\n\n|✅ 2026-10-03 12:06–12:15【S12 解死锁并转 done · [协作]-[机制排查与修复]-S12解体检刷新】**真读数两条判据均过,await-verify → done**。⛔ **先纠一个前提**:原判「任何会话来跑 --once 都会把自己算进 health() 的 busy 判定」**经实测不成立** —— 探针读数 self_working=True 且 d[\"working\"] 里只有 a80f300d、**没有自己** ⇒ fetch() 第 495–497 行的 SELF_SID 排除**本来就生效**;真正让 V.busy 为真的是**另一个真在跑的会话 a80f300d「分析定时任务创建会话机制」**(状态机日志末条 12:04:31 busy=true,3.9 分钟前,_target_busy=True)。节流⛔不是原因(health_at 年龄≈17.2h ≫ health_every=300)。**结构性病根**(读源码非推断):sessions.status 取值域⛔没有「空闲」档(1861–1882 行已实测)⇒ 只要有活会话 busy_() 恒真 ⇒ health() 每轮早退 ⇒ 报告永不刷新 ⇒ 本节点永远无法过。**修法(机制层最小改动)**:① health() 去掉 busy 早退,⛔ 判据五项一条未动(哑火/延迟/关键件缺失/锁摩擦/并发 IN_PROGRESS),改的只是执行时机;② 并发安全改由**原子替换写盘**(.tmp + os.replace)保证,⛔ 不靠「忙时别写」的时序运气;③ 报告新增「执行时机」一行,读者可判断是否空闲窗口。改前备份 collabd.py.bak-s12-health-20261003-120949。**判据一(计数)**:目标 9 条逐条回读四元组 9/9 全 (PAUSED,once,None,None) ✅;参照 87d55715(S7) 同为 PAUSED ✅;integrity_check=ok;周期 ACTIVE 3(=基线未误伤)。在册『once∧ACTIVE∧next_run_at IS NULL』现读数=4 ⇒ 逐条核 created_at **全部晚于清理动作**(b6ce8822 23:41:16 / 99f803b0 23:47:12 / 056c427a 23:47:31 / 8305da51 10-03 11:50:50)⇒ 属清理后新建排期,⛔ 不在 S12 范围,非回归。**判据二(状态)**:**体检报告真刷新** mtime 10-02 20:50:22 → **10-03 12:10:13**,结果『✅ 通过 / 无异常』;notes 3 条全是清理后新建排期的补跑(b6ce8822 延迟19分 / 99f803b0 延迟17分 / 056c427a 延迟24分,⛔ 不是哑火);**10 个 id(9 条+87d55715)逐个 grep 报告全文 = 全部不出现** ✅;旧红项 5e335e01(曾被列哑火)不再出现 ✅。⛔ 本轮全程只读 mode=ro,未改任何排期状态、未用 automation_update、未做文件级 cp/mv 碰库;⛔ 未碰 a80f300d(不 load/不改配置/不抢 writer_occupied);⛔ 未手改 queue.json/queue.md/NEXT.md。产物:目标-本机协作-3e3182/S12_体检刷新解锁_20261003.md",
"verify_note": "2026-10-03 12:15 转 done:两条判据均以**真读数**通过 —— 判据一 9/9 PAUSED + 无回归;判据二体检报告已真刷新(10-03 12:10:13)且 10 个 id 全部不在 issues/notes 段。⛔ 途中先纠前提:不是「自己算进 busy」, SELF_SID 排除本就生效,真因是 sessions.status ⛔无空闲档 ⇒ busy 恒真 ⇒ 报告永不刷新。"
}
],
"critical_path": [
"S4",
"S8",
"S9",
"S5",
"S6",
"S7",
"S10",
"S11",
"S12"
],
"notes": "🔴 与「需求台账四态」的分工:**本图管「该做什么、依赖是什么」**;台账 `tmp/supervise-inbox/tasks.json` 管「某件做到哪一步」。节点 done 必须有**可核对产物**(⛔ 不许只标 done)。\n\n🔴 2026-10-01 21:0x 排查发现([跟进]-会话协作自检-队列上报,真读数 · ⛔ 未修,登记待定夺):**「同线互斥」实际不生效**。`collabd.py:597-605` 的 `busy_lines` 取「`sessions.status='working'` 那条会话的 `cwd` **末段**」当线名,但本工作区所有会话的 `cwd` 末段都是 **`ai1net-dsh-server`(工作区名)**,与本图任何节点的 `line`(`机制排查与修复`/`会话协作自检`)都不相等 ⇒ `if p[\"line\"] not in busy_lines` 恒真。实测(只读 SQL,21:0x):`working` 会话 2 条 —— `fe212445`(本会话 `[跟进]-会话协作自检-队列上报`)、`3f43ce71`(`接续 · 会话机制合并包 · 任务4b-4d-6`,其日志同样已顶到 10,485,710 B ≈ 10 MiB 上限、mtime 16:55)—— 而 `queue.json` 的 `blocked_lines` 只报出 `[\"ai1net-dsh-server\"]`。⇒ 后果:① 同一条线可被同时派多件(本图 `rules.domain` 明令禁「整工作区域粗域」,此处等于退化回粗域);② `waste`(可派未派)判据把「本会话+那条接续会话在跑」误当成「有棒在跑」。",
"_目标来源": "用户 2026-10-01 原话:继续会话协作完成之前的需求,并把「协作机制的问题排查和修复」也当作目标一起完成"
}