Skip to content

fix(codex): clamp cached tokens to input in CodexUsageRow - #3215

Closed
Yuxin-Qiao wants to merge 1 commit into
steipete:mainfrom
Yuxin-Qiao:codex/fix-codex-cached-clamp
Closed

fix(codex): clamp cached tokens to input in CodexUsageRow#3215
Yuxin-Qiao wants to merge 1 commit into
steipete:mainfrom
Yuxin-Qiao:codex/fix-codex-cached-clamp

Conversation

@Yuxin-Qiao

Copy link
Copy Markdown
Contributor

Summary

  • clamp cached to min(cached, input) in CodexUsageRow.init to prevent inflated totals when malformed ~/.codex/sessions/*.jsonl reports more cached_input_tokens than input_tokens
  • keep input/output non-negative and preserve existing reasoning clamp
  • regenerate CodexParserHash (hash cfd84d13 -> 5dc8605a)

Mirrors TokScale crates/tokscale-core/src/sessions/codex.rs:194-210 (clamped_cached = min(cached, input); clamped_reasoning = min(reasoning, output)).

TokScale reference

  • TokScale parses cached_input_tokens / cache_read_input_tokens as cached and clamps to input before TokenBreakdown conversion.
  • CodexBar previously stored cached verbatim and only clamped reasoning, so a single malformed token_count row could inflate totalTokens and costUSD for that day.

Evidence (text, no screenshots)

  • make check pass (2032 lint files, 0 violations, parser hash current 5dc8605a458f782f, Provider manifests 69 providers):
Codex parser hash is current (5dc8605a458f782f)
Provider manifests are current (69 providers)
Done linting! Found 0 violations, 0 serious in 1990 files.
  • Minimal reproduction (swift, HOME/.tmp-home):
input=100 cached=1000
old cached stored: 1000 -> total would be 1100
new cached clamped: 100 -> total would be 100
clamp works: PASS
  • Diff stat: 2 files changed, 6 insertions(+), 4 deletions(-) (only CostUsageScanner.swift + generated hash).

Testing

  • make check full pass (see above)
  • No UI changes; backend-only, no provider silo or pricing table changes.

TokScale clamps cached_input_tokens to input_tokens (clamped_cached = min(cached, input)) to prevent inflated totals when malformed JSONL reports more cached than input. CodexBar previously stored cached verbatim and only clamped reasoning, so a malformed row could inflate totalTokens and cost.

This mirrors crates/tokscale-core/src/sessions/codex.rs:194-210 and keeps input/cached/output non-negative.
@clawsweeper

clawsweeper Bot commented Aug 26, 2026

Copy link
Copy Markdown

🦞👀
ClawSweeper picked this up.

Pull request received. I will update this pull request when review starts.

@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: 046448e649

ℹ️ 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".

self.input = max(0, input)
// Clamp cached to not exceed input (toks scale: clamped_cached = min(cached, input))
// prevents inflated totals when malformed JSONL reports more cached than input.
self.cached = min(max(0, cached), max(0, input))

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Keep aggregate and row token counts consistent

When a token_count has cached_input_tokens > input_tokens—the exact malformed case targeted here—the parser's add(...) calls still store the unclamped cached count in usage.days, while this initializer stores a different value in codexRows. codexCanonicalPricingRows then cannot reconcile the row with its aggregate and drops it, so reports still expose the inflated cacheReadTokens from usage.days; they also lose row-only reasoning and can return no cost for priority or authoritative-cost groups. Apply the clamp before both representations are recorded, and cover the scanner-to-report path rather than only constructing this row.

AGENTS.md reference: AGENTS.md:L19-L25

Useful? React with 👍 / 👎.

@clawsweeper clawsweeper Bot added merge-risk: 🚨 other 🚨 Merging this PR has meaningful risk outside the owned taxonomy. P2 Normal priority bug or improvement with limited blast radius. rating: 🦪 silver shellfish Thin PR readiness signal; proof, validation, or implementation needs work. status: 📣 needs proof The PR needs real behavior proof before ClawSweeper can clear the contributor ask. labels Aug 26, 2026
@clawsweeper

clawsweeper Bot commented Aug 26, 2026

Copy link
Copy Markdown

Codex review: needs real behavior proof before merge. Reviewed August 26, 2026, 3:07 PM ET / 19:07 UTC.

ClawSweeper review

What this changes

The PR clamps cached Codex session tokens to the input-token count when creating a parsed usage row.

Merge readiness

Blocked until stronger real behavior proof is added - 5 items remain

Keep open: the clamp is applied only to per-event rows, while the scanner still records the malformed cached count in daily aggregates. Those representations then fail reconciliation, so the intended reporting fix is incomplete.

Priority: P2
Reviewed head: 046448e649b8f5075801bd17a0dc7bab1cc0335e

Review scores

Measure Result What it means
Overall readiness 🦪 silver shellfish (2/6) The intended safeguard is narrow, but the current patch leaves the aggregate path inconsistent and lacks real end-to-end behavior proof.
Proof confidence 🦪 silver shellfish (2/6) Needs stronger real behavior proof before merge: The supplied terminal text demonstrates the initializer calculation, but not an after-fix scan-to-report result through the real aggregate and reconciliation path; add a redacted terminal transcript of that path before merge. After adding proof, update the PR body; ClawSweeper should re-review automatically. If it does not, the PR author or someone with repository write access can comment @clawsweeper re-review.
Patch quality 🦐 gold shrimp (3/6) 1 actionable review finding remain.

Verification

Check Result Evidence
Real behavior Needs proof Needs stronger real behavior proof before merge: The supplied terminal text demonstrates the initializer calculation, but not an after-fix scan-to-report result through the real aggregate and reconciliation path; add a redacted terminal transcript of that path before merge. After adding proof, update the PR body; ClawSweeper should re-review automatically. If it does not, the PR author or someone with repository write access can comment @clawsweeper re-review.
Evidence reviewed 4 items Row-only clamp: The changed initializer clamps cached tokens, but it is reached after the scanner has calculated the token delta.
Aggregate remains unnormalized: The parser passes deltaCached unchanged to add(), whose packed daily aggregate adds cached verbatim before appending the now-clamped row.
Reconciliation rejects the mismatch: Canonical pricing rows compare row totals against the persisted daily aggregate and mark the group unresolved when they differ.
Findings 1 actionable finding [P2] Clamp cached tokens before recording aggregates
Security None None.

Live Verification

Command: swift test --filter CostUsageScannerTests

Result: FAIL (failed) — execution before step 1 run: sh -lc pnpm install --ignore-scripts --frozen-lockfile failed: ! Corepack is about to download https://registry.npmjs.org/pnpm/-/pnpm-11.24.0.tgz

sh -lc pnpm install --ignore-scripts --frozen-lockfile failed: ! Corepack is about to download https://registry.npmjs.org/pnpm/-/pnpm-11.24.0.tgz

Assertions:

  • FAIL expect_output: Test run

How this fits together

CodexBar scans local Codex JSONL sessions into daily aggregate counters and per-event rows, then reconciles them to calculate usage and cost reports. This change affects the per-event row branch of that pipeline.

flowchart LR
A[Codex JSONL session] --> B[Token-count parser]
B --> C[Daily aggregate counters]
B --> D[Per-event usage rows]
C --> E[Row and aggregate reconciliation]
D --> E
E --> F[Usage and cost report]
Loading

Before merge

  • Add real behavior proof - Needs stronger real behavior proof before merge: The supplied terminal text demonstrates the initializer calculation, but not an after-fix scan-to-report result through the real aggregate and reconciliation path; add a redacted terminal transcript of that path before merge. After adding proof, update the PR body; ClawSweeper should re-review automatically. If it does not, the PR author or someone with repository write access can comment @clawsweeper re-review.
  • Clamp cached tokens before recording aggregates (P2) - add(...) has already persisted deltaCached into usage.days unchanged before this initializer clamps the row. For the malformed case this PR targets, the aggregate and row totals differ; reconciliation rejects the rows and report output can still retain the inflated aggregate count. Normalize the delta before both writes and add a scanner-to-report regression test.
  • Resolve merge risk (P1) - Merging this row-only normalization can leave malformed cached counts in aggregate reports and cause the corrected rows to be excluded from pricing reconciliation.
  • Resolve merge risk (P1) - The current head is reported as dirty against main and needs a merge/rebase refresh after the functional repair.
  • Complete next step (P2) - The aggregate/row inconsistency has a narrow mechanical repair and a clear regression-test boundary.

Findings

  • [P2] Clamp cached tokens before recording aggregates — Sources/CodexBarCore/Vendored/CostUsage/CostUsageScanner.swift:368
Agent review details

Security

None.

Review metrics

Metric Value Why it matters
Patch surface 2 files changed, 6 additions, 4 deletions The small patch changes only the row representation while the scanner also persists an aggregate representation.

Merge-risk options

Maintainer options:

  1. Normalize at scanner ingestion (recommended)
    Clamp cached tokens before both aggregate and row writes, then prove the resulting report has matching cached-token and cost values.
  2. Pause this narrow patch
    Close or replace the branch if the shared-ingestion repair cannot be made without changing the parser’s established accounting contract.
Copy recommended automerge instruction
@clawsweeper automerge

Special instructions:
Normalize cached input before aggregate and row writes; add a scanner-to-report regression fixture for cached tokens exceeding input.

Technical review

Best possible solution:

Normalize cached tokens once at scanner ingestion before writing either aggregate counters or usage rows, and cover the complete scanner-to-report path with a malformed JSONL fixture.

Do we have a high-confidence way to reproduce the issue?

Yes—source inspection shows that a token_count with cached tokens above input is stored unclamped in the daily aggregate before the row initializer clamps only the row.

Is this the best way to solve the issue?

No—the clamp must be applied before both aggregate and row writes; changing only the row cannot preserve the reconciliation invariant.

Full review comments:

  • [P2] Clamp cached tokens before recording aggregates — Sources/CodexBarCore/Vendored/CostUsage/CostUsageScanner.swift:368
    add(...) has already persisted deltaCached into usage.days unchanged before this initializer clamps the row. For the malformed case this PR targets, the aggregate and row totals differ; reconciliation rejects the rows and report output can still retain the inflated aggregate count. Normalize the delta before both writes and add a scanner-to-report regression test.
    Confidence: 0.98

Overall correctness: patch is incorrect
Overall confidence: 0.96

AGENTS.md: found and applied where relevant.

Codex review notes: model internal, reasoning high; reviewed against cf79d1310493.

Labels

Label changes:

  • add P2: Affected malformed local session data can produce incorrect usage and cost reporting, with a bounded reporting blast radius.
  • add merge-risk: 🚨 other: The patch can leave two persisted report representations inconsistent, causing incorrect aggregate accounting or rejected pricing rows.
  • add rating: 🦪 silver shellfish: Overall readiness is 🦪 silver shellfish; proof is 🦪 silver shellfish and patch quality is 🦐 gold shrimp.
  • add status: 📣 needs proof: The PR needs real behavior proof before ClawSweeper can clear the contributor ask. Needs stronger real behavior proof before merge: The supplied terminal text demonstrates the initializer calculation, but not an after-fix scan-to-report result through the real aggregate and reconciliation path; add a redacted terminal transcript of that path before merge. After adding proof, update the PR body; ClawSweeper should re-review automatically. If it does not, the PR author or someone with repository write access can comment @clawsweeper re-review.

Label justifications:

  • P2: Affected malformed local session data can produce incorrect usage and cost reporting, with a bounded reporting blast radius.
  • merge-risk: 🚨 other: The patch can leave two persisted report representations inconsistent, causing incorrect aggregate accounting or rejected pricing rows.
  • rating: 🦪 silver shellfish: Overall readiness is 🦪 silver shellfish; proof is 🦪 silver shellfish and patch quality is 🦐 gold shrimp.
  • status: 📣 needs proof: The PR needs real behavior proof before ClawSweeper can clear the contributor ask. Needs stronger real behavior proof before merge: The supplied terminal text demonstrates the initializer calculation, but not an after-fix scan-to-report result through the real aggregate and reconciliation path; add a redacted terminal transcript of that path before merge. After adding proof, update the PR body; ClawSweeper should re-review automatically. If it does not, the PR author or someone with repository write access can comment @clawsweeper re-review.

Evidence

Acceptance criteria:

  • [P1] swift test --filter CostUsageScannerTests.
  • [P1] make check.

What I checked:

Likely related people:

  • Peter Steinberger: Current-main blame attributes the aggregate writer and row-reconciliation logic to this implementation lineage. (role: current implementation provenance; confidence: medium; commits: cf79d1310493, 10587234b54e; files: Sources/CodexBarCore/Vendored/CostUsage/CostUsageScanner.swift, Sources/CodexBarCore/Vendored/CostUsage/CostUsageScanner+ReportReconciliation.swift)

Rank-up moves

Optional improvements that raise the rating; they are not merge blockers.

  • Normalize cached values before both aggregate and row writes.
  • Add a focused malformed-JSONL scanner-to-report regression test.
  • Post a redacted terminal transcript of the corrected report; updating the PR body should trigger a fresh review, or a maintainer can comment @clawsweeper re-review.

Rating scale

Score Internal tier Crab rank Meaning
6/6 S 🦀 challenger crab Exceptional readiness
5/6 A 🦞 diamond lobster Very strong readiness
4/6 B 🐚 platinum hermit Good normal PR; ordinary maintainer review
3/6 C 🦐 gold shrimp Useful, but confidence is limited
2/6 D 🦪 silver shellfish Proof or implementation needs work
1/6 F 🧂 unranked krab Not merge-ready
N/A NA 🌊 off-meta tidepool Rating does not apply

Overall follows the weaker of proof and patch quality.
Shiny media proof means a screenshot, video, or linked artifact directly shows the changed behavior. Runtime, network, CSP, and security claims still need visible diagnostics.

Workflow

  • ClawSweeper keeps one durable marker-backed review comment per issue or PR.
  • Re-runs edit this comment so the latest verdict, findings, and automation markers stay together instead of adding duplicate bot comments.
  • A fresh review can be triggered by eligible @clawsweeper re-review comments, exact-item GitHub events, scheduled/background review runs, or manual workflow dispatch.
  • PR/issue authors and users with repository write access can comment @clawsweeper re-review or @clawsweeper re-run on an open PR or issue to request a fresh review only.
  • Maintainers can also comment @clawsweeper review to request a fresh review only.
  • Fresh-review commands do not start repair, autofix, rebase, CI repair, or automerge.
  • Maintainer-only repair and merge flows require explicit commands such as @clawsweeper autofix, @clawsweeper automerge, @clawsweeper fix ci, or @clawsweeper address review.
  • Maintainers can comment @clawsweeper explain to ask for more context, or @clawsweeper stop to stop active automation.

@steipete steipete left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

Reviewed head 046448e. The aggregate/row mismatch already identified in review is a blocker: the scanner records the raw cached delta in the daily aggregate before constructing the newly clamped row, so reconciliation can discard the corrected rows.

There is also a valid-data case to preserve. The bare-usage parser currently subtracts cached tokens from prompt input before constructing its totals. A response with prompt=120 and cached=100 therefore reaches this initializer as input=20, cached=100. A universal initializer clamp would turn only the row into cached=20 and disagree with its aggregate even though the original response is valid. Please settle that representation at ingestion and cover both native token_count and bare-usage paths through scanner, persistence, reload, and final reporting. The initializer arithmetic alone does not establish the displayed token or cost result.

The generated parser-hash change also needs an upgrade strategy consistent with the no-rebuild guarantees in #3051. The current 0.55.1 hash is not a compatible predecessor, so a mechanical refresh would rebuild existing databases; simply allowlisting it would retain malformed persisted aggregates. Please prove selective repair or another bounded migration that preserves unaffected history and scan offsets, and include valid cached-heavy, malformed, incremental, and SQLite-upgrade regressions before rebasing this for merge.

@Yuxin-Qiao

Copy link
Copy Markdown
Contributor Author

Closing this PR based on review feedback:

  1. CostUsagePricing.codexCostUSD already enforces min(cachedInputTokens, totalInput) at pricing evaluation time.
  2. In bare-usage records, input stores billedInput (prompt - cached), so clamping cached to input in CodexUsageRow.init incorrectly truncates valid cached-heavy rows and creates aggregate/row reconciliation mismatches.
  3. Key TokScale parity logic was already incorporated in fix(codex): tokscale parity for token counts - max cached, clamped, reasoning split, stale #3120.

Thanks for the thorough review @steipete!

@Yuxin-Qiao Yuxin-Qiao closed this Aug 27, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

merge-risk: 🚨 other 🚨 Merging this PR has meaningful risk outside the owned taxonomy. P2 Normal priority bug or improvement with limited blast radius. rating: 🦪 silver shellfish Thin PR readiness signal; proof, validation, or implementation needs work. status: 📣 needs proof The PR needs real behavior proof before ClawSweeper can clear the contributor ask.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants