- 变更规模:新增 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.6 KiB
S12 体检刷新解锁 · 机制层最小修法 + 读数转 done
- 执行会话:[协作]-[机制排查与修复]-S12解体检刷新(自动化
b15bbc86,12:05 一次性) - 执行时间:2026-10-03 12:06–12:15
- 域锁:先抢
ai1net-dsh-server/目标-本机协作-3e3182域锁;因落点属机制层(session-mechanism/scripts/collabd.py),按门禁另抢全局独占执行锁(两把均已释放) - 改的文件:
E:\ProgramData\.workbuddy\skills\session-mechanism\scripts\collabd.py(备份collabd.py.bak-s12-health-20261003-120949)
1. 一句话结论
✅ 真死锁已解:体检报告从 2026-10-02 20:50:22 刷新到 2026-10-03 12:10:13,结果 ✅ 通过;S12 两条判据均以真读数通过(非模拟)⇒ 任务图 S12 由 await-verify 转 done。
2. ⛔ 提示词的前提经实测不成立(这一条改变了修法)
原判:「任何会话来跑 --once,都会把自己算进 health() 的 busy 判定 ⇒ 永远早退」。
实测(探针读数,tmp/s12probe.py):
SELF_SID=e0fc9822-…(本会话)已被正确排除 ——d["self_working"] = True, 而d["working"]里只有a80f300d,没有自己。 ⇒ 排除逻辑在fetch()第 495–497 行本来就生效,「自己算进 busy」这个自锁不存在。- 真正让
V["busy"]为真的是另一个会话a80f300d「分析定时任务创建会话机制」, 且它真在执行:状态机日志末条12:04:31 busy=true(3.9 分钟前),_target_busy()=True。 - 节流不是原因:
health_at年龄 ≈ 17.2 h ≫health_every=300⇒ 若不 busy 本轮就会落盘。
⇒ 结论:「排除调用方自身」这条修法解不开问题(已经生效了);「等一个没会话的窗口」不可靠
(本工作区常驻协作会话长期活着,实测 4259 条状态机记录持续 busy=true)。必须改的是早退本身。
3. 结构性病根(读源码非推断)
sessions.status 取值域只有 working / completed / error / archived ——
⛔ 没有「空闲」这一档(collabd.py 1861–1882 行已实测记录:活着的会话跑完一轮、停在等下一轮时,
status 仍是 working)。
⇒ busy_(d) = bool(d["locks"]) or bool(d["working"]) 只要工作区里存在任意一条活会话就恒为真
⇒ health() 第 727–729 行 if V["busy"]: return out 每轮都早退
⇒ 体检报告永不刷新(实测跨 15.5 小时未动)
⇒ 依赖「体检报告确认」的验收判据(S12 第二条)永远无法过 ⇒ 这是真死锁,不是「等窗口」能解决的时序问题。
4. 修法(最小、⛔ 不动判据本身)
health()去掉busy早退:out = {"skipped": False, …},⛔ 删掉if V["busy"]: return out。- ⛔ 判据一条未动:哑火 / 延迟(补跑)/ 关键件缺失 / 锁摩擦 / 并发
IN_PROGRESS五项判据全部原样。 - 改的只是「什么时候执行体检」,⛔ 不是放宽判据。
- ⛔ 判据一条未动:哑火 / 延迟(补跑)/ 关键件缺失 / 锁摩擦 / 并发
- 并发安全改由原子替换写盘保证:临时文件
体检报告.md.tmp+os.replace(同目录、同卷 ⇒ 原子)。- 理由:去掉早退后体检会在别的会话运行时也写,⛔ 不能再靠「忙时别写」这种时序运气躲半截文件。
- 与本机既有铁律一致(多进程共用小状态文件优先「原子替换 + 回读核对」,⛔ 别先上文件锁)。
- 报告新增一行「执行时机」:
有会话在跑也照跑(2026-10-03 起;⛔ 不再因 busy 早退)/空闲—— 让读报告的人一眼看出这份是不是空闲窗口跑的,⛔ 不让判据失去可追溯性。
5. 两条判据的真读数(tmp/s12verify2.py / tmp/s12verify3.py,只读 mode=ro)
5.1 判据一(计数):deleted_at IS NULL ∧ ACTIVE ∧ once ∧ next_run_at IS NULL
现读数 = 4(期望 0)。逐条核 created_at ⇒ 4 条全部晚于清理动作(10-02 22:30)= 清理后新建,
⛔ 不在 S12 清理范围,本轮不处置:
b6ce8822created 10-02 23:41:16「v1原型重跑-③段」99f803b0created 10-02 23:47:12「[跟进]-队列上报(固定席位·不分类别)」056c427acreated 10-02 23:47:31「[协作]-③段-v1原型重跑」8305da51created 10-03 11:50:50「[检查]-[目标检查]-ai1net-dsh-server-第3棒」(本棒自己那条)
目标 9 条逐条回读四元组 = 9/9 全 (PAUSED, once, None, None) ✅
(3872e203 / 45d4e2d4 / 4e7b8401 / 5c77eedc / 5e335e01 / 73f138ce / 9a06e9b8 / 9ec39dac / a2982f2e)
参照 87d55715(S7)= (PAUSED, once, 1790909520000, None) ✅
PRAGMA integrity_check = ok;周期排期 ACTIVE 3(=基线,未误伤);总行数 150(141→150 增的是清理后新建排期,⛔ 不是删了又补)。
5.2 判据二(状态):体检报告(本轮真刷新,⛔ 非模拟)
- mtime = 2026-10-03 12:10:13(改前 2026-10-02 20:50:22)
- 结果行 =
## 结果:✅ 通过,issues = 无异常 - notes(补跑 · ⛔ 不是哑火)3 条:
b6ce8822延迟 19 分钟 /99f803b0延迟 17 分钟 /056c427a延迟 24 分钟 - 10 个 id(目标 9 条 +
87d55715)逐个 grep 报告全文 = 全部不出现 ✅ - 旧报告里那条红项
5e335e01(曾被列为哑火)在新报告中不再出现 ✅ - 结构性旁证(读源码):
fetch()候选 SQL 为where status='ACTIVE'⇒PAUSED行在数据源层就进不了d["due"]⇒ 结构上不可能出现在 issues/notes(与实测一致)
6. 本轮未做 / 未碰
- ⛔ 未改任何排期状态(全程只读
mode=ro),⛔ 未用automation_update - ⛔ 未碰
a80f300d(宿主在跑的会话:⛔ 不 load、不改配置、不抢writer_occupied) - ⛔ 未手改
queue.json/queue.md/NEXT.md(collabd 派生产物) - ⛔ 未做文件级
cp/mv碰workbuddy.db(WAL 三件套铁律) - 任务图
交付物/任务图-会话协作自检.json在域目录外,但本棒持有全局独占执行锁 ⇒ 合规改动,非硬闯
7. 判读这条修法时要注意的两点
- 不是「放宽判据」:判据(五项)一字未改,改的是执行时机。报告里显式写明执行时机,读者可判断可信度。
- 副作用(已知、可接受):体检现在会在有会话运行时写
体检报告.md。落盘走os.replace原子替换, ⛔ 不会出现半截文件;且health_every=300的节流仍在(health_at戳)⇒ 不会高频刷盘。