Skip to content

Add a read-only OpenCodex usage.jsonl parser and independent cache - #3018

Merged
steipete merged 9 commits into
steipete:mainfrom
Yuxin-Qiao:feat/spend-d-opencodex
Aug 18, 2026
Merged

Add a read-only OpenCodex usage.jsonl parser and independent cache#3018
steipete merged 9 commits into
steipete:mainfrom
Yuxin-Qiao:feat/spend-d-opencodex

Conversation

@Yuxin-Qiao

@Yuxin-Qiao Yuxin-Qiao commented Aug 17, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Parse ~/.opencodex/usage.jsonl (or OPENCODEX_HOME) as a spend-only source, not a quota Provider.
  • Persist an independent opencodex-usage.sqlite with requestId dedupe; never write Codex cost tables.
  • Tests use fixtures only and do not read the developer home log.

Stacked PR 3/5 (Wave D). Depends on #3015 and #3016.

Review follow-up

  • Unreported/unsupported rows with no usage stay unpriced (not $0).
  • Session/today totals use entry(in:forLocalDayContaining:) instead of daily.last.
  • Unpriced estimated requests move into the unpriced bucket and still count as 1 request.
  • Cache writes INSERT OR REPLACE and dedupe by requestID before insert.
  • OpenCodex snapshots set costProvenance: .listPriceEstimate.

Test plan

Fixtures only. No Keychain, no live accounts, no ~/.opencodex.

CODEXBAR_SUPPRESS_TEST_KEYCHAIN_ACCESS=1 swift test --filter OpenCodexUsageParserTests
✔ unreported rows without usage stay unpriced instead of zero spend
✔ unpriced estimated requests count once
✔ session totals use the current day instead of the latest historical day
✔ does not resolve a default home while tests are running
✔ duplicate request ids replace instead of aborting the cache write
✔ parses persisted usage rows without reading the developer home
✔ skips malformed lines and keeps nil usage classes unset
✔ aggregates a fixture log into an independent snapshot
✔ Suite OpenCodexUsageParserTests passed (8 tests)
  • OpenCodexUsageParserTests (8/8)
  • Missing usage.jsonl is a silent source absence, not a global error
  • Cache file is opencodex-usage.sqlite

Co-authored-by: Cursor <cursoragent@cursor.com>
@clawsweeper

clawsweeper Bot commented Aug 17, 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: 2413d5b3ef

ℹ️ 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 on lines +173 to +174
let cost = Self.listPriceUSD(entry: entry, customPricing: customPricing)
if let cost {

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 Avoid pricing entries without reported usage

When an unreported or unsupported row names a recognized model but has no usage payload, listPriceUSD prices four zero token counts and returns 0. That sets sawCost, publishes costUSD == 0, and makes the snapshot provenance a list-price estimate even though the source supplied no meterable usage. Gate pricing on a reported/estimated status with usable token data so these rows remain unpriced or unmetered rather than appearing as confirmed zero spend.

Useful? React with 👍 / 👎.

Comment on lines +125 to +127
sessionTokens: daily.last.flatMap(\.totalTokens),
sessionCostUSD: daily.last.flatMap(\.costUSD),
sessionRequests: daily.last.flatMap(\.requestCount),

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 Derive session totals from the current day

If the log has historical usage but no entry for today, daily.last returns the most recent prior day, so the menu's session/today cost, tokens, and requests continue showing yesterday's values. Other token snapshot construction explicitly looks up the local day containing now and emits zero when history exists but today does not; this aggregator should follow the same current-day lookup.

Useful? React with 👍 / 👎.

Comment on lines +177 to +179
} else if entry.usageStatus == .reported || entry.usageStatus == .estimated {
day.unpriced += 1
if day.priced > 0 { day.priced -= 1 }

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 Count unpriced estimates only once

For an estimated request whose model cannot be priced, this branch increments unpriced without removing the already incremented estimated count. Because entry(dayKey:day:) defines requestCount as the sum of these buckets, one physical request becomes two requests and also inflates the coverage denominator. Reclassify the estimate into one bucket, or keep request count independent from overlapping coverage dimensions.

Useful? React with 👍 / 👎.

Comment on lines +117 to +123
: CostUsagePricing.codexAggregateCostUSD(
model: model,
inputTokens: input,
cachedInputTokens: cached,
outputTokens: output,
modelsDevCatalog: pricing.modelsDevCatalog,
modelsDevCacheRoot: pricing.modelsDevCacheRoot)

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 Apply custom pricing to aggregate fallback rows

When a cached day lacks trusted canonical rows, this fallback calls codexAggregateCostUSD, which still resolves only models.dev or bundled pricing and never consults the new custom-pricing overlay. Consequently the same model uses the override with complete rows but silently reverts to public rates for legacy or incomplete caches, producing inconsistent spend totals. The aggregate path needs the same overlay handling while preserving its threshold safety check.

Useful? React with 👍 / 👎.

Comment on lines +78 to +79
bypassScannerDebounce: true,
calendar: self.settings.costUsageBucketCalendar)

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 Use the pinned calendar for every Codex cache read

The pinned calendar is passed only to this foreground refresh. The startup hydration path still calls loadCachedCodexTokenSnapshotResult on the default fetcher, and the Spend Dashboard constructs CostUsageFetcher(cacheRoot:) without a calendar, so after the system timezone changes those paths bucket the same cache using .current while this path uses the pinned zone. This can initially publish different day totals and lets dashboard scans re-bucket history; propagate the pinned calendar through the shared fetcher and cached/dashboard loaders as well.

Useful? React with 👍 / 👎.

@clawsweeper clawsweeper Bot added merge-risk: 🚨 compatibility 🚨 Merging this PR could break existing users, config, migrations, defaults, or upgrades. P2 Normal priority bug or improvement with limited blast radius. rating: 🧂 unranked krab Not merge-ready due to missing proof or serious correctness/safety concerns. status: 📣 needs proof The PR needs real behavior proof before ClawSweeper can clear the contributor ask. labels Aug 17, 2026
@clawsweeper

clawsweeper Bot commented Aug 17, 2026

Copy link
Copy Markdown

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

ClawSweeper review

What this changes

This stacked PR adds OpenCodex JSONL usage parsing, an independent SQLite cache, and shared spend-accounting support for the planned source.

Merge readiness

Blocked until real behavior proof from a real setup is added - 10 items remain

Keep open: the new local usage source and cache require maintainer sign-off and real behavior proof, and the accounting path currently omits reasoning tokens when totals or prices must be derived.

Priority: P2
Reviewed head: a532d413d35e061ce4b5695ae134c1e2cf8fdc7c
Owner decision: Required. See Decision needed.

Review scores

Measure Result What it means
Overall readiness 🧂 unranked krab (1/6) Useful implementation and focused fixtures are present, but two accounting defects and missing real behavior proof block readiness.
Proof confidence 🧂 unranked krab (1/6) Needs real behavior proof before merge: The body reports fixture tests only; add redacted after-fix terminal output or logs from a real OpenCodex log showing parsing, silent absence, cache creation, and corrected totals 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 🦪 silver shellfish (2/6) 2 actionable review findings remain.

Verification

Check Result Evidence
Real behavior Needs proof Needs real behavior proof before merge: The body reports fixture tests only; add redacted after-fix terminal output or logs from a real OpenCodex log showing parsing, silent absence, cache creation, and corrected totals 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 5 items Derived total omits reasoning: The fallback total adds input, output, cache-read, and cache-creation tokens but excludes reasoning output; the PR fixture models reasoning as separately counted in its total.
Estimated pricing omits reasoning: The list-price call forwards output tokens but not reasoning output tokens, so rows lacking a supplied total can also understate estimated cost.
Current integration boundary: Repository-wide references find the parser and store only in their new implementation/tests; no current app path loads an OpenCodex snapshot.
Findings 2 actionable findings [P2] Include reasoning tokens in derived totals
[P2] Price reasoning output at the output rate
Security None None.

How this fits together

CodexBar converts local usage data into estimated spend for menu and dashboard summaries. The proposed OpenCodex reader would feed that pipeline through a separate cache without changing quota-provider data.

flowchart LR
A[OpenCodex usage log] --> B[JSONL parser]
B --> C[Independent SQLite cache]
C --> D[Spend aggregation]
D --> E[Cost snapshot]
E --> F[Menu and dashboard]
Loading

Decision needed

Question Recommendation
Should CodexBar adopt an opt-in OpenCodex local-log spend source and its independent local cache? Sponsor the opt-in source: Approve the direction conditional on the accounting repair, remaining stacked integration, and real behavior proof.

Why: VISION.md requires sign-off for a new feature that adds local data storage and privacy behavior; this stacked PR does not establish that approval.

Before merge

  • Add real behavior proof - Needs real behavior proof before merge: The body reports fixture tests only; add redacted after-fix terminal output or logs from a real OpenCodex log showing parsing, silent absence, cache creation, and corrected totals 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.
  • Include reasoning tokens in derived totals (P2) - When a valid row lacks totalTokens, this fallback omits reasoningOutputTokens. The fixture treats reasoning as part of the total, so such rows under-report usage; include it here and cover a nil-total row.
  • Price reasoning output at the output rate (P2) - The usage model exposes reasoning output separately, but this estimate forwards only outputTokens. Rows without a supplied total therefore also exclude reasoning from estimated spend; add it to the priced output count and regress it.
  • Resolve merge risk (P1) - Rows that omit a total can under-report both tokens and list-price spend when they contain reasoning output.
  • Resolve merge risk (P1) - Fixture tests do not prove parsing, source absence, cache behavior, and visible totals against a real OpenCodex log.
  • Resolve merge risk (P1) - The opt-in local source and its persisted cache need product approval before landing.
  • Complete next step (P2) - The reasoning-token omissions have a narrow, source-proven repair; product sign-off and contributor real-behavior proof remain separate merge gates.
  • Improve patch quality - Repair reasoning-token total and price calculation with regression coverage.
  • Improve patch quality - Add redacted real-log evidence, then update the PR body for automatic re-review.

Findings

  • [P2] Include reasoning tokens in derived totals — Sources/CodexBarCore/Vendored/OpenCodexUsage/OpenCodexUsageModels.swift:45-50
  • [P2] Price reasoning output at the output rate — Sources/CodexBarCore/Vendored/OpenCodexUsage/OpenCodexUsageAggregator.swift:277-296
Agent review details

Security

None.

Review metrics

Metric Value Why it matters
Patch scope 37 files changed; production +1,973/-203, tests +532/-1 The parser arrives with broad shared-accounting and persisted-settings changes, so upgrade and product review matter.

Merge-risk options

Maintainer options:

  1. Repair derived reasoning accounting (recommended)
    Include reasoning output in fallback totals and list-price output calculations, with nil-total regression coverage, before merge.
  2. Pause for source sign-off
    Hold the stacked feature if maintainers do not want an additional local usage source and cache contract.

Technical review

Best possible solution:

Approve an opt-in OpenCodex spend source only after repairing reasoning-token accounting and showing redacted real-log evidence for the complete source-to-summary path.

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

Yes for the accounting defect: a valid row with reasoning output but no total reaches the fallback path, which visibly omits those tokens and their estimated output cost.

Is this the best way to solve the issue?

No: the implementation must include reasoning output in derived totals and output-rate pricing, then receive sign-off for the opt-in source and cache direction.

Full review comments:

  • [P2] Include reasoning tokens in derived totals — Sources/CodexBarCore/Vendored/OpenCodexUsage/OpenCodexUsageModels.swift:45-50
    When a valid row lacks totalTokens, this fallback omits reasoningOutputTokens. The fixture treats reasoning as part of the total, so such rows under-report usage; include it here and cover a nil-total row.
    Confidence: 0.93
  • [P2] Price reasoning output at the output rate — Sources/CodexBarCore/Vendored/OpenCodexUsage/OpenCodexUsageAggregator.swift:277-296
    The usage model exposes reasoning output separately, but this estimate forwards only outputTokens. Rows without a supplied total therefore also exclude reasoning from estimated spend; add it to the priced output count and regress it.
    Confidence: 0.88

Overall correctness: patch is incorrect
Overall confidence: 0.9

AGENTS.md: found and applied where relevant.

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

Labels

Label justifications:

  • P2: The feature affects local spend estimates but has bounded user impact.
  • merge-risk: 🚨 compatibility: The branch changes shared accounting models and adds persisted settings and cache behavior.
  • rating: 🧂 unranked krab: Overall readiness is 🧂 unranked krab; proof is 🧂 unranked krab and patch quality is 🦪 silver shellfish.
  • status: 📣 needs proof: The PR needs real behavior proof before ClawSweeper can clear the contributor ask. Needs real behavior proof before merge: The body reports fixture tests only; add redacted after-fix terminal output or logs from a real OpenCodex log showing parsing, silent absence, cache creation, and corrected totals 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] CODEXBAR_SUPPRESS_TEST_KEYCHAIN_ACCESS=1 swift test --filter OpenCodexUsageParserTests.
  • [P1] make check.
  • [P1] make test.

What I checked:

Likely related people:

  • steipete: Introduced the unified Usage & Spend dashboard that owns the destination surface for local spend sources. (role: dashboard feature introducer; confidence: high; commits: 0397529ae6e5; files: Sources/CodexBar/SpendDashboardController.swift)
  • Yuxin Qiao: Has prior merged work on cost refresh behavior and is the recent contributor across the proposed accounting changes. (role: recent spend-area contributor; confidence: medium; commits: 28504b9c5514; files: Sources/CodexBar/UsageStore+TokenCost.swift, Sources/CodexBarCore/Vendored/OpenCodexUsage/OpenCodexUsageAggregator.swift)

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.

History

Review history (1 earlier review cycle)
  • reviewed 2026-08-17T18:23:35.449Z sha 2413d5b :: needs real behavior proof before merge. :: [P2] Do not price rows without usable reported usage | [P2] Use today’s bucket for the session summary | [P2] Avoid double-counting unpriced estimates | [P2] Deduplicate cache rows before inserting | [P2] Apply custom pricing to aggregate fallback rows | [P2] Propagate the pinned calendar to cached and dashboard scans

Yuxin-Qiao and others added 8 commits August 18, 2026 02:53
Pinned calendars now reach cached Codex reads, vendor-reported costs keep their source, and shorter summaries no longer inherit snapshot-wide metered spend.

Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Cached day totals now honor overlay rates, including explicit free rates, without rereading custom-pricing.json for every usage row.

Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
…imit.

Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Unreported rows no longer publish zero spend, estimated unpriced requests count once, and duplicate request IDs replace instead of aborting the cache write.

Co-authored-by: Cursor <cursoragent@cursor.com>
@Yuxin-Qiao
Yuxin-Qiao force-pushed the feat/spend-d-opencodex branch from 2413d5b to a532d41 Compare August 17, 2026 19:26
@Yuxin-Qiao

Copy link
Copy Markdown
Contributor Author

@clawsweeper re-review

@clawsweeper

clawsweeper Bot commented Aug 17, 2026

Copy link
Copy Markdown

🦞🧹
ClawSweeper re-review requested.

I asked ClawSweeper to review this item again.
Action: item re-review queued (workflow sweep.yml, event exact_review_queue).
Result: when the review finishes, ClawSweeper will create the durable review comment if needed or update the existing comment in place.

@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: a532d413d3

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

{
return cost
}
guard let pricing = self.resolvedCodexPricing(

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 Preserve incomplete matching custom prices as unknown

When custom-pricing.json contains a matching model but intentionally omits a rate for a token class that has usage, estimatedCodexCostUSD returns nil and this fallback silently substitutes models.dev or bundled pricing. That violates the overlay's stated behavior that missing fields remain unknown, and produces a public-list-price total instead of an unpriced result; distinguish “no matching overlay” from “matching but incomplete overlay” before falling back.

Useful? React with 👍 / 👎.

Comment on lines +293 to +296
inputTokens: input,
cachedInputTokens: cacheRead,
outputTokens: output,
cacheWriteInputTokens: cacheWrite)

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 Include exclusive cached tokens in bundled pricing input

For OpenCodex rows with cache reads or writes and no custom price, this passes the separately counted cache tokens to an API whose inputTokens contract treats cached tokens as a subset and subtracts them from input. The OpenCodex model instead adds cache classes to input when resolving totals, so an entry with 100 input and 10 cache-read tokens is priced as 90 regular + 10 cached rather than 100 regular + 10 cached, underestimating spend. Convert the exclusive classes to the total-input representation expected by codexCostUSD.

Useful? React with 👍 / 👎.

costUSD: entryCost,
modelsUsed: modelNames,
modelBreakdowns: Self.sortedModelBreakdowns(breakdown),
unpricedRequestCount: entryCost == nil && dayTotal > 0 ? 1 : nil,

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 Expose unpriced models on partially priced days

When a day contains at least one priced model and at least one model whose cost is nil, entryCost remains non-nil because any priced model sets dayCostSeen, so this condition records no unpriced coverage. The resulting day carries only the partial dollar sum while coverageCounts classifies it as priced, hiding the omitted model from Spend Dashboard coverage; track missing-cost model groups independently of whether some cost was seen.

Useful? React with 👍 / 👎.

@steipete
steipete merged commit a532d41 into steipete:main Aug 18, 2026
7 of 9 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

merge-risk: 🚨 compatibility 🚨 Merging this PR could break existing users, config, migrations, defaults, or upgrades. P2 Normal priority bug or improvement with limited blast radius. rating: 🧂 unranked krab Not merge-ready due to missing proof or serious correctness/safety concerns. 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