- 变更规模:新增 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/ 知识文件,按口径入库)
18 KiB
唤醒机制 · 现行工作机制说明 +「能否正常运行」判定
日期:2026-10-02(07:0x 取证) 触发:用户两条指令 —— ①「把唤醒框图的线 连接到 协作程序上,然后分析 唤醒 后台任务机制是否可以正常运行」②「详细说明当前唤醒的具体工作机制」 取数方式:只读宿主机库(
sqlite3 file:...?mode=ro)+ 工作区台账/日志文件。⛔ 未写库、⛔ 未拷库文件、⛔ 未起任何进程。
§0 一句话结论
唤醒机制「能准时醒、能走完流程、但送不出去」——而且送不出去的时候,它一声不响。
| 环节 | 判定 | 一句话 |
|---|---|---|
| ① 时钟(排期) | ✅ 正常 | 宿主每小时真触发、真开一条新会话,连续 8 轮有记录 |
| ② 执行(会话内三步) | ✅ 正常(有 1 次例外) | 让位检查通过、三条判据都取到真读数、跑了一次 --tick;⚠️ 近期 8 跳里 1 跳被中断 |
③ 送达(--tick 投递) |
❌ 结构性不达 | 投了,但被自己的去抖 same-item 挡掉;且不写日志、不喊用户 ⇒ 静默空转 |
⇒ 「是否可以正常运行」的答案是:排期与执行这两段可以,整条链路不行 —— 卡在最后一段,而且是无声地卡。
§1 现行机制 · 六个环节(每环都附一手证据)
环节 1 · 时钟 = 宿主的一条每小时排期(⛔ 不是常驻进程、⛔ 不是网关注册的定时任务)
| 项 | 实测值 |
|---|---|
| 排期名 | [唤醒]-会话协作自检-脉冲 |
| id | 7fe0fe82… |
| 类型 | recurring / FREQ=HOURLY;INTERVAL=1 / status=ACTIVE |
| 下次到点 | automations.next_run_at = 10-02 07:10:34(⇒ 这一项读得到,见 §4 的看板假话②) |
| 工作目录 | E:/ProgramData/AIProject/ai1net-dsh-server(正斜杠形态) |
复跑:sqlite3 file:E:/ProgramData/.workbuddy/workbuddy.db?mode=ro "select id,name,rrule,next_run_at,status from automations where name like '%唤醒%'"
⚠️ 同线的两条旧排期仍是
PAUSED:[协作]-唤醒机制-唤醒轮A/B(caca9a89/16bec5ce)⇒ 在册但不生效,⛔ 别把它们当成"还有两条钟"。
环节 2 · 触发 = 每跳开一条新会话(⛔ 不是"复用同一条")
automation_runs 逐跳有记录;sessions 里同标题会话每小时新增一条、id 各不相同:
| 建(开新会话) | 完 | 会话 id |
|---|---|---|
| 10-02 06:08:00 | 06:10:34 | a5af050c |
| 10-02 05:06:24 | 05:07:34 | 01d55d84 |
| 10-02 03:55:52 | 05:09:09 | f9ad6a46 |
| 10-02 02:47:55 | 02:55:41 | cf2f3fe5 |
| 10-02 01:34:04 | 01:47:23 | 1495f547 |
| 10-02 00:25:36 | 00:33:50 | 945bb847 |
⚠️ 点火时刻是漂的(01:34 / 02:47 / 03:55 / 05:06 / 06:08)—— 不是固定 :xx,迟到量 4~47 分钟。
⇒ 所以任何"唤醒准点就会推"的假设都不成立;靠它报时不可行。
环节 3 · 会话内三步(这是"机制本体")
从最近一跳(06:08,会话 a5af050c)自己的自述逐字取:
- 第 0 步 · 让位检查 —— 「本工作区除本会话外没有正在执行的
[唤醒]会话(两道判据都指向本会话)⇒ 本轮照常执行」。 - 取三条判据(真读数):
- ① 除本会话外正在执行的会话 = 0;
- ②
queue.json有待处理项 ——head=S9/pending=1/doing空 /gate=free; - ③
goal.json.acceptance_state仍有非 pass 项 —— 只剩V2-主会话可响应=待重验。
- 跑一次
collabd.py --tick(="推一次投递轮")。
环节 4 · 投递 = 「唤醒只负责跑一次 tick,推给谁由协作程序决定」
- 唤醒会话不直接找跟进会话;投递目标
target=follow由协作程序判。 - 最近一次成功投递:10-02 04:06:52 →
19e6e204([跟进]会话),http=200,hash512943bd519bbf1d。 - ⚠️ 此后再无成功投递(台账
wakeups.jsonl最后一条就是它)。
环节 5 · 三条兜底出口(推不成时写文件)
| 文件 | 什么时候写 | 现状 |
|---|---|---|
NEED-USER.md |
需用户介入 | mtime 10-02 05:01(停在 05:01 那一版) |
READY.md |
「可派未派(浪费)」 | mtime 10-02 06:54,内容=派 S9 |
STALL.md |
有会话在跑但长时间无成果 | 06:06:31 生成 |
🔴 但是:--tick 被去抖挡掉时,这三份一份都不写(下详 §3-①)。
环节 6 · 它与另外两台"钟"的分工
| 角色 | 谁在驱动 | 出口 |
|---|---|---|
| 唤醒(本文件) | 宿主排期,每小时,开新会话 | 跑一次 --tick |
| 跟进 | 宿主排期,每小时,开新会话(055ffdd1,next 10-02 07:34:23) |
收队列上报、判"该不该派活" |
| 钩子(补充) | 宿主在 PreToolUse ^Bash$ / UserPromptSubmit 时机起短命子进程 |
事件驱动地补一次 --tick |
⇒ 三者的唯一"推"出口都是协作程序;⛔ 唤醒不叫主会话(主会话只由用户触发)。
§2 「能否正常运行」的判定 —— 三段分开答
✅ 段一:能点火
automation_runs里连续 8 跳有记录,result_success=1;会话按预期被创建。- ⚠️ 例外 1 次:05:06 那一跳被中断 —— 该跳的
thread_title原文是Automation prompt interrupted: unknown_i…⇒ 那一小时的唤醒没跑完。 ⇒ 结论:"每小时必醒"成立,但"每小时必跑完"不成立(近期 8 跳 1 次中断)。
✅ 段二:能执行(会话内三步照跑)
06:08 那跳把三步全做完了,并且自己写出了体检:闸门在册、各闸门日志末行是 06:08、看板进程在跑。⇒ 会话内部的流程是好的。
❌ 段三:送不出去(这是"整条链路不行"的地方)
真因不是"没人投",而是投了、被自己的去抖静默挡掉。三重叠加:
① 去抖 same-item 无时限(直接原因)
- 机制自己的状态文件
tmp/supervise-inbox/collabd-state.json里:"wake_info": { "skipped": "same-item" }(mtime 07:01,最新一跳)。 - 含义:队首这条内容的 hash 与上一次已投递的完全相同 ⇒ 判定"重复"、跳过。
- 🔴 它没有时限:只要内容不变就永远不再投。实测后果 —— 队首从 04:06:52 起约 2 小时没出过件。
- 🔴 而且它静默:生产日志
_collabd.log的最后一行停在 05:01:23,NEED-USER.md也停在 05:01 ⇒ 被挡掉的每一轮,日志、喊话都不写(="卡住但一声不响")。
② target-busy 判据(已登记、未修)
_session_status()只回sessions.status小写串;"活"与"忙"是同一根字符串working⇒ 有活跟进会话时也会被判"忙"而延后(S9 待修)。- 旁证:日志里 04:06 那两行
忙判据读不到(19e6e204)⇒ 按**没在跑**处理—— 它是靠"读不到"才绕过去投成的。
③ 常驻投递缺席(放大静默窗口)
- 全机进程扫描:没有任何
collabd.py --supervise(只剩看板board.py --serve 8788)。 - ⇒ 本该由常驻投递承担的高频投递没了,唯一投递源只剩"每小时唤醒会话 + 宿主钩子"; 一旦上面 ①/② 命中,没有人补投,静默窗口就是一小时起。
④ 顺带发现:看板服务此刻也已停
- 06:08 那跳的自述里还写着「看板进程
board.py --serve 8788在跑」; 07:0x 实测:netstat里 8788 无监听,tasklist里 一个python.exe都没有(curl回HTTP 000)。 - ⇒ 投递与看板"两个常驻"现在都不在 —— 它们起在会话里 ⇒ 会话一结束就被带走(=S8 记过的那个根因)。
附带旁证(同样指向"读数曾经过期"): · 生产
_collabd.log尾部还留着 10-01 14:09–15:07 一串fatal 'gbk' codec can't encode '\u26d4'—— 即"编码未声明 ⇒ GBK ⇒ 整轮 fatal"那类故障的历史痕迹。 · 弃用的wake-pulse.sh仍在盘上(.workbuddy/collab/)。
§3 本轮"顺带"修掉的(都是同一个病:看板在说假话)
| # | 位置 | 原来写什么 | 实测是什么 |
|---|---|---|---|
| ① | board_ext.py 唤醒格 tip |
「唤醒已从『排期开新会话』改成『脉冲复用同一条会话』」 | 反的 —— 排期每小时开一条新会话 |
| ② | board_ext.py 唤醒格 tip |
「『下次几点到点』⛔ 读不到 —— 脉冲是会话自己起的后台任务」 | 两半都错 —— 是宿主排期;next_run_at 读得到(10-02 07:10:34) |
| ③ | board_ext.py WAKEUP_KINDS |
用 上报·唤醒/监督程序·心跳 当"唤醒痕迹" |
两类 kind 都已退役(最后一条 09-30T10:44:43)⇒ 照它读会得出「两天没响过」的假结论;现役真读数在会话表 |
| ④ | board_ext.py 唤醒格数据 |
死字段 "edge": "唤醒"(注释"箭头指主会话") |
零消费方(board.html 已改成固定文案)⇒ "在册 ≠ 生效"的坑,已删 |
| ⑤ | board_ext.py(技能侧模板) |
改名替换过头留下的乱码 4 处((原来叫"唤醒")、("唤醒" not in nm) and ("唤醒" not in nm) 等) |
已逐处订正 |
| ⑥ | references/architecture.md |
「常驻投递进程…它同时提供唤醒时钟」/「唤醒不是一个定时自动化…不引入任何周期性排期」 | 🔴 这两句是口径(定案),不是错的 —— 我 07:0x 一度把它们当"与现状相反"给废掉了,同日已回改(见 §7) |
§7 🔴 同日自我更正:我把「口径」当「实测」废掉了(2026-10-02 07:2x)
起因:用户问「为什么唤醒会话还是定时任务呢,不应该是一个会话靠自己的后台任务定时唤醒吗」⇒ 回查口径原文,发现我上一轮的"订正"订错了。
定案口径(⛔ 一直没变,多处同款):
| 出处 | 原文 |
|---|---|
architecture.md §0 |
⛔ 已废弃:「用自动任务当闹钟」 —— 2026-10-01 用户明确废弃(原话:「定时任务的方案已经废弃了」) |
architecture.md §4.0/§4.1 |
两节整节作废 ⇒ 「时钟由常驻投递提供」 |
architecture.md §5-1/deploy.md §5/collabd.py 用法头 |
常驻投递(collabd.py --supervise)= 投递本体 + 唤醒时钟;⛔ 不用自动任务、⛔ 不靠宿主排期 |
architecture.md §8 |
「—— 它同时就是唤醒时钟」 |
⇒ 正确记法:口径不动;现状是偏差 ——「唤醒时钟缺位,由一条已作废的旧路代偿」。 我错在:把"现状与口径不符"读成了"口径已被推翻",于是把两处口径当实测给订正掉了。⇒ 已回改(原文保留,另加「实测偏差」行,并写明⛔ 不许据此改口径)。
现场读数(07:10 那一跳,正在跑):[唤醒] 会话 477ab6ca 建 07:10:53、status=working;台账最后一条仍停在 04:06:52;去抖仍 same-item ⇒ 每小时开一条新会话、每次都投不出去,此刻就摆在眼前。
⚠️ 另有一条同样被误会的禁区:用户问的「一个会话靠自己的后台任务定时唤醒」—— 这条路是 2026-10-01 明令禁止的(architecture.md §4.0 末:「⛔ 禁止(原「第四种」,仍然禁):把长跑服务放进会话后台任务」,理由=每轮输出唤醒宿主 ⇒ 会话永不空闲 ⇒ 用户看到"卡死",历史复现 6 次)。⇒ 它不是备选方案,⛔ 别照它照做。
🔴 订正③值得单独记一笔:它意味着看板原样会告诉用户"唤醒两天没响过",而事实是它每小时都在响 —— 又一处「在册 ≠ 生效」的镜像(这回是"在册的是旧的")。
§8 相关引用的全量清扫(2026-10-02 07:2x · 对应「修复错误概念时要排查相关引用」)
缘起:用户原话「修复错误概念的时候 要排查相关引用 确保更新完全」—— 同日的「口径 vs 实测」自我更正(§7)若不把引用一起改,下一个人读到的仍是旧概念。
做法:全量枚举 ⇒ 逐处只加标注、⛔ 不删原字(保留取证)⇒ 复核(判定窗口=该行或其后 3 行内是否带标注)。
| 落点 | 处数 | 说明 |
|---|---|---|
architecture.md |
10 | §3 投递/派活行、§5-1 时钟行、§4 ④时钟行、§7.3 标题、排期名前缀 + 本轮 §4.1 的标题/红字「⇒ 🔴 结论」/「旧『代价』已被 ④ 补上」段/红字「⇒ 🔴 立场」 |
collab-detail.md |
8 | :95/:235/:404/:425/:512/:513/:778/:891 |
collab.md / SKILL.md |
3 | 措辞「唤醒轮排期名」⇒「自动唤醒任务的排期名」(旧措辞会被读成"唤醒本该用排期") |
board_ext.py |
3 | 死函数 _triggers_gw 的旧名「心跳」;运行期文案「在等脉冲到点」⇒「现状由排期代偿到点叫醒;⚠️ 定案是常驻投递直接叫醒」 |
MEMORY-全文-20261001.md |
2 条 | 整条改写(见下) |
MEMORY.md |
1 | 补入「🔴🔴 唤醒时钟=这个常驻进程本身」 |
🔴 最重的一处(差一点漏掉):状态层 ⇒ 全文 的指针目标 MEMORY-全文-20261001.md:35,原文整条与定案相反 ——
「监管+投递都=钩子事件驱动」/「⛔ 不常驻(常驻天生拿不到口令)」/「两个拨钟方:① 钩子 ② 自动任务」/「⚠️ 常驻走不通」。
⇒ 已整条改写为定案口径 + 附「2026-10-02 订正记录(三项全错)」;同文件 :34 两处同步订正。
⇒ 教训(新):查残留 ⛔ 不能只查"正文" —— 指针目标(⇒ 全文/⇒ 细则)必须一起查,它们在读者眼里就是正文。
复核结论("确保更新完全"的证据):技能库 md 侧疑似残留 30 → 13;13 条逐条判过 = 全部是正确口径/历史引用/用户原话(如 :546/:548「常驻投递=唤醒时钟」正是定案、pitfalls.md:363「不建当闹钟的自动任务」正是修法)⇒ 无遗留。
已退役技能名(workbuddy-session-forensics/multi-session-collab):活引用零(命中全在 归档/技能-退役-20261001/、带日期接续包、归档/配置与备份/)。
回归:install.py --manifest = 35 份 / 语法失败 0;--verify = 全绿(PASS 45 / FAIL 0);两份 board_ext.py md5 一致(技能侧为源、cp 到工作区实跑份)。
改前备份:tmp/bak-wake-sweep-20261002/(4 份 md 改前原样)。
⚠️ 顺带发现(既存,非本轮引入):MEMORY.md 8039 字符(自设上限 7,650)/MEMORY-全文-20261001.md 10,453(上限 7,800)—— 两者改前即已超,建议专做一次压缩(涉及信息取舍,⛔ 不顺手做)。
§4 缺口 / 待决(⛔ 我没有擅自改)
| 级别 | 缺口 | 影响 | 建议 |
|---|---|---|---|
| P1 | 去抖 same-item 无时限 |
队首一旦投出过一次、内容不变 ⇒ 永不再投(实测停摆 ≈2h) | 并入 S9(同方向:投递判据);至少加时限/退避 |
| P1 | 去抖跳过时不写日志、不写 NEED-USER.md |
卡住但一声不响 ⇒ 只能靠人凑巧看见 | 静默跳过也必须留痕(已有 skipped.jsonl 那套写法可循) |
| P1 | 常驻投递缺席(--supervise 全机无进程) |
投递没有主时钟 ⇒ 静默窗口一小时起 | S8(载体)待决,⛔ 受"⛔ 不在会话里挂常驻后台任务"约束 |
| P2 | 看板服务同样已停(8788 无监听、无 python.exe) |
用户看不到看板;且它是"读数打架"的又一例(06:08 报在跑、07:0x 已无) | 与 S8 同根因(起在会话里 ⇒ 会话结束被带走),一并治 |
| P2 | target-busy 判据把"活"与"忙"判成同一个值 |
有活跟进会话也会被判忙 | S9 |
| P2 | 唤醒格还没读排期表的 next_run_at |
看板给不出"下次几点醒" | 已登记,未实现(本轮只订正文案) |
| P2 | 同线的 唤醒轮A/B 两条 PAUSED 排期仍在册 |
容易被误读成"还有两条钟" | 收敛清单 |
| P3 | _triggers_gw() 无调用方(历史件) |
改它不会有任何效果 | 已加"无调用方"护栏注释 |
§5 我没做的(如实)
- ⛔ 未起常驻投递、⛔ 未派新会话、⛔ 未改 S9/S8/S5 任何一件的代码(都属机制层,且本轮执行锁由本会话持有,应由下一棒在抢到锁后做)。
- ⛔ 未删任何排期、未动线上任何文件(除本文档与 §3 列的订正处+看板技能文件)。
- ⛔ 未对
_collabd.log的历史 fatal 做清理(那是取证痕迹)。
§6 复跑(全部只读)
PY=E:/ProgramData/.workbuddy/binaries/python/versions/3.13.12/python.exe
# ① 排期与运行记录(⛔ 别 SELECT *:automation_runs 有大字段会淹输出)
$PY tmp/_q_wake3.py
# ② 唤醒/跟进是不是各开新会话
$PY tmp/_q_wake4.py
# ③ 唤醒最近一跳的自述(机制自己写的账)
$PY tmp/_q_wake5.py
# ④ 去抖现状
$PY -c "import json,io;print(json.load(io.open('tmp/supervise-inbox/collabd-state.json',encoding='utf-8'))['wake_info'])"
# ⑤ 生产日志尾
tail -6 .workbuddy/collab/logs/_collabd.log