Skip to content

fix(integrations): read a missing token estimate as unavailable, not zero - #2006

Merged
hidb4ai merged 2 commits into
oceanbase:masterfrom
kragent66-glitch:fix/1926-missing-token-estimates
Oct 19, 2026
Merged

hidb4ai merged 2 commits into
oceanbase:masterfrom
kragent66-glitch:fix/1926-missing-token-estimates

Conversation

@kragent66-glitch

Copy link
Copy Markdown
Contributor

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 the recall.totals block is well-formed — it never checks whether anything was actually measured. A valid response with recall.estimator: null (estimation disabled) and zero totals has a well-formed totals block, so token_reduction returns 0 and the formatter prints saved 0, which reads the same as a genuinely measured zero.

What changes are included in this PR?

  • integrations/claude-code/plugins/powercontext/scripts/statusline.py and integrations/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 (no PC invalid response / diagnostic regression).
    • New measured_reduction() returns the reduction to display only when totals.comparable_preparations > 0. With no comparable preparations — estimation disabled, or nothing comparable in the window — it returns None, which the existing savings_phrase already renders as no data <window>. A window with comparable preparations still reports its genuine (possibly zero) reduction.
    • The internal validation logic is factored into a shared _validated_totals() used by both helpers (no behaviour change).
  • Tests (tests/claude_code_plugin/test_statusline.py, tests/codex_plugin/test_token_savings.py): an estimate-less window reads no data today · no data in 30d and stays online; a window with comparable preparations still reads saved 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 data instead of saved 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.
  • Sabotage check: the four new regression tests fail on the pre-fix formatters (saved 0 instead of no data) and pass with the fix.
  • uv run ruff check / ruff format --check on 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.

…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>".
@CLAassistant

CLAassistant commented Oct 15, 2026 •

Copy link
Copy Markdown

CLA assistant check
All committers have signed the CLA.

@Alanxtl Alanxtl left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

lgtm

@hidb4ai hidb4ai left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

LGTM

@hidb4ai

hidb4ai commented Oct 15, 2026

Copy link
Copy Markdown
Contributor

CI failed

if not isinstance(reduction, int) or isinstance(reduction, bool):
return None
return reduction
return totals

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

[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 即无阻断项。

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

该问题在当前 head 上仍未修复,门禁依旧失败,因此我不能确认它已被修正。

复核证据(全部取自当前 headRefOid):

  1. 本 PR 只有一个提交,headRefOid 仍为 8bcee82fc0ec7c1ff63cd75a87ef42ec50aa7d03,与我上次评论时的 original_commit_id 相同——即没有新提交被推送。
  2. gh api repos/oceanbase/powercontext/commits/8bcee82f.../check-runs:quality 与 Recall effort (OceanBase) 两个 job 仍为 failure(check run 116278921222、116278927832,该提交上每个 job 名只有一个 check run,没有重跑记录)。
  3. 两个 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 结束。
  4. 这两个文件确实在 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 hireview001 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

  • 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`.
@AlexStocks AlexStocks added the 1.4.1 v1.4.1 release tracking label Oct 17, 2026

today_reduction = token_reduction(today)
month_reduction = token_reduction(month)
today_reduction = measured_reduction(today)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

[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 的分界一致,覆盖到位。

@hidb4ai hidb4ai left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

LGTM

@hidb4ai
hidb4ai merged commit 9054db6 into oceanbase:master Oct 19, 2026
36 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

1.4.1 v1.4.1 release tracking

Projects

None yet

Development

Successfully merging this pull request may close these issues.

bug(integrations): distinguish missing token estimates from zero savings

6 participants