Repository navigation
fix(integrations): read a missing token estimate as unavailable, not zero - #2006
Conversation
…zero The Claude Code statusline and the Codex Stop hook decided "no data" from token_reduction() being None, which only checks that the totals are well-formed. A valid response with recall.estimator: null (estimation disabled) and zero totals is well-formed, so token_reduction returned 0 and the formatter printed "saved 0" — indistinguishable from a measured zero. Keep token_reduction() as the validity check in _load_stats so estimate-less responses stay online, and add a display helper that returns the reduction only when totals.comparable_preparations > 0. With nothing comparable it returns None, which savings_phrase already renders as "no data <window>".
|
CI failed |
| if not isinstance(reduction, int) or isinstance(reduction, bool): | ||
| return None | ||
| return reduction | ||
| return totals |
There was a problem hiding this comment.
[P1] 本 PR 使仓库 lint 门禁(ty check)失败:dict[Unknown, Unknown] 不满足声明的 dict[str, Any] | None
当前 head 的 quality 与 Recall effort (OceanBase) 两个 job 都是红的,原因相同:ty check 对 _validated_totals 报 invalid-return-type——statusline.py:105 的 return totals 处,ty 推断 isinstance(totals, dict) 收窄后的类型是 Top[dict[Unknown, Unknown]](键类型 Unknown),不满足声明的 dict[str, Any] | None。token_savings.py:114 同型同错。这不是环境抖动,是本 PR 把原来的 return reduction(int)改成 return totals 后新引入的,make check 在本地也会同样失败。
运行时行为本身核对无误:comparable_preparations 在非负整数循环里已校验,measured_reduction 直接取值安全;== 0 判「无可比准备」的语义与「真实测得 0」的区分是正确的改进。
修复方向(二选一,两个孪生文件都要改):
- 返回处收窄类型:
return cast(dict[str, Any], totals); - 或放宽签名:
def _validated_totals(value: object) -> dict[Any, Any] | None(下游只做[]取值与比较,键类型无关紧要)。
修完 CI 转绿后本 PR 即无阻断项。
There was a problem hiding this comment.
该问题在当前 head 上仍未修复,门禁依旧失败,因此我不能确认它已被修正。
复核证据(全部取自当前 headRefOid):
- 本 PR 只有一个提交,
headRefOid仍为8bcee82fc0ec7c1ff63cd75a87ef42ec50aa7d03,与我上次评论时的original_commit_id相同——即没有新提交被推送。 gh api repos/oceanbase/powercontext/commits/8bcee82f.../check-runs:quality与Recall effort (OceanBase)两个 job 仍为failure(check run116278921222、116278927832,该提交上每个 job 名只有一个 check run,没有重跑记录)。- 两个 job 的日志根因相同:
ty check报告error[invalid-return-type],位置是integrations/claude-code/plugins/powercontext/scripts/statusline.py:105与integrations/codex/plugins/powercontext/hooks/token_savings.py:114,make check最终以退出码 2 结束。 - 这两个文件确实在
ty check的检查范围内:pyproject.toml的[tool.ty.environment].extra-paths显式列出了./integrations/claude-code/plugins/powercontext/scripts与./integrations/codex/plugins/powercontext/scripts,因此不是误报,也不是环境抖动。
修复方向不变:让 isinstance(totals, dict) 收窄后的返回值满足声明的 dict[str, Any] | None(例如先显式标注 totals: dict[str, Any],或把键类型收窄为 str),并在本地跑一次 uv run ty check 或 make check 确认通过。如果已修复,请 push 新提交,我再在新 head 上复核。
hireview001
left a comment
There was a problem hiding this comment.
- CI failed: Recall effort (OceanBase) (failure).
- CI failed: quality (failure).
- CI failed: E2E harness (failure). Failed steps: Recall effort (OceanBase) / Check code and types on Linux.
- CI failed: Main (failure). Failed steps: quality / Run checks.
`_validated_totals` narrowed `recall["totals"]` to `dict[Unknown, Unknown]` via `isinstance(..., dict)` and returned it under a `dict[str, Any] | None` annotation, so `ty check` reported `invalid-return-type` for both the Claude Code statusline and the Codex Stop hook. That hook is what the `quality` (Main) and `Recall effort (OceanBase)` (E2E harness) workflows run, so both gates failed on the previous head. Cast the narrowed mapping to the declared type; behaviour is unchanged. Add a helper-level regression test in each integration pinning that the reduction accessors yield an int or None, never the totals mapping: a missing estimate reads as unavailable (`None`) while a genuine measured zero still reads as `0`.
|
|
||
| today_reduction = token_reduction(today) | ||
| month_reduction = token_reduction(month) | ||
| today_reduction = measured_reduction(today) |
There was a problem hiding this comment.
[P2] 同一代理在 OpenCode 侧仍是旧逻辑,三个宿主从此显示不同的值
本 PR 只改了两个 Python 宿主,但这段代理还有第三份实现,且两份被改文件的 docstring 明写"Render the same two-window compression proxy used by OpenCode"。OpenCode 侧仍然是 reductionOf(value) = tokenTotals(value)?.token_reduction(integrations/opencode/plugins/powercontext/src/tui.tsx:124),并同步编译进 integrations/opencode/plugins/powercontext/lib/tui.js:2157。
后果:同一个 recall.estimator: null + 全零 totals 的响应,Claude Code 与 Codex 现在渲染 no data today · no data in 30d,OpenCode 仍渲染 saved 0 today · saved 0 in 30d。也就是说本 PR 修掉的是"缺估算被当成 0",但用户换宿主仍会看到被当成 0 的那一版,两个宿主之间也第一次出现同 payload 不同文案。
issue #1926 只列了这两个文件,所以这不是本 PR 的范围错误;建议二选一:顺手把 measuredReduction 等价逻辑加到 OpenCode 的 src 与 lib(lib/tui.js 是提交进仓库的构建产物,两处都要改),或在本 PR 内明确记录一个后续 issue,避免这份代理继续分叉。
另:test_a_window_without_estimates_reads_as_unavailable_not_zero 断言的是 _stats(0) 仍渲染 saved 0,这与 comparable_preparations > 0 的分界一致,覆盖到位。
Which issue or RFC does this PR close?
Closes #1926
Rationale for this change
The Claude Code statusline and the Codex Stop hook both decided "no data" from
token_reduction(...) is None, but that only checks that therecall.totalsblock is well-formed — it never checks whether anything was actually measured. A valid response withrecall.estimator: null(estimation disabled) and zero totals has a well-formedtotalsblock, sotoken_reductionreturns0and the formatter printssaved 0, which reads the same as a genuinely measured zero.What changes are included in this PR?
integrations/claude-code/plugins/powercontext/scripts/statusline.pyandintegrations/codex/plugins/powercontext/hooks/token_savings.py:token_reduction()is unchanged in behaviour; it stays the validity check in_load_stats, so a response without estimates is still accepted and the integration stays online (noPC invalid response/ diagnostic regression).measured_reduction()returns the reduction to display only whentotals.comparable_preparations > 0. With no comparable preparations — estimation disabled, or nothing comparable in the window — it returnsNone, which the existingsavings_phrasealready renders asno data <window>. A window with comparable preparations still reports its genuine (possibly zero) reduction._validated_totals()used by both helpers (no behaviour change).tests/claude_code_plugin/test_statusline.py,tests/codex_plugin/test_token_savings.py): an estimate-less window readsno data today · no data in 30dand stays online; a window with comparable preparations still readssaved 0 today · saved 0 in 30d.Are there any user-facing changes?
Yes — a rendered statusline / Stop message with no comparable estimate now shows
no datainstead ofsaved 0. This is the fix the issue asks for. No public API, persisted format, or failure-classification changes.How was this change tested?
uv run pytest tests/claude_code_plugin tests/codex_plugin -q→ 358 passed, 4 skipped.saved 0instead ofno data) and pass with the fix.uv run ruff check/ruff format --checkon the four touched files → clean.AI usage statement
Built with an AI coding agent (Hermes Agent) driving the free GLM lane for the coding loop; the change was researched, written, and independently verified (tests run and a sabotage check performed) by the agent under human-authored repo instructions.