Skip to content

Report real Grok token usage and list-price cost from CLI logs - #3135

Open
olddonkey wants to merge 23 commits into
steipete:mainfrom
olddonkey:feat/grok-real-token-usage
Open

Report real Grok token usage and list-price cost from CLI logs#3135
olddonkey wants to merge 23 commits into
steipete:mainfrom
olddonkey:feat/grok-real-token-usage

Conversation

@olddonkey

@olddonkey olddonkey commented Aug 22, 2026

Copy link
Copy Markdown
Contributor

Problem

#3085 enabled Grok token cost, but the local session projection was not measuring actual consumption:

  • signals.json exposes ending context-window occupancy, not per-turn token usage. On a real corpus it reported 653K tokens where the completed turns contained 54.1M.
  • Grok cost was always nil, and the models.dev resolver did not include the xAI catalog.
  • Expanding the scan from small metadata files to growing updates.jsonl files could not safely remain on @MainActor.

What this changes

Read the completed-turn usage that the Grok CLI actually records

GrokLocalSessionScanner reads turn_completed events from each session's updates.jsonl, matches both session/update and _x.ai/session/update, and buckets each line by its own timestamp. It preserves raw aggregate token totals and uses the recorded modelCalls count only to approximate per-call tiered list pricing in O(1).

The parser streams through the shared chunked JSONL reader instead of loading whole files. Production bounds are explicit: a 64 MiB tail per file, 1 MiB per record, 20,000 retained turns per file, and global scan budgets of 256 recent sessions, 256 MiB, and 100,000 turns. The process-wide LRU cache retains at most 64 files or 50,000 turns. Cancellation is checked between chunks, I/O and cancelled results are not cached, and any truncation marks history incomplete rather than presenting a partial total as complete.

Price the Grok models and preserve provenance

The models.dev xAI catalog is now eligible for cost pricing. Responses-API names such as grok-4.6-build resolve to the base grok-4.6 SKU after exact lookup, while real independent names such as grok-build-0.1 remain untouched. Cost is published as .listPriceEstimate; Grok's internal costUsdTicks is not presented as billed spend.

On a stale catalog, the scan prices immediately and refreshes in the background. On a fresh install with no catalog artifact, the first scan awaits the initial best-effort refresh attempt before creating the snapshot, so a successful first refresh is visible in the first publication. A refresh failure still degrades to token-only data rather than failing the local scan.

Keep scanning and publication off the main actor

The provider projection consumes the async probe's snapshot. Remaining fallback paths scan on a detached utility task with a single scan in flight. A maximum-window snapshot is narrowed by each consumer through CostUsageTokenSnapshot.narrowed(toHistoryDays:calendar:), including the spend dashboard's 365-day request.

The 365-day lookback and the models.dev pricing refresh now live in the injected defaults of the localSummary / cliVersion seams introduced by #3237, so the credential-binding seam and this branch's scan behavior both hold.

Keep OpenCodex xAI history out of the Grok subscription row

OpenCodex usage.jsonl records provider/model usage but does not retain the credential mode used for each request. Reading today's config.json cannot distinguish older API-key traffic from older OAuth traffic after a configuration switch.

For that reason, xAI entries remain .tokenOnly and are not merged into the Grok subscription row until the producer records request-time credential provenance. The current-config reader and its routing parameter were removed, with dispatcher and fan-out regressions and updated documentation. Other existing OpenCodex subscription routes are unchanged.

Review findings addressed

All items below are present in the current head; the commit SHAs they were originally fixed on were rewritten by later rebases and are therefore not cited.

  • P1 — preserve record-time xAI credential attribution: OpenCodex xAI-to-Grok attribution is paused when record-time evidence is unavailable. Historical traffic can no longer move between provider rows when the current auth config changes.
  • P1 — keep bare OpenCodex xAI history token-only: standalone aggregation does not assign list-price dollars when request-time credential provenance is unavailable, so the CLI no longer reports xAI dollars.
  • P1 — label populated Grok cost surfaces: menu details, charts, dashboard rows, and spend views identify the amount as a public xAI list-price estimate rather than a bill.
  • P1 — keep failed-refresh local totals advancing: every failed Grok remote billing refresh schedules the bounded local scan; the existing single-flight task coalesces concurrent scans.
  • P1 — isolate xAI pricing invalidation: xAI has a separate pricing fingerprint (xaiModelsDevProviderIDs), so xAI-only catalog changes do not invalidate Codex caches. Route resolution still accepts xAI through codexCompatibleModelsDevProviderIDs.
  • P1 — restore the published fallback for Grok menu consumers: menu cards and cost history consume the current-config local publication when the remote snapshot is absent, while override cards retain the original isolated projection and cannot inherit provider-level data.
  • P1 — rescan after an empty Grok publication: a current-config publication with no snapshot is treated as no usable fallback, so a later remote failure rescans newly written local turns.
  • P1 — keep Usage & Spend on the freshest Grok data: dashboard capture and retained-publication paths use the same timestamp selector as live menu consumers.
  • P2 — publish pricing after the first catalog refresh: the initial refresh is awaited only when no cached catalog exists. Stale catalogs still use the non-blocking refresh path.
  • P2 — bound growing session logs: chunked tail reads, per-file and global byte/turn/session budgets, bounded LRU retention, cancellation checks, and incomplete-history propagation.
  • P2 — bound discovery I/O: Grok session discovery stops at 4,096 tree entries and marks history incomplete when that bound is reached.
  • P2 — refresh after preservable network failures: a timeout can retain the last remote provider snapshot while still rescanning local sessions; live consumers select the newer current-config local publication.
  • P3 — correct the documented session source: the docs describe updates.jsonl, the requested window up to 365 days, list-price provenance, and every production bound.

ClawSweeper P2 findings — addressed

  • Run refreshable Grok scans on the dedicated executor: summarizeRequestingPricingRefresh queues its corpus scan through CostUsageScanExecutor instead of calling the scanner inline, so it no longer occupies the cooperative pool. A cancelled scan reports unestablished coverage rather than an authoritative zero.
  • Use an inclusive local-day cutoff for Grok history: the cutoff is startOfDay(now) minus historyDays - 1, matching the window narrowed(toHistoryDays:) renders, so no partial extra day is collected for consumers to discard.
  • Invalidate Pi caches across the newly priceable xAI route: resolved upstream. fix: price Claude Kimi context aliases without crossing providers #3259 removed the Pi cache's reviewed-predecessor adoption entirely, so any parser-hash change now invalidates Pi caches. This branch no longer touches PiSessionCostScanner at all; the per-cache content gate it previously carried is gone with the mechanism it gated.

The first two carry regressions confirmed to fail without their fix.

Changelog placement — corrected

An earlier revision removed the CHANGELOG.md entry on the assumption that release notes were release-owned. That was wrong for a normal PR in this repository, and the entry had been silently absent since. It lives under the ## 0.56.1 — Unreleased section main already carries, because 0.56.0 shipped without this change.

Parser-hash bookkeeping

This branch touches CostUsagePricing.swift, which is inside the Codex parser-hash scope, so both remaining gates were updated together on the current head:

  • CodexParserHash.value regenerated to ac4862abcdfe21a8.
  • CostUsageStore.compatiblePredecessorParserHashes records main's d9a91f31d0addc15, with the exact-set assertion in CostUsageStoreTests kept in sync.

There is no longer a third gate. #3259 removed PiSessionCostScanner's reviewed-predecessor adoption, so a parser-hash change invalidates Pi caches normally and there is nothing to review or advance. PiSessionCostScanner.swift and PiSessionCostCompatibilityTests.swift are byte-identical to main on this head, which is exactly the conservative outcome offered as the alternative on the previous head.

Deterministic behavior proof

The production bounded JSONL scanner, publication path, failure preservation, timestamp selection, menu consumers, and spend-dashboard capture are driven through temporary on-disk Grok session files:

initial_completed_turn_tokens=77
retained_remote_tokens_after_timeout=77
local_completed_turn_tokens_after_append=100
selected_live_consumer_tokens=100
selected_source=newer_current_config_local_publication
second_timeout_local_scan_count=2
dashboard_selected_tokens=100

The regression writes a completed 77-token turn and installs its projection as the retained remote snapshot. It appends a second completed 23-token turn, injects URLError.timedOut, verifies the remote snapshot is preserved at 77, verifies the local publication advances to 100, and verifies the live consumer selects the newer 100-token snapshot. A second failed refresh proves local logs are rescanned again. The status-menu regression separately proves newer local tokens beat stale remote tokens in both the visible card and the cost-history submenu, while the override-card isolation regression remains green. The dashboard regression independently installs a one-minute-older 77-token remote snapshot and a current 100-token local publication, then verifies capture-only Usage & Spend selects the local 100-token, $1 list-price snapshot and its newer timestamp.

Real-session evidence (current head)

The opt-in native-session proof below was re-run on this exact head against the real ~/.grok/sessions/**/updates.jsonl corpus and the local models.dev catalog, so it is no longer earlier-head evidence. It reads local session logs only — no credentials, no network probe, no Keychain access.

CODEXBAR_LIVE_GROK_CATALOG_PROOF=1 swift test --filter GrokXAISpendCatalogTests

catalog_source=grok
today_tokens=0
last_30_days_tokens=2739923
today_cost_usd=nil
window_cost_usd=2.181282
cost_provenance=listPriceEstimate
history_days=365
priced_days=1
token_days=1
daily_buckets=1
available_sources=grok

All 3 tests in that suite passed. The transcript prints aggregate fields only. The corpus had no completed-turn tokens that day and one priced day in the last 30, so these values change as local logs age. The populated-surface assertion in the same suite verifies the visible Public xAI list-price estimate · not a bill. disclosure.

No credential values, account identity, session contents, or interactive Keychain and browser-cookie reads were emitted by any of this work.

Testing

Verified on exact head eef38cd29ca08d98abd730ac1c3dd154359ae2d9:

  • make check: passed — parser hash ac4862abcdfe21a8, provider/package/documentation gates, SwiftFormat clean, SwiftLint 0 violations in 2055 files.
  • make test: passed — 965/965 selections, 81/81 groups successful on the first pass, 0 failed groups, 0 retries, 0 timeouts (666.0 seconds).
  • Focused suites: ProviderArchitectureGatekeeperTests 39/39 and 135 tests across the Grok scanner/pricing/menu/dashboard, OpenCodex routing/fan-out, CostUsageStoreTests and PiSessionCostCompatibilityTests suites.
  • Rebased on upstream main e0d2fd90b (0.56.1 development head), 0 behind / 23 ahead, git diff --check clean.

Automated tests use temporary homes, injected catalogs and transports, and no live credentials or interactive Keychain reads.

Maintainer decision

Every code finding from the 2026-08-25 review and the ClawSweeper follow-up is addressed and still in place after this rebase. The branch is mergeable again and fully green locally.

One thing is still yours to call:

  • Default estimate semantics — whether a non-billed public list-price estimate should be shown by default for Grok. The amount is labeled as an estimate everywhere it renders and stays distinct from SuperGrok subscription credits.

The Pi cache question from the previous head is closed: #3259 removed the mechanism upstream, so this branch invalidates Pi caches normally and carries no Pi changes.

@clawsweeper

clawsweeper Bot commented Aug 22, 2026

Copy link
Copy Markdown

🦞👀
ClawSweeper picked this up.

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

olddonkey added a commit to olddonkey/CodexBar that referenced this pull request Aug 22, 2026

@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: 09cf7edb0f

ℹ️ 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 thread Sources/CodexBar/UsageStore+Refresh.swift Outdated
Comment thread Sources/CodexBarCore/Providers/Grok/GrokLocalSessionScanner.swift
@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 22, 2026
@clawsweeper

clawsweeper Bot commented Aug 22, 2026

Copy link
Copy Markdown

Codex review: needs maintainer review before merge. Reviewed August 29, 2026, 9:32 PM ET / August 30, 2026, 01:32 UTC.

ClawSweeper review

What this changes

This PR replaces Grok context-window totals with bounded completed-turn CLI-log scanning, estimates native Grok usage at disclosed public xAI list prices, and keeps OpenCodex xAI history token-only without request-time credential provenance.

Regression provenance

Possible regression — probable (reviewed change; failure trace). No predecessor PR is attributed.

Merge readiness

⚠️ Ready for maintainer review - 4 items remain

Keep this PR open for the repository owner’s product decision on displaying a non-billed public xAI list-price estimate by default. The completed-turn scanner, bounded execution path, disclosures, regressions, and current-head real-session proof support the technical implementation.

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

Review scores

Measure Result What it means
Overall readiness 🐚 platinum hermit (4/6) The PR has current-head real-session proof and focused regression coverage; its remaining gate is owner direction on default estimate semantics.
Proof confidence 🦞 diamond lobster (5/6) Sufficient (terminal): The changed production owner is the bounded Grok completed-turn scanner and snapshot publication path. The supplied exact-head native-session terminal transcript runs the focused suite against real local logs and reports the after-fix token total, list-price estimate, provenance, and visible disclosure.
Patch quality 🐚 platinum hermit (4/6) No actionable review findings were identified.

Verification

Check Result Evidence
Real behavior Verified Sufficient (terminal): The changed production owner is the bounded Grok completed-turn scanner and snapshot publication path. The supplied exact-head native-session terminal transcript runs the focused suite against real local logs and reports the after-fix token total, list-price estimate, provenance, and visible disclosure.
Evidence reviewed 6 items Current-main behavior remains incomplete: The PR base still derives Grok totals from signals.json context-window fields and explicitly leaves cost nil, so the central user problem is not already solved on current main.
Introduced bounded scan path: The PR requests pricing, then runs the synchronous completed-turn corpus scan through the dedicated CostUsageScanExecutor and reports incomplete coverage on cancellation.
Scoped pricing and cache behavior: xAI model lookup is enabled for native Grok pricing while the Codex pricing-cache fingerprint intentionally remains scoped to Codex-compatible provider IDs.
Findings None None.
Security None None.

How this fits together

CodexBar collects provider usage into snapshots consumed by menu cards and the Usage & Spend dashboard. This change reads local Grok CLI session logs, prices completed turns from the models.dev xAI catalog, and publishes token and estimate data to those views.

flowchart LR
  A[Grok CLI session logs] --> B[Bounded completed-turn scanner]
  C[models.dev xAI catalog] --> D[List-price calculation]
  B --> D
  D --> E[Grok usage snapshot]
  E --> F[Menu and cost history]
  E --> G[Usage and Spend dashboard]
  H[OpenCodex xAI history] --> I[Token-only route]
Loading

Decision needed

Question Recommendation
Should CodexBar show a clearly labeled public xAI list-price estimate by default for native Grok CLI usage? Approve disclosed estimate: Show the estimate by default with the existing non-bill disclosure.

Why: The previous Grok surface exposed token totals without a dollar estimate, so choosing the meaning of the newly displayed default signal requires product judgment.

Before merge

  • Resolve merge risk (P1) - Merging would change the default Grok experience from token-only usage to a dollar estimate; its clear non-bill disclosure does not decide whether that product default is desired.
  • Resolve merge risk (P1) - The captured PR body has a 1,043-unit omitted range; this review’s decision relies on the supplied explicit owner-decision request and observed proof, not on assumptions about that omitted text.
  • Complete next step (P2) - A proof-positive implementation remains open because the default cost-estimate semantics need explicit owner approval.
Agent review details

Security

None.

Review metrics

Metric Value Why it matters
Review surface 34 files; production +1,362/-160, tests +2,096/-137, docs/changelog +56/-18 The change spans scanning, snapshot publication, pricing, UI, and regressions, making explicit default-semantics approval important.

Merge-risk options

Maintainer options:

  1. Approve the disclosed default (recommended)
    Accept the new dollar estimate as a clearly labeled, non-billed Grok usage signal.
  2. Require an opt-in design
    Preserve token-only defaults and add the estimate only behind an explicitly documented setting.

Technical review

Best possible solution:

Retain the bounded completed-turn implementation and explicitly approve the disclosed estimate as the default, or direct a narrowly scoped token-only/opt-in alternative.

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

Yes. The supplied exact-head native-session transcript exercises the bounded Grok scanner against real local CLI logs and reports tokens, list-price provenance, and the disclosed estimate.

Is this the best way to solve the issue?

Unclear. The technical path is well covered, but an owner must decide whether a non-billed list-price dollar value belongs in the default Grok experience.

AGENTS.md: found and applied where relevant.

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

Labels

Label changes:

  • add proof: sufficient: Contributor real behavior proof is sufficient. The changed production owner is the bounded Grok completed-turn scanner and snapshot publication path. The supplied exact-head native-session terminal transcript runs the focused suite against real local logs and reports the after-fix token total, list-price estimate, provenance, and visible disclosure.
  • add rating: 🐚 platinum hermit: Overall readiness is 🐚 platinum hermit; proof is 🦞 diamond lobster and patch quality is 🐚 platinum hermit.
  • add status: 👀 ready for maintainer look: ClawSweeper has no concrete contributor-facing blocker left for this PR. Sufficient (terminal): The changed production owner is the bounded Grok completed-turn scanner and snapshot publication path. The supplied exact-head native-session terminal transcript runs the focused suite against real local logs and reports the after-fix token total, list-price estimate, provenance, and visible disclosure.
  • remove status: 📣 needs proof: Current PR status label is status: 👀 ready for maintainer look.
  • remove rating: 🦐 gold shrimp: Current PR rating is rating: 🐚 platinum hermit, so this older rating label is no longer current.

Label justifications:

  • P2: This is a substantial provider-usage improvement with limited blast radius, pending an owner product decision.
  • merge-risk: 🚨 compatibility: Existing Grok users would newly see a dollar figure, so its default semantics must be intentional.
  • rating: 🐚 platinum hermit: Overall readiness is 🐚 platinum hermit; proof is 🦞 diamond lobster and patch quality is 🐚 platinum hermit.
  • status: 👀 ready for maintainer look: ClawSweeper has no concrete contributor-facing blocker left for this PR. Sufficient (terminal): The changed production owner is the bounded Grok completed-turn scanner and snapshot publication path. The supplied exact-head native-session terminal transcript runs the focused suite against real local logs and reports the after-fix token total, list-price estimate, provenance, and visible disclosure.
  • proof: sufficient: Contributor real behavior proof is sufficient. The changed production owner is the bounded Grok completed-turn scanner and snapshot publication path. The supplied exact-head native-session terminal transcript runs the focused suite against real local logs and reports the after-fix token total, list-price estimate, provenance, and visible disclosure.

Evidence

What I checked:

Likely related people:

  • steipete: Suggested for follow-up; no historical authorship or introduction is verified. (role: unverified routing candidate; confidence: low)
  • Chipagosfinest: Suggested for follow-up; no historical authorship or introduction is verified. (role: unverified routing candidate; confidence: low)

Rank-up moves

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

  • Record the owner decision on whether the disclosed estimate is enabled by default.

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 (30 earlier review cycles; latest 8 shown)
  • reviewed 2026-08-25T08:55:43.508Z sha ff85e9a :: needs maintainer review before merge. :: none
  • reviewed 2026-08-25T22:40:46.822Z sha fcdffd1 :: needs maintainer review before merge. :: none
  • reviewed 2026-08-28T01:24:16.179Z sha cc5c319 :: needs changes before merge. :: [P2] Keep Grok scans on the dedicated utility executor
  • reviewed 2026-08-28T17:03:27.504Z sha fcd4152 :: needs real behavior proof before merge. :: [P2] Run Grok scans through the dedicated utility executor | [P2] Invalidate Pi caches when xAI rates change
  • reviewed 2026-08-28T17:18:04.876Z sha fcd4152 :: needs real behavior proof before merge. :: [P2] Recompute Grok cost for the requested history window
  • reviewed 2026-08-28T17:55:19.028Z sha 07d94e4 :: found issues before merge. :: [P2] Run Grok scans on the dedicated utility executor | [P2] Use an exact local-day cutoff for the maximum history window | [P2] Do not adopt Pi caches across the new xAI route
  • reviewed 2026-08-28T19:29:22.512Z sha 07d94e4 :: needs changes before merge. :: [P2] Run refreshable Grok scans on the dedicated executor | [P2] Use an inclusive local-day cutoff for Grok history | [P2] Invalidate Pi caches across the newly priceable xAI route
  • reviewed 2026-08-28T22:40:02.644Z sha 39e8e7c :: needs real behavior proof before merge. :: none

@olddonkey
olddonkey force-pushed the feat/grok-real-token-usage branch from 08360b5 to e3cd3b9 Compare August 22, 2026 06:21
@olddonkey olddonkey changed the title Report real Grok token usage and list-price cost from CLI session logs Report real Grok token usage and list-price cost, from the CLI logs and OpenCodex alike Aug 22, 2026
@clawsweeper clawsweeper Bot added merge-risk: 🚨 auth-provider 🚨 Merging this PR could break OAuth, tokens, provider routing, model choice, or credentials. rating: 🦐 gold shrimp Decent PR readiness signal, but merge confidence is limited. and removed rating: 🧂 unranked krab Not merge-ready due to missing proof or serious correctness/safety concerns. labels Aug 22, 2026
@olddonkey

Copy link
Copy Markdown
Contributor Author

Both automated findings are addressed, plus the review's other checklist items. The inline comments were left against 09cf7edb0, which no longer exists — the branch has since been rebased onto 27c7f334e and the head is now 03e5a25dc, so I'm summarising here rather than replying in a stale diff.

P1 — Preserve the Grok fallback on repeated probe failures

Fixed in 923193ec0, Sources/CodexBar/UsageStore+Refresh.swift. The guard had been hoisted into the if provider == .grok, publication == nil condition, so a Grok failure with a publication fell through to the generic else if tokenCostRequiresProviderSnapshot { clearTokenSnapshot } branch. Grok now owns its branch outright and can never reach the clear:

if provider == .grok {
    if self.tokenSnapshotPublicationForCurrentProviderConfig(for: provider) == nil {
        Task { @MainActor [weak self] in
            await self?.scanAndPublishGrokLocalTokenSnapshot(...)
        }
    }
} else if Self.tokenCostRequiresProviderSnapshot(provider) {
    self.clearTokenSnapshot(for: provider)
}

Regression coverage is in missing remote snapshot scans and publishes local tokens then clears empty data. Per the review's request it now drives two consecutive failing refreshes (03e5a25dc) rather than one — which matters here, because the first failure is what publishes through the fallback scan and only the second arrives with a publication in place, i.e. the failure that used to wipe the row. Both iterations assert the row still reads 77 tokens and that no redundant rescan ran.

P2 — Refresh pricing before scanning Grok sessions

Correct, and thank you — this was a genuine gap and not one the local tests would have surfaced. refreshPricingIfAllowed is gated to Codex and Claude, and Grok never reaches it at all because its snapshot comes from the provider probe rather than CostUsageFetcher.loadTokenSnapshot. On a machine with Codex or Claude also enabled the shared cache is already populated, so the failure is invisible there; enable only Grok and the catalog never appears and the Cost row shows tokens with no money, permanently.

Fixed in 744677e68. The Grok scan paths now request ModelsDevPricingPipeline.refreshIfNeeded through a summarizeRequestingPricingRefresh wrapper, called from all four scan sites (GrokStatusProbe, both branches in GrokProviderDescriptor, and UsageStore.scanAndPublishGrokLocalTokenSnapshot). It is detached rather than awaited, matching how the Codex and Claude paths already treat it — pricing availability must not delay or fail a local scan — and it is safe to call repeatedly, since it returns immediately unless the cache is stale and serialises through its own coordinator. summarize itself stays synchronous and side-effect free.

Note the inline comment still points at GrokLocalSessionScanner.swift:662; that line is the unchanged pricing lookup, and the fix is upstream of it in the new wrapper, so the anchor looks live even though it is addressed.

Coverage: absent models dev cache requests a background refresh, stale models dev cache requests a background refresh, and fresh models dev cache skips the background refresh. All three assert whether a refresh was requested through an injected transport — no test touches the network.

Real-session evidence

CODEXBAR_LIVE_GROK_CATALOG_PROOF=1 swift test --filter GrokXAISpendCatalogTests, against real local Grok CLI sessions, through the shipped code path:

catalog_source=grok
today_tokens=5043749
last_30_days_tokens=52696354
today_cost_usd=3.3471699999999993
window_cost_usd=49.353424
cost_provenance=listPriceEstimate
history_days=365
priced_days=4
token_days=4
daily_buckets=4
available_sources=grok

The same corpus on main reports 653K tokens and no cost. history_days=365 shows the requested window is honoured (it was pinned to 30). priced_days == token_days shows no day was silently left unpriced. The gated proof was extended in e3cd3b9ce to print cost, provenance and priced-day coverage, since tokens alone cannot evidence the half of this change that is about money.

Those figures were cross-checked against an independent reimplementation of the pricing formula over the same logs; the two agree to the cent.

Merge risk / branch state

Rebased onto current main (27c7f334e); the branch reports clean. Full suite on the head: 77/77 groups, 922 selections, 0 failures. swiftformat --lint and swiftlint --strict clean. Upstream CI green on the previous head including all three Linux builds.

One thing deliberately left undone: no CHANGELOG.md entry. 0.54.1 was finalized and there is no open Unreleased section, so I did not invent a version heading — happy to add one wherever you prefer.

@clawsweeper clawsweeper Bot added rating: 🧂 unranked krab Not merge-ready due to missing proof or serious correctness/safety concerns. rating: 🦐 gold shrimp Decent PR readiness signal, but merge confidence is limited. and removed rating: 🦐 gold shrimp Decent PR readiness signal, but merge confidence is limited. rating: 🧂 unranked krab Not merge-ready due to missing proof or serious correctness/safety concerns. labels Aug 22, 2026
@olddonkey

olddonkey commented Aug 22, 2026

Copy link
Copy Markdown
Contributor Author

Both new findings addressed at 44d79a95a.

P1 — Do not map every xAI log record to the Grok subscription

Agreed, and taken as specified rather than argued down. Routing on the prefix alone is right for the case that motivated this — traffic authenticated with the user's Grok account, which is what makes it consume SuperGrok quota — but it silently folds an API-key user's pay-as-you-go xAI spend into the subscription row. CodexBar already models the developer platform as its own xai provider precisely to keep those apart, so the old behaviour crossed a boundary the app deliberately maintains.

The usage log carries no per-record credential evidence; I checked every field emitted for xai rows (requestId, timestamp, provider, model, requestedModel, resolvedModel, usage, usageStatus, status, routeDecision, …) and there is nothing about auth, account or key. The signal that does exist is ~/.opencodex/config.json, which records authMode per provider.

So attribution now requires positive OAuth evidence:

  • xai routes to .subscription(.grok) only when its configured authMode is OAuth. Anything else returns .tokenOnly — the spend is real, it just belongs to no tracked subscription — rather than .unknown, which would read as "unrecognised provider".
  • Fail closed. A missing or malformed config, a providers block without xai, or an entry without authMode all count as no evidence and keep the records off the Grok row.
  • OpenCodexRouteDispatcher stays a pure function. The set of OAuth-backed provider ids is threaded in from the caller (OpenCodexUsageFanOutSpendDashboardSource), so the routing site never touches the filesystem and every existing caller and test that does not care about auth keeps working.
  • The gate applies only to xai. openai, kimi-coding, deepseek and opencode-go are untouched — changing them would be an unreviewed behaviour change for other providers — and a test pins that they ignore xAI auth state entirely.

Coverage: xai OAuth config routes to Grok, xai non OAuth config stays token only (parameterised over several non-OAuth values), xai routing fails closed without readable complete OAuth config, non xai subscription routes ignore xai auth state, plus fan-out cases proving the same entries land on the Grok row under an OAuth config and are absent under an API-key one. No test reads the developer's real ~/.opencodex; the home directory is injected.

docs/grok.md no longer claims this path cannot distinguish OAuth from API-key traffic, because it now can.

P2 — Republish the Grok snapshot after a missing catalog refreshes

I looked at this closely and am deliberately not adding a republish path. Reasoning, so you can overrule it if you disagree:

The refresh is fire-and-forget, so the scan that requests it returns whatever the cache currently holds — that part is accurate. But the parse cache stores parsed turns, not prices, so aggregation and pricing re-run on every summarize. The next Grok scan therefore prices against the refreshed catalog with no extra machinery, bounding the unpriced window to a single refresh cycle. That is the same behaviour Codex and Claude already have: refreshPricingIfAllowed dispatches into Task.detached and their current scan does not wait for it either.

The alternative — plumbing a completion signal back across the actor boundary into the @MainActor publication path — buys one refresh cycle of latency on first run, at the cost of a new cross-actor completion path in code that publishes user-visible spend. That trade looked disproportionate, and inconsistent with how the two established providers behave. I have recorded the reasoning as a comment at the call site rather than leaving it implicit, so the next reader does not have to re-derive it.

Happy to build it if you would rather have it.

Evidence

The attribution itself only becomes visible in the app: SpendDashboardSource.mergingOpenCodexInputs is what merges the fan-out into provider rows, and the CLI's cost command reports OpenCodex as its own source rather than routing it, so terminal output cannot show this path. The figures below are read off the freshly packaged build running against real local data, on a machine whose ~/.opencodex/config.json has "xai": { "authMode": "oauth" }; screenshots of both panes follow.

The two halves stay distinguishable in the UI, which makes the attribution legible rather than something you have to take on trust: the CLI goes through the responses API so its SKU is grok-4.6-build, while OpenCodex's records resolve to the bare grok-4.6 / grok-4.5 / grok-4.3. Both sit under the Grok provider.

model row source shown
grok-4.6-build Grok CLI session logs $50.52 · 54M
grok-4.6 OpenCodex $161.21 · 182M
grok-4.5 OpenCodex $4.54 · 5.5M
grok-4.3 OpenCodex $0.50 · 201K

Independently recomputing the same corpus agrees to the cent on both halves: 54,121,501 tokens / $50.52 for the CLI logs, and $166.25 across 1,520 OpenCodex xai records. The CLI half is reproducible by anyone on their own machine through the gated proof test (CODEXBAR_LIVE_GROK_CATALOG_PROOF=1), whose output is in the PR body.

The negative direction — API-key traffic staying off the Grok row — is covered by tests rather than a screenshot, since demonstrating it live would mean rewriting the machine's OpenCodex config.

State

Full suite on 44d79a95a: 77/77 groups, 922 selections, 0 failures. swiftformat --lint and swiftlint --strict clean. Rebased on 27c7f334e.

image image

@clawsweeper clawsweeper Bot added proof: 📸 screenshot Contributor real behavior proof includes screenshot evidence. proof: sufficient Contributor real behavior proof is sufficient. status: ⏳ waiting on author ClawSweeper has contributor-facing work open and is waiting for author action. and removed status: 📣 needs proof The PR needs real behavior proof before ClawSweeper can clear the contributor ask. labels Aug 22, 2026
@olddonkey olddonkey changed the title Report real Grok token usage and list-price cost, from the CLI logs and OpenCodex alike Report real Grok token usage and list-price cost from CLI logs Aug 23, 2026
@olddonkey

Copy link
Copy Markdown
Contributor Author

Addressed both current findings in 211e1977d and resolved the two review threads.

  • P1 / historical xAI attribution: removed current-config-based xAI → Grok routing. usage.jsonl has no request-time credential provenance, so xAI records now remain token-only until the producer can persist that evidence. Removed the config reader/plumbing and added dispatcher/fan-out regressions.
  • P2 / first pricing publication: when no models.dev artifact exists, the first Grok scan now awaits the initial best-effort refresh attempt before summarizing. A successful refresh prices the first returned snapshot; stale catalogs still price immediately and refresh in the background. Added a regression that writes the catalog during refresh and asserts the first summary is priced.
  • Updated the PR title/body and docs/grok.md so they no longer claim OpenCodex xAI traffic is merged into the Grok subscription row.

Validation on the exact pushed head:

  • focused Grok/OpenCodex suites: 32 tests passed
  • make check: passed
  • make test: 922 selections, 77/77 groups, 0 failed groups, 0 retries
  • branch is based on current main (27c7f334e) and the merge-tree is clean

@clawsweeper re-review

@clawsweeper

clawsweeper Bot commented Aug 23, 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.

@clawsweeper clawsweeper Bot added merge-risk: 🚨 availability 🚨 Merging this PR could cause crashes, hangs, restart loops, stalls, or process outages. and removed merge-risk: 🚨 compatibility 🚨 Merging this PR could break existing users, config, migrations, defaults, or upgrades. proof: sufficient Contributor real behavior proof is sufficient. proof: 📸 screenshot Contributor real behavior proof includes screenshot evidence. labels Aug 23, 2026
@clawsweeper

clawsweeper Bot commented Aug 25, 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.

@clawsweeper clawsweeper Bot added the proof: sufficient Contributor real behavior proof is sufficient. label Aug 25, 2026
@olddonkey

Copy link
Copy Markdown
Contributor Author

Rebased PR #3135 onto current upstream main (41d904fd6) and force-pushed exact head fcdffd1077147dc538ddd1412f37fac7336acacf.

  • Preserved the new main-thread safety work from Keep Grok session scans off the menu thread #3198: Grok local session I/O remains on the detached utility scan path, while failed remote refreshes still schedule the single-flight completed-turn refresh.
  • Migrated the latest-main Grok projection regression from metadata-only signals.json input to completed-turn updates.jsonl fixtures.
  • Regenerated the Codex parser hash (678fd59821eccb04) and added the previous main hash as a compatible predecessor; the adoption regression passes.
  • Scoped Grok parse-cache decode assertions to their own fixture tree so the full suite is not order-dependent.

Exact-head validation:

  • make check: passed; SwiftFormat 0/2002, SwiftLint 0 violations in 2001 files.
  • make test: 933/933 selections, 78/78 groups passed on the first attempt, 0 failures/retries/timeouts (648.5s).
  • Live native-session proof: 3/3 passed; 2,739,923 last-30-day tokens and $2.181282 list-price estimate, with listPriceEstimate provenance and the non-bill disclosure.
  • ./Scripts/package_app.sh: production build, widget packaging, signing validation, resource probes, and launch smoke passed; the exact worktree app remained running.
  • GitHub exact-head CI: all checks passed, including both macOS shards, Linux x64/ARM64/musl, lint, aggregate, and GitGuardian.

The PR body now carries the rebased exact-head evidence. ClawSweeper is already reviewing fcdffd107; the owner product decision on default disclosed estimate semantics remains unchanged.

@olddonkey

Copy link
Copy Markdown
Contributor Author

Rebased onto current upstream main (d94c71acf) and force-pushed exact head cc5c3191f. The branch is mergeable again.

Conflict resolution

  • GrokProviderDescriptor.swift: kept the injectable localSummary / cliVersion seams from fix(grok): bind usage to captured account credentials #3237 and moved this branch's 365-day lookback and models.dev pricing refresh into the injected defaults, so the credential-binding seam and the Grok scan behaviour both survive.
  • CostUsageStore.swift / CostUsageStoreTests.swift: regenerated the Codex parser hash to d0aabf812fd30f48 and recorded main's 21f10143afe00c55 as a compatible predecessor; dropped the stale branch-internal entry that was never on main.
  • ProviderArchitectureGatekeeperTests.swift: re-anchored 27 suppression and allowlist line numbers onto main's current SpendDashboardController, UsageStore, and PiSessionCostScanner layout.
  • CostUsagePricingTests.swift: restored byte-identical to main (see below).

Four issues the rebase surfaced that were not conflicts

  1. missing remote snapshot scans and publishes local tokens then clears empty data pinned its fixture to 2026-08-20 while the unstubbed publish path scans against the real clock through a seven-day window. It began failing today on its own; it is now anchored to Date() so it cannot expire again.
  2. main's Bailian tests (Add Bailian CLI token plan usage source #3080) plus this branch's xAI test pushed CostUsagePricingTests.swift past both the 1500-line file and 800-line type limits. The xAI pricing-fingerprint test moved next to the other Grok pricing tests, which leaves that file identical to main.
  3. The changelog entry for this PR had a net-zero history across earlier rebases and was absent. Restored under ## 0.55.2 — Unreleased.
  4. PiSessionCostScanner.pricingContext gates its reviewed predecessor keys on the exact current parser hash. Details below — this is the one item I would like you to confirm.

Pi cache predecessor gate — please review

Following the precedent in 5b8602981, I advanced the literal to d0aabf812fd30f48 and appended 21f10143afe00c55 to the reviewed list.

The compatibility basis: the only parser-hash-scoped change in this branch is CostUsagePricing.swift widening the Codex route allowlist from codexModelsDevProviderIDs to codexCompatibleModelsDevProviderIDs, and Pi maps sessions onto .codex / .claude only for the openai-codex and anthropic providers. Every Codex and Claude model identity a Pi cache already holds therefore prices identically under the new parser.

The bound I could not close: normalizeCodexModel does not strip a non-openai/ route prefix, so a Pi session that recorded an xai/-prefixed model under the openai-codex provider would keep its unpriced value in an adopted cache while a full rescan would now price it. I wrote that tradeoff into the gate comment rather than eliding it. If you would rather stay on the conservative side, say so and I will drop the gate advance and let this branch invalidate Pi caches normally.

Validation on exact head cc5c3191f

  • make check: passed; 0 SwiftFormat changes, 0 SwiftLint violations in 2036 files.
  • make test: 955/955 selections, 80/80 groups passed on the first attempt; 0 failures, 0 retries, 0 timeouts (959.8s).
  • GitHub CI: all checks green, including both macOS shards, Linux x64/ARM64/musl, lint, aggregate, and GitGuardian.

The four P1s and the P2 from your 2026-08-25 review are unchanged by the rebase and still in place.

@olddonkey

Copy link
Copy Markdown
Contributor Author

Rebased onto 0.56.0 main (fc1bd0d79) and force-pushed exact head fcd41524b. Also rewrote the PR description, which had gone stale.

Why the description changed

It still cited exact head fcdffd107, parser hash 678fd59821eccb04, and make test 933 selections / 78 groups — all superseded. Several P1 items cited commit SHAs that two rebases have since rewritten, so those citations are gone and the findings are described directly instead.

It also contained a contradiction worth calling out rather than quietly fixing: the body claimed the CHANGELOG.md entry had been removed as release-owned, while the branch now carries one. Removing it was my mistake — a normal PR in this repository does carry its entry. It is restored, and because 0.56.0 shipped without this change, it opens a new ## 0.56.1 — Unreleased section rather than landing in the released one. Note that main has no development section open yet; if you would rather open it yourself in a chore: commit, say so and I will drop mine.

Parser-hash bookkeeping for f8577be489f4c13d

#3250 moved the hash while this branch was in review, so all three gates were redone together: regenerated to ea03fe5a4da51ed0, f8577be489f4c13d added to CostUsageStore.compatiblePredecessorParserHashes and its exact-set assertion, and the Pi gate advanced with f8577be489f4c13d added to its reviewed list.

On the Pi gate specifically — #3250 did exactly what I had asked about in my previous comment (advance the literal, append the prior hash), so I have taken that as settled and kept your read-view rationale alongside this branch's xAI note in the same comment. The one bound I still cannot close is unchanged and is now stated in the PR body as well: a Pi session recording an xai/-prefixed model under the openai-codex provider would keep its unpriced value in an adopted cache while a full rescan would price it. Happy to drop the gate advance if you prefer the conservative side.

Validation on exact head fcd41524b

  • make check: passed — SwiftFormat clean, SwiftLint 0 violations in 2041 files.
  • make test: 957/957 selections, 80/80 groups on the first pass; 0 failures, 0 retries, 0 timeouts (759.3s).
  • ./Scripts/package_app.sh: production build, signing validation, resource probes, and launch smoke passed; packaged helper reports CodexBar 0.56.0, config validate returns Config: OK.
  • Rebased 0 behind / 21 ahead, git diff --check clean, GitHub reports MERGEABLE.

Two open questions remain yours: the default estimate semantics, and the Pi gate advance above.

@olddonkey

Copy link
Copy Markdown
Contributor Author

All three P2 findings from the latest ClawSweeper review are addressed on exact head 39e8e7ca9. Each fix carries a regression that I confirmed fails without it.

Run refreshable Grok scans on the dedicated executor

summarizeRequestingPricingRefresh now queues the synchronous corpus scan through CostUsageScanExecutor instead of calling the scanner inline, so the probe and descriptor callers no longer occupy the cooperative pool that executor exists to protect. A cancelled scan returns historyCoverageIsEstablished: false rather than an authoritative zero, preserving the "always returns a value" contract its callers document.

The unused fileManager parameter is gone: nothing passed a custom one, and FileManager is not Sendable, which is presumably why summarizeOffMainThread never took it either.

Regression: a blocker occupies the executor, and the Grok scan is asserted not to finish for 300 ms — an inline scan of that fixture completes in microseconds. Releasing the blocker yields the correct total.

Use an inclusive local-day cutoff for Grok history

The cutoff is now startOfDay(now) minus historyDays - 1, matching the window narrowed(toHistoryDays:) renders. Regression: with a three-day window, a turn on the day before the window was previously counted (660 tokens across two buckets) and is now correctly excluded (110 tokens, one bucket).

Invalidate Pi caches across the newly priceable xAI route

I took this one slightly differently from the literal suggestion, and want to flag that rather than bury it.

Rather than dropping the reviewed-predecessor gate outright, adoption is now gated per cache by cacheHoldsNewlyPriceableRoute, which scans daysByProvider, per-file contributions / unkeyedContributions, entryUsages, and lastModelContext for a model identity whose route prefix the widened allowlist now prices. Such a cache is rebuilt; a cache without one is still adopted.

The reasoning: reviewing a parser transition establishes that the cache shape is unchanged, which is a different claim from output equivalence. Gating on content proves equivalence per cache instead of assuming it, and it keeps the existing reviewed hash adoption preserves pi and omp parsing regression meaningful — dropping the gate would have forced that test to be inverted, and main would lose its adoption coverage.

If you would still rather have the blunt version (no adoption at all across this transition), say so and I will swap it; the content check is the only thing standing between the two.

Regression: a cache holding xai/grok-4.6 under the openai-codex provider, stamped with a reviewed predecessor key, is no longer adopted.

Validation on exact head 39e8e7ca9

  • make check: passed — SwiftLint 0 violations in 2042 files.
  • make test: 958/958 selections, 80/80 groups on the first pass; 0 failures, 0 retries, 0 timeouts (720.6s).
  • Rebased on 9769d7394, 0 behind / 22 ahead.

Still yours

The remaining merge-risk item is the owner decision: showing a non-billed xAI list-price dollar value by default is new product behavior without recorded approval. ClawSweeper's recommendation is to approve the disclosed estimate once the repair findings land, which they now have. I cannot record that decision on your behalf.

…on logs

two ways and expensive in a third.

Wrong tokens: the scanner summed `contextTokensUsed` from `signals.json`, which
is the session's ENDING context-window occupancy, not what it consumed. On a
real machine that reported 653K where actual consumption was 48.0M. Read the
sibling `updates.jsonl` instead, where every `turn_completed` event carries the
turn's real usage, and bucket by the per-line timestamp so a session crossing
local midnight lands in both days.

No cost: `toCostUsageTokenSnapshot` hardcoded nil dollars, and nothing could
have priced a Grok model anyway because `codexModelsDevProviderIDs` had no
`xai`. Add it, and resolve `grok-<version>-build` onto its base catalog model —
the `-build` suffix is an artifact of the responses-API surface, not a separate
SKU. `grok-build-0.1` is a real model and is never rewritten. Cost is the public
xAI card via models.dev, provenance `.listPriceEstimate`, so Grok stays
comparable with Claude and Codex. grok's own `costUsdTicks` is deliberately not
used for display.

A turn's `usage` is the aggregate of `modelCalls` API calls, so tiering on the
turn total would push nearly every multi-call turn into the >=200k bracket.
Price on the per-call average instead, in closed form over the two synthetic
call groups. This under-tiers slightly when context grows within a turn
(measured ~4% below the vendor's own accounting on a 27-turn sample, against
~+10% for aggregate tiering); the trade is documented at the call site and
pinned by a test.

Main-actor cost: the scan ran synchronously inside `@MainActor UsageStore` on
every menu-card build, refresh and dashboard load. It now reads the projection
the async probe already produced, and the remaining fallback scans on a
detached task with one scan in flight at a time. The probe projects the maximum
window and consumers narrow it, so `costUsageHistoryDays` and the dashboard's
365-day request are both honoured.

Hardening: `modelCalls` comes from a file, so it is validated before it can size
any work; parsing is cached per (path, size, mtime) with entries evicted when a
file is no longer visited; the cache lock is not held across file reads.

Note for upgraders: adding `xai` to `codexModelsDevProviderIDs` changes the
Codex pricing-cache key, so the first launch after this re-prices existing Codex
history once. Same one-time cost as when kimi and deepseek were added.
OpenCodex sends inference straight to api.x.ai using the Grok account's OAuth
credentials, so it burns the same SuperGrok subscription the Grok provider
reports on. It only spawns the `grok` binary to refresh tokens, so those
requests never reach ~/.grok/sessions and the local session scanner cannot see
them — 1,435 requests on one real machine that CodexBar attributed to nothing.

Route the `xai` provider prefix to the Grok subscription, the same way `openai`
already routes to Codex. Like that mapping, this routes on the prefix and does
not distinguish OAuth from API-key traffic. The `-build` suffix seen in the data
is a responses-API protocol artifact, not a separate billing pool, so traffic is
not split by it.

Routing alone would have produced tokens with no dollars. The aggregator priced
the bare `entry.model`, and a name without a route prefix is resolved against
the `openai` provider — which is why `gpt-5.6-sol` prices today and `grok-4.6`
resolved to `openai/grok-4.6` and missed. Qualify an unprefixed model with its
provider before pricing. Codex rows are unaffected (the qualified name resolves
to the same target), and providers outside the supported set keep returning nil.
Grok resolved list prices straight out of the cached models.dev catalog, but
nothing in its path ever fetched that catalog. The only fetch trigger is
CostUsageFetcher.refreshPricingIfAllowed, which is gated to Codex and Claude —
and Grok never reaches it at all, because its snapshot comes from the provider
probe rather than the shared token-cost pipeline.

On a machine where Codex or Claude is also enabled the cache is already there,
so this is invisible. Enable only Grok and the file never appears: every price
lookup returns nil and the Cost row shows tokens with no money, permanently.

Request ModelsDevPricingPipeline.refreshIfNeeded from the Grok scan paths. It is
safe to call repeatedly — it returns immediately unless the cache is stale and
serialises through its own coordinator — and it is detached rather than awaited,
matching how the Codex and Claude paths already treat it: pricing availability
must never delay or fail a local scan, and the next refresh fills in the value.

`summarize` stays synchronous and side-effect free; the refresh lives in a
wrapper so the parse-cache behaviour and existing tests are untouched.

Reported as P2 by the automated review on the pull request.
… proof

The opt-in live proof scanned real sessions but printed tokens only, which
cannot evidence the half of this change that is about money. It now also
reports today's and the window's list-price cost, the provenance, the window
actually used, and how many days carried a price versus tokens — so an
all-unpriced result is visible in the output instead of reading as zero.

Still skipped unless CODEXBAR_LIVE_GROK_CATALOG_PROOF=1.
The regression guard drove a single failing refresh after a local publication
existed. The defect it covers is specifically about the *second* failure: the
first one publishes through the fallback scan, and only the next one arrives
with a publication already in place — which is what used to hit the generic
clear branch. Drive the failure twice and assert the row and the scan count
both hold.
Routing every OpenCodex `xai` record to the Grok subscription is right for the
case that motivated it — traffic authenticated with the user's Grok account,
which is what makes it burn the SuperGrok quota. It is wrong for anyone using an
xAI API key: their pay-as-you-go developer-platform spend gets folded into the
subscription row, silently inflating it. CodexBar models that platform as its
own xAI provider precisely to keep the two apart.

The usage log carries no per-record credential evidence, so the decision has to
come from the OpenCodex provider config, which records `authMode` per provider.
Read it, and attribute to Grok only when that mode is OAuth; anything else is
token-only spend that belongs to no tracked subscription.

Fail closed: a missing or malformed config, no `xai` entry, or an absent
`authMode` all count as no OAuth evidence and keep the records off the Grok row.
The dispatcher stays a pure function — the set of OAuth-backed provider ids is
threaded in from the caller rather than read at the routing site — and the gate
applies only to `xai`, leaving the other routes exactly as they were.

Also records why the Grok pricing refresh stays fire-and-forget: the parse cache
holds parsed turns rather than prices, so the next scan reprices against the
refreshed catalog, and plumbing completion back to republish was judged
disproportionate to a delay Codex and Claude already share.

Raised as P1 by the automated review; the owner chose verifiable attribution
over prefix-only routing.
Rebase onto current main and reconcile the surfaces it moved:

- Route the Grok local summary through the injectable `localSummary`/`cliVersion`
  seams steipete#3237 introduced, keeping the 365-day lookback and the models.dev
  pricing refresh in the injected defaults rather than at the call sites.
- Regenerate the Codex parser hash and record main's `21f10143afe00c55` as a
  compatible predecessor; the Grok-only parser additions leave persisted Codex
  rows unchanged. Drop the stale branch-internal predecessor entry.
- Re-anchor the provider-architecture gatekeeper suppressions and allowlists to
  the line numbers main's Spend dashboard and usage store now sit at.
- Anchor the unstubbed Grok publish test to the real clock instead of a fixed
  calendar day, which had drifted outside its own seven-day window.
- Move the xAI pricing-fingerprint test next to the other Grok pricing tests so
  `CostUsagePricingTests.swift` stays byte-identical to main and inside the
  file- and type-length limits.
0.56.0 shipped without this change, so the entry belongs in the open
`0.56.1 — Unreleased` section rather than the released one.
- Queue the refreshable Grok summary through `CostUsageScanExecutor`. It called the
  synchronous corpus scanner inline, so the probe and descriptor callers ran a
  potentially multi-minute scan on the cooperative pool the executor exists to protect.
  A cancelled scan now reports unestablished coverage rather than an authoritative zero.
- Derive the Grok lookback cutoff from the local start of day minus `historyDays - 1`
  so the scan window matches the inclusive local-day window `narrowed(toHistoryDays:)`
  renders, instead of collecting a partial extra day consumers discard.

Each fix carries a regression that was confirmed to fail without it.
steipete#3259 moved the Codex parser hash to `d9a91f31d0addc15` and removed the Pi
cache's reviewed-predecessor adoption entirely, so this branch no longer carries
a Pi gate: `PiSessionCostScanner` and `PiSessionCostCompatibilityTests` are
byte-identical to main again, which is the conservative option the maintainer
offered on the previous head.

- Regenerate `CodexParserHash.value` to `ac4862abcdfe21a8`.
- Record main's `d9a91f31d0addc15` in `CostUsageStore.compatiblePredecessorParserHashes`
  and in its exact-set assertion.
@olddonkey

Copy link
Copy Markdown
Contributor Author

Rebased onto current upstream main (e0d2fd90b, the 0.56.1 development head) and force-pushed exact head eef38cd29ca08d98abd730ac1c3dd154359ae2d9. The branch is mergeable again: 23 ahead, 0 behind.

The Pi cache question is closed — by #3259, not by me

#3259 removed PiSessionCostScanner's reviewed-predecessor adoption entirely, so a parser-hash change now invalidates Pi caches normally and there is no gate left to advance or review.

I dropped this branch's whole Pi contribution accordingly, including the cacheHoldsNewlyPriceableRoute per-cache content check from the last head and its regression. PiSessionCostScanner.swift and PiSessionCostCompatibilityTests.swift are byte-identical to main on this head. That is exactly the conservative alternative you were offered on the previous head, so the second maintainer decision item is gone from the description; only the default estimate-semantics call remains.

Conflict resolution

  • CHANGELOG.md: kept main's ### Performance entry and placed this branch's entry in a new ### Usage & Spend section under the same 0.56.1 — Unreleased heading.
  • PiSessionCostScanner.swift, PiSessionCostCompatibilityTests.swift: resolved to main (see above).
  • ProviderArchitectureGatekeeperTests.swift: the four conflicted anchors resolved to main's current line numbers with no branch-local offsets; the suite passes 39/39.
  • CostUsageStore.swift / CostUsageStoreTests.swift: restored to main first, then re-applied only the new predecessor entry. The previous head had drifted into reordering 3c984b655688593f and rewriting its comment, which was pure noise against main; that is gone.

Parser-hash bookkeeping

Two gates now instead of three:

  • CodexParserHash.value regenerated to ac4862abcdfe21a8.
  • CostUsageStore.compatiblePredecessorParserHashes records main's d9a91f31d0addc15, with the exact-set assertion in CostUsageStoreTests kept in sync.

I also rewrote two commit messages that still described the dropped Pi gate, so the history matches what the commits actually contain.

Validation on exact head eef38cd29

  • make check: passed — SwiftFormat clean, SwiftLint 0 violations in 2055 files, provider/package/documentation gates green.
  • make test: passed — 965/965 selections, 81/81 groups successful on the first pass, 0 failed groups, 0 retries, 0 timeouts (666.0 s).
  • Focused: ProviderArchitectureGatekeeperTests 39/39, plus 135 tests across the Grok scanner/pricing/menu/dashboard, OpenCodex routing/fan-out, CostUsageStoreTests and PiSessionCostCompatibilityTests.
  • The opt-in local-session proof was re-run on this head rather than carried over: 3/3 passed, 2,739,923 last-30-day tokens, window_cost_usd=2.181282, cost_provenance=listPriceEstimate, and the visible Public xAI list-price estimate · not a bill. disclosure. It reads local session logs only — no credentials, no network probe, no Keychain access. The description no longer carries an earlier-head evidence caveat.

git diff --check is clean and CI is running on this head.

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. proof: sufficient Contributor real behavior proof is sufficient. rating: 🐚 platinum hermit Good normal PR readiness with ordinary maintainer review expected. status: 👀 ready for maintainer look ClawSweeper has no concrete contributor-facing blocker left for this PR.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants