Files
workbuddy_skills/session-mechanism/references/99-速查清单.md
T

110 lines
9.6 KiB
Markdown
Raw Normal View History

# 速查清单 · 常见坑一句话版
> 📌 **来路**:原嵌在 `pitfalls.md` 的 **P0-2** 条里(占那条 79% 的篇幅 ⇒ 把一个条目撑成 11.6 KB)。
> **2026-10-04 拆出**:⛔ **内容逐字保留**(一字未改),只是搬家。
> **什么时候用**:已知坑名、想快速确认「这个形状是不是老坑」⇒ 先扫这份;
> 要**根因与实证** ⇒ 去 `pitfalls.md` 查对应编号。
> 📌 **注意**:这��条只给「现象 / 根因 / 修法」三行,⛔ 不含证据;
> **别拿它当结论** —— 当年 P0-2 里的旧结论就因为"只有弱相关性"被作废过。
## P0 ⛔ 子进程不加 `CREATE_NO_WINDOW` ⇒ 桌面**反复闪黑窗**(用户:"一会弹出来一会弹出来的,影响我操作电脑")
- **现象**:守护/常驻程序跑起来后,桌面每隔 10~30 秒**闪一个控制台窗口**。
- **根因**:程序里有 `subprocess.run(["netstat", "-ano"], stdout=PIPE, …)`(**认网关端口**用)——
**`netstat` 是控制台程序**,不带 `creationflags=0x08000000` 就会**新建一个控制台窗口**;而该函数**每轮都被调**(常驻程序 ~10 s/投递 ~30 s)⇒ 桌面反复闪。
- **修法**:✅ **所有** `subprocess` 调用一律带 `creationflags=0x08000000`(CREATE_NO_WINDOW)。
⛔ **只重定向 `stdout`/`stderr` 是没用的** —— 窗口照样会建。
- **验收**:桌面从"反复闪"变为"零窗口";**功能侧**用"能否照常认出网关端口"核对(日志/`wakeups.jsonl` 仍有记录)。
- 🔑 **推广**:任何**长期循环**里的外部命令调用,先过一遍"**它会不会开窗**"。
> 本档给**现象 / 根因 / 修法**。**架构层判据 ⇒ `references/architecture.md`**;原技能存档 ⇒ `references/collab-detail.md §6`。
## P1 ⛔ 用「会话后台任务」跑常驻 ⇒ 宿主"卡死"
- **现象**:用户说"卡住了/发消息不恢复";会话永不回 idle。
- **根因**:会话后台任务**每轮输出都会唤醒宿主会话**。
- **修法**:🔴 **首选=根本不要常驻** —— 由**宿主钩子按需唤起**一次性进程(见 **P0-3**)。
⛔ 旧建议"用 `run_in_background` 起常驻"是**错的**:那正是 **P0-2**(任务记在该会话名下 ⇒ 该会话卡输出)。
真需要常驻时,只能**会话之外**起(启动文件夹/独立窗口),并接受它**拿不到口令 ⇒ 只能落盘、不能投递**。
- ⚠️ **`cmd start` / `wmic process call create` / PowerShell `Start-Process` 常被安全策略拦** ⇒ ⛔ 别在"独立起进程"上耗时间。
## P2 ⛔ 自愈没有去抖 ⇒ 反复杀掉正在服务的好实例("自愈比故障更伤")
- **现象**:监控到的端口**反复通/断**;日志里"launching → 又被杀"循环。
- **根因**:启动器常有**幂等闸**("进程活着但端口不通 ⇒ 杀掉旧的再拉一份");若守护进程**每次探测失败就触发**,就变成**抖动**,把可能正在服务的好实例打死。
- **修法**:**连续 N 次(≥3)失败才动手** + **冷却(数百秒)** + 探测用**socket 连一下**(⛔ 别发真请求)。
## P3 ⛔ 粗粒度域锁 ⇒ 自造串行瓶颈,整轮白开
- **现象**:别的会话**抢锁失败 ⇒ 什么都不做就退出**(纯浪费的会话)。
- **根因**:把**整个工作区**声明成一个域 ⇒ 所有写操作串行。
- **修法**:**按节点涉及的文件/子目录声明域**;不重叠即可并行。**机制层才独占**。
## P4 ⛔ `fail-open` 掩盖字段名错误 ⇒ 视图静默为空
- **现象**:`rc=0`、日志无异常,但**某个区块一直是空的**。
- **根因**:异常被 `except: pass` 吞掉;查询写错列名(真实例:给 `sessions` 查了不存在的 `name` 列)。
- **修法**:① **必须核对输出非空**(⛔ 不能只看退出码)② 关键查询先 `pragma table_info(<表>)` 核对列名 ③ 定期 `diff` 视图一眼。
## P5 ⛔ 把"接续任务"当成果 ⇒ 被"空转链条"骗
- **现象**:看起来一直在推进("下一棒已派"),实际**总工期不动**。
- **根因**:**排期 ≠ 成果**。
- **修法**:**证据分级**(真成果/接续任务/刚开跑/哑火);"在跑的棒 N 个"是排期数,⛔ 不是成果数。
## P6 ⛔ 用"加自动化"回应一切需求 ⇒ 会话洪泛
- **现象**:一个需求挂上 7 个自动化,其中 5 个同用途。
- **修法**:**白名单+确认制**(只有"接续会话/派活"可免确认)+ **配额 ≤2**。建前自问:**非得开新会话吗?已有的能不能覆盖?**
## P7 ⚠️ 一次性自动化跑完 `status` 仍为 ACTIVE ⇒ 统计虚高
- **现象**:数"在挂的棒"= 6,实际只有 2 个真在等。
- **修法**:**一律用 `next_run_at > now` 过滤**;⛔ 不看 `status` 单独判断。清理僵尸旧件(已跑完的 once)。
## P8 ⛔ 诊断数据不落盘/不落库 ⇒ 只能靠"自述"
- **根因**:把"谁干完了、结论是什么"寄托在文件扫描/人报告上。
- **修法**:**读宿主已落的运行记录**(`automation_runs.thread_title` 等)⇒ **0 token、不轮询**。
- ⚠️ 但要**核对该列是否真有内容**(`IN_PROGRESS` 时标题可能为空 ⇒ 判"在跑"看 `status`)。
## P9 ⛔ 校验"可用"时只看"能跑起来"
- **现象**:结论写"八项判据起停各一遍全绿",但**端口此刻并不通**。
- **判据**:**"能跑起来" ≠ "可用"**。**可用 = 现在这一刻服务可达,且能被维持住**。
## P10 ⚠️ 长前台命令被沙箱杀,连带杀掉先前后台起的进程
- **现象**:命令无输出、`Exit -1`;随后发现后台进程也没了。
- **修法**:**单条前台命令控制在 ~90 秒内**;长活**拆短步**或**走异步**。
## P12 ⛔ 反复重启常驻程序 ⇒ **把主会话自己弄卡**(最容易被忽视的一条)
- **现象**:**每次进入"改常驻程序"的阶段,主会话就变卡、被反复打断**(用户原话:"为什么每次一改到这里就把自己的会话弄卡")。
- **根因(两条叠加 + 一条次因)**:
1. 🔴 用**会话后台任务**起常驻 ⇒ 该进程成为**本会话的附带物**;**每次 kill / launch 都产生一条 `failed` 任务通知** ⇒ **每次都打断会话**。实测:一个阶段里重启 **7~8 次** ⇒ **7~8 次打断**。
2. 🔴 **同一会话里做了 500+ 次工具调用** + 反复读写长文件 ⇒ 上下文极大 ⇒ 每轮推理显著变慢。
3. ⚠️ 注入钩子每轮往上下文加 ~1KB(应压在几百字符)。
- **修法**:
1. **攒批重启**:代码改动**攒到一次**再重启(≈3 处以上/或等功能自测全过)。⛔ **不要"改一行重启一次"**。
2. **优先热加载**:把**规则/配置/任务图/队列**做成**程序每轮读文件** ⇒ 改这些**根本不用重启**。
3. **长任务换会话**:一个会话**不要既"建系统"又"跑长验收"** ⇒ 到阈值就**交棒接续**(这正是本机制存在的意义)。
4. **注入瘦身**:`additionalContext` 压到几百字符。
5. 🔴 **改成"拉取模型"**(最根治):**程序只写队列/文件,⛔ 不调用会话、不通知、不起后台任务**;会话在"用户发话 / 棒收尾 / 需要时"三个时机**主动拉**队首 ⇒ **没有"推"就没有打断**。详见 `references/collab-detail.md` §1.3。
> 🔑 **一句话**:**"改代码 → 重启常驻 → 产生失败通知 → 打断自己"这个循环,是主会话变卡的机制性原因**,而不是外部故障。
> ⇒ **"拉"取代"推",是这个问题的根治解。**
- **现象**:`SyntaxError: unterminated string literal`。
- **根因**:在双引号字符串里写了 ASCII 双引号。
- **修法**:文案里的引号一律用 **`「」`**;改完 **`ast.parse` 校验**(比 `py_compile` 报错更清楚)。
## P13 ⛔ 常驻"活不长" ∧ 钩子指向无唤醒码的旧副本 ⇒ 机制**静默停摆**(最阴的一条:没人会发现)
- **现象**:唤醒回路代码写完、也实测到 1 次真投递,但此后**再也不投**。查下去:常驻进程**已消失**、单例端口 `Connection refused`、日志停在某一刻;而"唯一会自动跑"的钩子那条路,跑的是**另一份旧的精简副本**(没有队列闸门、没有唤醒码)⇒ 整个机械层**静默停摆**,且**没有任何告警**——因为告警也是那个程序发的。
- **根因**:① **从会话里起的常驻会随其宿主会话结束被回收**(实测:`13:09` 起的 pid,`13:12` 之后再无一轮);② **同目录另存过一份旧副本且钩子指向它** ⇒ "代码在 A、钩子在 B" ⇒ **接了线等于没接**;③ 认口取"`uptime` 最大"的口,而**那个口可能没有活会话** ⇒ 有活会话也照样落 `no-live-session` 跳过。
- **修法**:
1. 🔴 **"能自动跑起来"的路只有一条 = 钩子**(每个会话收尾跑一轮、零 token、不占会话)⇒ **把机制的关键环节挂在这条路上**,⛔ 别押在"常驻一直活着"上。
2. **钩子必须指向最新那份**(含队列/唤醒);**同目录⛔ 不要留旧副本**,或让旧副本显式**转调**新版(fail-open + 静默 + 超时,⛔ 不改自己原有行为)。
3. **认口=「第一个带活会话的口」**,⛔ 不是 `uptime` 最大者(实测两者常不同)—— 同 cwd 多实例时,这条正是**投得出去 / 投不出去**的分水岭。
4. **判"接线成立"看产物**:每轮覆写的视图/队列/状态文件 **mtime 是否跟着"会话收尾"推进** —— 比读代码可靠得多(本节即靠这一条定位的)。