fix(adapter): claude-code 补忙碌标志防止静默误判空闲 - #1317
Conversation
|
感谢这个 PR,问题定位得很准:claude-code 工作时 不过实测下来, 主要问题:CLI 自己把这句话打进正文,就会被判成「忙」
我用真实 claude(v2.1.263,独立 tmux socket,没碰线上会话)复现了一次: 会话已经真正结束(footer 没有中断段、
短 transcript 时命中行可能落在探测窗之外,但只要 transcript 长到把最后一条消息顶到输入框上方(日常几乎必然),命中行就落在窗内——上面这份就是(命中行在 L41/L43,窗口从 L35 起)。 用真 同一个探针、同一份输入,master 绿 / PR 红 ⇒ 这条是本 PR 引入的,不是既有行为。 后果比误翻绿更硬一些:
建议改法:锚 footer 的结构,而不是锚裸短语同仓 codex/traex 的既有做法就是「忙碌标签 + 上下文」联合锚定( 我验过的一个候选(行首模式字形 + const CLAUDE_BUSY_FOOTER_RE =
/^\s*(?:[⏵⏸]+\s.*\bon\b|.*next try in).*·\s*esc to interrupt\b/m;实测:真 footer 7/7 全中(含上面 5 种模式 + 重试 footer + 带
其它
以上是自动评审的初步意见,可能有我没覆盖到的场景;最终以维护者审阅为准。方向和问题定位都很好,收紧锚点之后我认为这就是一个干净的修复 🙏 |
|
补充一条实测(复审阶段追加,加强而非推翻上面的结论):窄视口下 footer 会被截断,这让两条 pattern 出现一个不对称——对 在 61 宽(本机 fleet 里真实存在的宽度)实测同一个会话、同一份 transcript:
也就是说窄视口下 补充确认的触发面(比我原文说的更广):
截断阈值实测:80 宽及以上中断段完整可见(80/93/98/160 全部 OK),61 宽开始截断 ⟹ 主流宽度不受影响,这一条只影响 ~80 宽以下的边缘会话,不改变上面的主结论和建议改法。 |
claude-code 适配器此前只有 readyPattern(/❯/),而 Claude 工作时 ❯ 常驻, idle 判定只剩 2s 静默这一条负证据:长思考或网关延迟造成的一次 ≥2s PTY 停顿就会把工作中的会话错翻成 idle,且没有任何拉回手段,卡片钉死 在绿色。 补上忙碌正证据双保险(review 后收紧为结构锚点): - busyPattern:deferPromptReadyWhileBusy 翻绿前先查屏幕,工作态 footer 的「· esc to interrupt ·」段否决错翻 - idleToBusyPattern:已错翻的绿卡在下一帧拉回工作中 锚点锚 footer 结构而非裸短语:transcript 与 footer 同屏,probe 区扫 末 max(12, ⌈行数/3⌉) 行,正文出现裸短语(讨论快捷键、贴 diff、grep 源码)会把已空闲的会话钉死在「工作中」(probe 重试无上限)。改为 行首模式字形(⏵⏵/⏸,不枚举 mode 名——manual/bypass 是运行时拼的) 或重试段「next try」+ `·` 分隔联合锚定:真 footer 7/7 命中,散文 8/8 不误报(双向实测);窄视口(<80 列)footer 截断时安全降级回原 行为,不误报。COMPLETION_RE 字形问题(✳ vs ✻)刻意未动:该分支无 spinner 守卫,单独修会新造提前翻绿 bug。 影响面:def 由 claude-code/seed/relay 共享(同源 TUI footer), 不影响其它 CLI 适配器。 验证:bunx vitest run test/cli-adapters.test.ts 444 passed(含新增 回归用例:7 种真 footer 命中、6 种散文负例、多行 probe 区正反例); bun run build 通过;已 rebase 到 upstream/master 61dadb0。
f6abeec to
7f3dfc8
Compare
|
按 review 意见收紧了锚点,感谢非常扎实的实测(尤其是 prose 自引用钉死空闲态那条,和窄视口截断的不对称分析,都是我没覆盖到的场景)。 改动: const CLAUDE_BUSY_FOOTER_RE = /^\s*(?:[⏵⏸]+\s.*\bon\b|.*next try).*·\s*esc to interrupt\b/m;
窄视口截断的结论也认同:结构锚点是「拿不到正证据、退回原行为」的安全降级,不是新增缺陷,这条留给 footer 方案的固有局限。 COMPLETION_RE 的字形问题维持不动,理由同你确认的现状。 |
|
重新 review 了新 head ✅ 阻断已解除原阻断(裸短语被 CLI 自己的 transcript 命中 ⟹ probe 无上限重试 ⟹ 钉死「工作中」)已修好,而且是用结构锚点修的。我重新造了一次复现场景验证(真 claude v2.1.263 + 独立 tmux socket,未碰线上会话):把 transcript 填长,让含裸短语的正文落到输入框上方(
阴性对照转红说明这个探针不是哑弹,确实打在这条改动上。真实屏幕双向也都对:抓到的真忙屏( 测试也补得很到位——5 种权限模式 + 反变异 9 枪 8 红:删
两条非阻断(都不影响合入,作者可自行取舍)N1(建议改,一行): 改回 顺带说明:你测试里那条 N2(可选,测试覆盖): 即「正文里带前缀引用一整行 footer」(贴日志、贴 review 引用、 小结阻断解除,我这边认为可以合入;N1/N2 是加固建议,不阻断。方向和落地都很干净,感谢按建议收紧锚点 🙏 以上仍是自动评审意见,最终以维护者审阅为准。 |
|
更正我上一条评论里的 N1 建议——我给的收紧方案不完整,复审时被指出,我实测确认了(结论不变:N1 仍是非阻断的加固项,但改法要换)。 我原话说「改回 第三条本身包含 如果要收紧,建议按 retry footer 的真实结构来锚。二进制里那段是逐字拼的: 也就是「行首 const CLAUDE_BUSY_FOOTER_RE =
/^\s*(?:[⏵⏸]+\s.*\bon\b|·\s*next try in\b.*·\s*attempt\b).*·\s*esc to interrupt\b/m;实测 8/8 全对:真 retry footer、真 footer(bypass/manual 等 5 模式)全部命中;上面三条散文 + 我另造的 重申严重度没变,这条不阻断合入:主路径( 抱歉上一条给了个只修 2/3 的方案,浪费你时间 🙏 其余结论(阻断已解除、可以合入、N2 补 |
|
再更正一次我自己的 N1 改法——我上一条给的锚点会漏掉真实的 retry footer,这次是往「不安全」的方向错了,必须说清。结论仍不变:N1 是非阻断加固项。 我上一条建议锚「行首 // vt = <Box aria-hidden width={2}><Text color="warning">{PH}</Text></Box> ← 2 列 glyph
// Ot = truncate(waitBanner, Do) ← "Waiting for API response"
// An = ` · next try in ${ze} · attempt ${nt.attempt} · esc to interrupt` ← 拼在后面
我那个「行首 真正兼顾两者的写法是只要求 const CLAUDE_BUSY_FOOTER_RE =
/^\s*(?:[⏵⏸]+\s.*\bon\b.*|.*·\s*next try in\b[^\r\n]*·\s*attempt\b[^\r\n]*)·\s*esc to interrupt\b/m;实测 10/10 全对:
你现有的整套测试在这个改法下仍然 444/444 全绿(含你那条 再次重申严重度:N1 不阻断合入。主路径( 两次给了不完整的方案,浪费时间,抱歉 🙏 其余结论(阻断已解除、可以合入、N2 补 |
deepcoldy
left a comment
There was a problem hiding this comment.
首审 + 复审双人独立验证:阻断已解除。
结构锚点修好了「裸短语被 CLI 自己的 transcript 命中 ⟹ idle probe 无上限重试 ⟹ 卡片钉死工作中且输出压住不发」这条回归。重造复现场景验证:本 head PASS,换回旧裸短语版阴性对照 FAIL(证探针非哑弹)。
反变异 9 枪 8 红(唯一绿枪已插桩归类为覆盖缺口而非死代码),合并态 bun run build 绿、444/444、CI 9/9 绿,merge-tree 预演 rc=0 且实际产出树 == 预测树。
N1(retry 支路可再收紧一档,两人各自实测且作者原测试仍全绿 ⟹ 纯收紧)与 N2(补 ^ 负例)是加固建议,不阻断,已在评论里留档。
改了什么
claude-code 适配器此前只有
readyPattern: /❯/,而 Claude Code 工作时输入框 ❯ 常驻——idle 判定只剩「2s 静默」这一条负证据。长思考或三方网关延迟造成的一次 ≥2s PTY 停顿就会把工作中的会话错翻成 idle,且没有任何拉回手段(无busyPattern⇒deferPromptReadyWhileBusy()直接 return false;无idleToBusyPattern⇒onBusy永不触发),卡片钉死在绿色。本 PR 给 claude-code 补上忙碌正证据双保险:
busyPattern: /(?<!press )esc to interrupt/:工作态 footer 比空闲态多出「· esc to interrupt ·」一段,翻绿前先查屏幕,忙碌标志否决错翻idleToBusyPattern(同源):已错翻的绿卡在下一帧被拉回工作中为什么
影响面
adapters/cli/共用工具COMPLETION_RE的字形问题(✳U+2733 vs 屏幕 ✻U+273B)刻意未动:该分支无 spinner 守卫,单独修会新造提前翻绿 bug测试验证
bunx vitest run test/cli-adapters.test.ts— 441 passed(含新增回归用例:工作态 footer 命中、空闲态不命中、散文不命中、seed/relay 共享 def)bun run build— 通过,audit 无异常bun run daemon:restart— 已在 live daemon 验证加载