fix(desktop): clear the composer gauge on target change, pair the frozen window - #4837
Conversation
…zen window Two review follow-ups to apache#4731, both confirmed against the merged head: - The tracker only cleared its reading when the target went away, so switching sessions or routes left the previous target's number on screen — indefinitely, when the new target's first read rejected, since a failed read deliberately keeps the last value standing. A changed target now clears the reading before the first read on the new one; re-aiming at the same target keeps the value, so a same-target refresh failure still does not flicker. The kept value is now only ever the current target's. - The probe boundary narrowed the live reading to usageTokens, so the gauge divided the snapshot's numerator by the live catalog's window while the inspector's bar used the snapshot's frozen one — the two indicators could disagree on the share, and with no metadata window the gauge dropped a percentage the snapshot could have supplied. The frozen contextWindow now travels with the tokens as meteredContextWindow, resolved after a declared override and before metadata. Refs apache#4717 Generated-by: Maka
jackwener
left a comment
There was a problem hiding this comment.
Approving at exact head 3312d92b. Both P2s from #4731 are closed. Two checks pass.
The target-change clearing is fixed at the point the old behaviour came from
The previous behaviour emitted undefined only when the next target was absent, so switching to a different session left the previous reading on screen — and a rejected first read on the new target pinned it there indefinitely, because the failure handler deliberately keeps the last value.
The tests now assert the clearing as the leading event rather than as an implementation detail:
tracker.setTarget({ sessionId: 's1', route: ROUTE });
tracker.setTarget({ sessionId: 's2', route: ROUTE });
// seen === [undefined, { usageTokens: 79_436, contextWindow: 128_000 }, undefined]That trailing undefined is the fix: the second setTarget clears before it reads, so no session ever shows another session's number. The comment says it plainly — the leading undefined is the aim itself. Keeping the last value on a failed read is still correct on its own; what changed is that it no longer compounds with a stale target.
The frozen window is now unrepresentable-as-unpaired
The gap I raised was not that the window was dropped downstream — it was that { readonly usageTokens: number } at the boundary made the pairing impossible to express, so no downstream code could have used it.
Both boundaries now carry { usageTokens; contextWindow? }, and the consumer forwards meteredContextWindow alongside the numerator instead of replacing the numerator alone. The reason is recorded where the next reader will need it:
Dropping the window would leave the gauge to divide the snapshot's numerator by whatever window the live catalog currently reports — one row's tokens against another row's ceiling.
That also resolves what the module's own doc comment and the #4731 summary both claimed — that the inspector and the gauge cannot disagree. It held for the token count and not for the percentage; with the window travelling as a pair, it now holds for both.
This is a fix, so the merge decision remains a human's.
简体中文
在 3312d92b 上批准。#4731 的两条 P2 都已关闭。 两项检查通过。
「切换 target 时清除」修在了旧行为的来源处
先前的行为只有在下一个 target 为空时才发出 undefined,所以切换到另一个 session 会把上一个读数留在屏幕上——而新 target 的首次读取一旦被拒,它就被无限期钉在那里,因为失败处理器是有意保留最后一个值的。
现在测试把「清除」断言成了领头的事件,而不是一个实现细节:
tracker.setTarget({ sessionId: 's1', route: ROUTE });
tracker.setTarget({ sessionId: 's2', route: ROUTE });
// seen === [undefined, { usageTokens: 79_436, contextWindow: 128_000 }, undefined]末尾那个 undefined 就是修复本身:第二次 setTarget 在读取之前先清除,于是没有任何一个 session 会显示另一个 session 的数字。 注释说得很直白——领头的 undefined 正是目的本身。「读取失败时保留最后一个值」单独看仍然是对的;变了的是它不再与一个过期的 target 叠加。
冻结窗口现在「无法被表达成不成对的样子」
我提的那个缺口并不是「窗口在下游被丢掉了」——而是边界处的 { readonly usageTokens: number } 让这个配对根本无法表达,所以下游任何代码都不可能用到它。
现在两处边界都携带 { usageTokens; contextWindow? },消费端也在分子之外一并转发 meteredContextWindow,而不再只替换分子。理由被记录在了下一个读者需要它的地方:
丢掉窗口,会让 gauge 用快照的分子去除以实时目录当前报告的任意窗口——一行的 tokens 配上另一行的天花板。
这同时也解决了模块自己的文档注释与 #4731 摘要共同声称的那件事——inspector 与 gauge 不可能不一致。那句话对 token 数成立、对百分比不成立;如今窗口作为一对同行,它对两者都成立了。
这是一个 fix,合并与否仍由人决定。
Automated review notice: This comment was posted by an automated review agent operated by jackwener. It is not an independent human review and does not replace one.
Review follow-up to #4731 — two P2s raised by @hqhq1025 and confirmed by @jackwener on the merged head, each verified by reproduction before fixing:
1. Clear the previous reading when changing targets
setTargetonly emittedundefinedwhen the new target was absent. Reproduced on the merged head: resolve session A at 79,436 tokens, switch to B, reject B's first read → A's number stays on screen indefinitely — the keep-last-value-on-failure protection, meant for same-target reads, preserved the previous target's value.A changed target now clears the reading before the first read on the new one. Re-aiming at the same target does not clear (no flicker on refresh; a same-target failure keeps the value standing, as before). The kept value is now only ever the current target's — the two rules compose instead of colliding.
2. Carry the snapshot's frozen context window with its usage
The probe boundary narrowed the live reading to
{ usageTokens }, so the gauge divided the snapshot's numerator by the live catalog's window while the inspector's bar used the snapshot's frozen window — the PR's "one number from one row, cannot disagree" claim held for tokens but not for the percentage, and with no metadata window the gauge dropped a share the snapshot could have supplied.The frozen
contextWindownow travels with the tokens asmeteredContextWindow, resolved in the order declared override → metered window → metadata fallback (declared still wins — it is the user's intent and the only one that arms the compaction threshold).Tests
Validation
apps/desktoptypecheck (all four configs),@maka/uitsc--baseratchet, Astryx surface inventory, knip (both workspaces)live-context-usage17/17,composer-context-usage2/2Refs #4717
🤖 Generated by Maka