Skip to content

fix(traex): 容量排队期间保持工作状态,修复 Dashboard 假 Idle - #928

Open
ITcathyh wants to merge 1 commit into
deepcoldy:masterfrom
ITcathyh:fix-queued-session-status
Open

fix(traex): 容量排队期间保持工作状态,修复 Dashboard 假 Idle#928
ITcathyh wants to merge 1 commit into
deepcoldy:masterfrom
ITcathyh:fix-queued-session-status

Conversation

@ITcathyh

Copy link
Copy Markdown
Contributor

优先级:P1

根因

TraeX 适配器缺少 busyPattern / idleToBusyPattern。容量排队画面静止时会匹配状态栏 ready pattern,并在 2 秒静默窗后被 IdleDetector 判为 idle;worker 又因没有 busy pattern 直接放行 prompt-ready,导致 Dashboard 和卡片在仍排队时显示 Idle。

改动

为 TraeX 增加真实 TUI 工作态与排队态锚点:

  • spinner 帧集加 23 条旋转工作文案,通过 braille 帧前缀避免正文误判。
  • 独立覆盖 Queued for capacityToo many requests right now. You're in the queue 两类可能静态渲染的排队提示。
  • 同时覆盖排队态与真实 working turn,避免队列消解、排队标记离开视口时短暂闪现假 Idle。

这些锚点来自 9 个本地 TraeX 版本(0.201.1-alpha.5 至 0.201.2-alpha.2)的编译期 TUI 字符串;TraeX 已删除 Codex 的 esc to interrupt 页脚,因此没有沿用不成立的 Codex 模式。

影响面

仅修改 TraeX CLI 适配器的空闲/繁忙识别,不影响其他 CLI、会话后端或消息投递路径。

验证

  • 9 个 TraeX / traex-code-mode-host 二进制均验证包含所用工作与排队字符串。
  • 对 94MB TUI 日志验证 esc to interrupt 命中为 0。
  • 相关测试全部使用真实 TraeX 字符串,覆盖静态排队、spinner 工作态及从排队切换到工作态的连续 busy 判定。

@ITcathyh
ITcathyh requested a review from deepcoldy as a code owner August 18, 2026 14:35

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: fde4771b89

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/adapters/cli/traex.ts
Comment on lines +414 to +415
busyPattern: TRAEX_ACTIVE_BUSY_PATTERN,
idleToBusyPattern: TRAEX_ACTIVE_BUSY_PATTERN,

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Handle static queue state before declaring ZMX idle

When TraeX runs on the supported ZMX backend and this static queue notice arrives in the same redraw as the \d+% left ready marker, IdleDetector sees the queue text before its idle→busy edge is armed, then declares idle after quiescence. The worker cannot use busyPattern to veto that transition because deferPromptReadyWhileBusy() explicitly rejects ZMX screen evidence, and a static queue emits no later PTY data to trigger idleToBusyPattern; the Dashboard therefore still remains falsely Idle for this backend. Make the queue marker suppress the pre-idle decision without relying on an authoritative viewport.

AGENTS.md reference: AGENTS.md:L68-L68

Useful? React with 👍 / 👎.

@deepcoldy

Copy link
Copy Markdown
Owner

感谢这个修复!排队态被误判成 Idle 的根因分析和"用编译期 TUI 字符串做锚点、用 braille 帧前缀区分正文"的思路都很扎实,PTY / tmux / zellij / herdr 这几条路径上的 busyPattern + idleToBusyPattern 机制我们复核过是对的(deferPromptReadyWhileBusy 挂住 + idleToBusyPattern 自愈都验证有效)。

这是一条自动评审的初步意见,最终以维护者审阅为准。合入前建议再看两点:

1)ZMX 后端上原问题仍会复现(主要阻断)
本 PR 的两条机制在 ZMX 这个受支持后端上都兜不住首次「静态排队」:

  • busyPattern 路径:deferPromptReadyWhileBusy() / probeBusyPatternIdle() 都在 backendScreenEvidenceIsAuthoritativeForMutation() 为 false(ZMX)时提前返回——ZMX 的 history 不是可信的当前 viewport,按设计不允许用该快照变更状态。
  • idleToBusyPattern 路径:它只在 IdleDetector.feed() 内检查,且边沿要求「先 markIdle 再 armed」。而 ZMX 的屏幕内容走 onBackendScreenResync()(worker.ts)——那里只调 idleDetector.reset()从不把 history 喂进 IdleDetector(源码注释即"do not feed history into IdleDetector")。静态排队屏之后没有新的 PTY 字节,所以 idle→busy 的自愈边沿永远不会触发。

最小运行时结果:单个 chunk 同时含 Queued for capacity + 100% left,之后完全静默,过了 quiescence + spinner-guard 后仍然是 idle=1, busy=0——正则识别到了队列,但会话还是先被判 Idle 且不自愈。

建议补一条「不依赖权威 viewport、在首次 idle 之前就消费显式静态 queue 证据」的机制,并加一个 ZMX 形状的回归:单 chunk 含 queue + 100% left → 之后静默、计时推进后不得 Idle;queue 消失并出现真实 composer 后必须能恢复 Idle。注意不能只再加一个 screen-capture probe——ZMX 的安全边界正是禁止用该快照变更状态。

2)两条独立排队串没有行锚(建议一并收紧)
Queued for capacity 和整条 queue notice 目前匹配整行任意位置(不像 spinner 标签有 braille 帧作判别锚),所以助手正文里逐字引用这两句会误判成 busy。单独看影响较小,但一旦按第 1 点加了 pre-idle 的 busy latch,这个误判会被放大,所以建议这轮一起收紧:

  • 给这两条 standalone arm 加行首约束((?:^|[\n\r])[ \t]*…),排除句中引用;
  • 同时注意保留 ⠋ Queued for capacity 这种 spinner 前缀的队列态——它现在能匹配只是因为 standalone arm 无锚(子串命中)。注释把它列进了 spinner label 集,但数组里其实没有收进去,所以只给 standalone arm 加行锚反而会漏掉真实的 spinner 队列态。可以把该标签也纳入 spinner 分支,再让静态分支带行锚;
  • 建议加测试覆盖:裸行 / 带缩进 / ANSI 清行序列 / 带 at position N 后缀 → true,正文中段引用 → false,spinner 前缀队列 → true。

其余方面(PTY 路径修复、composer 无回归、opt-in 只作用于 TraeX、无 ReDoS、测试真跑)都没问题。辛苦啦 🙏

TraeX 适配器缺少 busyPattern/idleToBusyPattern,容量排队屏是静态
画面,匹配 readyPattern 的 \d+% left 状态栏分支后熬过 2s 静默窗,
被 IdleDetector 判为 idle,worker 的 deferPromptReadyWhileBusy 因无
busyPattern 直接放行,导致 Dashboard/卡片在会话排队等容量时显示 Idle。

给 traex 适配器补上 busyPattern + idleToBusyPattern,锚点全部从
traex 二进制编译期 TUI 字符串表提取(9 个本地版本
0.201.1-alpha.5 … 0.201.2-alpha.2 逐一验证,traex 与
traex-code-mode-host 均含):

- spinner 帧集 ⠋⠙⠹⠸⠼⠴⠦⠧⠇⠏ + 旋转工作文案
  (Working…/Thinking…/Pondering…/Working it out… 等 23 条),
  以 braille 帧做前缀锚点防止转录正文误判;
- 排队态独立锚点 "Queued for capacity" 与
  "Too many requests right now. You're in the queue",
  排队屏可能静态渲染(spinner 不动画),不强制要求帧前缀。

TraeX 从 Codex 分叉后删掉了 "esc to interrupt" 页脚提示
(全部 9 个版本 + 94MB TUI 日志 grep 命中均为 0),因此 Codex
busyPattern 的第二锚点对 traex 无效,不能照搬。

工作态与排队态必须同时覆盖:worker 的 busy-pattern idle probe 在
标记离开视口时即标记 ready,只匹配排队标记会在队列消解进入真实
working turn 时闪一下假 Idle。

二轮修复(评审意见):

1. ZMX 后端上原问题仍会复现,补 pre-idle static busy latch:
   busyPattern 是 viewport probe,ZMX 的 history 不是可信 viewport,
   deferPromptReadyWhileBusy/probeBusyPatternIdle 在
   backendScreenEvidenceIsAuthoritativeForMutation() 为 false 时
   提前返回;idleToBusyPattern 只在 IdleDetector.feed() 内检查且
   要求先 idle 再 armed,而 ZMX 屏幕内容走 onBackendScreenResync()
   只 reset 不喂 history,静态排队屏后无新 PTY 字节,自愈边沿永不
   触发。新增 CliAdapter.staticBusyPattern(opt-in),IdleDetector
   直接从 PTY 字节流消费显式静态 queue 证据(与 readyPattern/
   completionPattern 同一信任级,不碰 ZMX 禁止的 screen-capture
   快照):当前 chunk 含 queue 证据即 latch,抑制 screen-derived
   idle;当前 chunk 带来无 queue 的 ready 证据(真实 composer
   重绘)才清除。只用当前 chunk 判定——queue 文本与 queue 屏自带的
   100% left 都会残留在 outputTail,用 tail 永远清不掉 latch、
   还会被陈旧 ready 标记误清。reset()/resetReadyEvidence() 随状态
   rebase 清除;fireIdle()(结构化完成,reliableTurnTerminal)
   绕过 latch,权威完成不受影响。traex 的 staticBusyPattern 含
   spinner 前缀队列态(⠋ Queued for capacity):冻结的 braille
   帧只能买 3s spinner-guard,之后仍会假 idle。

2. 两条独立排队串加行锚收紧:standalone arm 改为
   (?:^|[\n\r])[ \t]*… 排除句中引用;"Queued for capacity" 纳入
   spinner label 集(注释原本声称收录但数组漏收),保留
   ⠋ Queued for capacity 这种 spinner 前缀队列态。

测试:
- test/idle-detector.test.ts:ZMX 形状回归(单 chunk 含 queue +
  100% left → 静默推进 10s 不得 Idle;composer 重绘后恢复 Idle)、
  spinner 前缀队列、完整 queue notice(含 at position N 后缀)、
  正文引用不 latch、噪声 chunk 不清 latch、reset 清除、fireIdle
  绕过、generic adapter 机制级验证;
- test/cli-adapters.test.ts:裸行/缩进/at position 后缀/spinner
  前缀 → true,正文中段引用 → false,staticBusyPattern 同上。
- pnpm build 通过;pnpm vitest run test/idle-detector.test.ts
  test/cli-adapters.test.ts 433/433 通过;全量 pnpm test 仅余
  master 上同样存在的环境性失败(skill-doctor)与全量并发抖动
  (隔离重跑通过),与本改动无关。

Co-authored-by: Claude <noreply@anthropic.com>
@ITcathyh
ITcathyh force-pushed the fix-queued-session-status branch from fde4771 to edcfcd5 Compare August 19, 2026 01:04
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants