- 变更规模:新增 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/ 知识文件,按口径入库)
157 lines
35 KiB
JSON
157 lines
35 KiB
JSON
{
|
||
"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 原话:继续会话协作完成之前的需求,并把「协作机制的问题排查和修复」也当作目标一起完成"
|
||
} |