Skip to content

Port upstream 0.67.0: add CostReportingPeriod core and range-aware cost scanners - #648

Closed
Finesssee wants to merge 3 commits into
port/upstream-0.67.0from
port/micro-0.67.0-cost-reporting-period-core
Closed

Finesssee wants to merge 3 commits into
port/upstream-0.67.0from
port/micro-0.67.0-cost-reporting-period-core

Conversation

@Finesssee

@Finesssee Finesssee commented Sep 29, 2026 •

Copy link
Copy Markdown
Collaborator

Summary

Adds the core cost reporting period model, the pinned cost time zone, and range-aware local cost scanners. This is part A of upstream 0.67.0 item 1. Nothing user-visible changes in this PR: the saved default is rolling:30, CostScanner::new(days) keeps its exact rolling behavior, and history stays bucketed in the machine zone until a caller applies a saved zone. The CLI --period and serve wiring (#653) and the desktop wiring (#665) are separate follow-up PRs.

  • New rust/src/cost_reporting_period.rs: CostReportingPeriod = Rolling(1..=365) | MonthToDate | AllAvailable, raw forms rolling:N, month-to-date, all. bounds(now, tz, earliest), days, entries filtering and identity(now, tz) (raw|tz|start|end) for cache keys. Month and day arithmetic is done on calendar dates, so leap years and 23/25 hour DST days are exact. Legacy integer selections migrate unchanged (migrated).
  • Settings.cost_reporting_period (settings.rs, settings/raw.rs), default rolling:30. A missing or unreadable value loads as the default.
  • CostScanner holds a period instead of days. new(days) is for_period(Rolling(days)). A new cost_scanner/window.rs resolves the period into scan windows.
  • Codex, Claude, Pi, OpenCode Go, Antigravity and Muse scanners take the resolved window. The 365 clamps in OpenCode Go, Antigravity and Muse now use the shared clamp_window_days (1..=36,500), so All is not clamped to 365.
  • Codex All skips years before the first existing YYYY partition (first_codex_partition_date), so the date-partition walk does not probe empty history. The existing range-keyed Codex cache already covers wider windows.
  • Bedrock: new daily_range(since, now); month to date is the current UTC month start, All is the current month plus 13 months, end is tomorrow (Cost Explorer buckets are UTC).
  • Cursor billing-cycle quota is untouched.

Pinned cost time zone (upstream tokenCostUsageBucketTimeZone)

  • Settings.cost_usage_bucket_time_zone: an IANA zone name; empty means the machine zone. Values are trimmed on load and anything that is not an IANA zone reads as empty, like the upstream setter.
  • settings/cost_time_zone.rs (new):
    • pin_cost_usage_bucket_time_zone() saves the machine zone when nothing valid is saved and returns whether the caller must save. When the system zone cannot be read safely it pins nothing, so the UTC fallback is never persisted.
    • apply_cost_usage_bucket_zone() applies the saved zone to the process.
  • cost_reporting_period.rs:
    • cost_bucket_zone() / set_cost_bucket_zone() hold the process-wide zone; the default is the machine zone.
    • CostTimeZone::from_identifier, is_valid_identifier and pin_identifier mirror upstream CostUsageBucketTimeZone.
    • core::try_local_timezone_name() returns None instead of the fallback.
  • Callers, as upstream:

Each local source buckets days according to the calendar upstream gives it:

Source Calendar used Upstream reference
Codex scanner: day keys, today, scan windows, pending-scan context Pinned zone CostUsageFetcher options.calendar
Claude and Pi: day keys, windows Pinned zone CostUsageFetcher options.calendar
Codex quota-window slices, Codex workspaces index Pinned zone Codex cache calendar
Muse: day keys, window Pinned zone MuseLocalUsageCache
Antigravity window Pinned zone fallbackOptions.calendar
OpenCodex daily buckets and window Pinned zone aggregator bucketCalendar
serve dashboard "today" Pinned zone CLIServeCommand
Grok Machine calendar GrokLocalSessionScanner uses Calendar.current
OpenCode Go Machine calendar OpenCodeGoLocalUsageReader
OpenCodex weekday/hour activity cells Local display buckets

Cache behavior:

  • Codex: the cache records bucket_time_zone (upstream timeZoneIdentifier). A cache bucketed in a different zone is rebuilt, and status reads ignore it. Caches written before the stamp existed are kept, because they were bucketed in the machine zone, which is what first launch pins.
  • Muse: the cache stamp changes from the UTC offset to the zone identifier, so existing Muse caches rescan once.
  • Claude and Pi: Windows does not persist day caches for them; they rescan transcripts each time, so there is nothing to stamp. Upstream stamps its Claude cache the same way as Codex.

Upstream reference

Ported / Deferred

Ported: everything listed in the summary.

Deferred, with the follow-up that owns it:

Intentional divergences from upstream:

  • No settings control for the bucket zone. Upstream v0.67.0 has no picker either.
  • Cursor: Windows local Cursor usage stays UTC-based (its CSV dates are UTC). Upstream buckets Cursor in the pinned calendar.
  • Claude and Pi rolling windows keep their legacy shape: a now - N*24h cutoff and a UTC today - N reported start. Month to date and All use pinned-zone midnights.
  • No calendarIdentifier stamp. Windows always buckets with the Gregorian calendar.

Validation

Run with +1.98.0 on E-cores (head 72fafbfa):

  • cargo fmt --all --check: clean
  • cargo clippy --workspace --all-targets -- -D warnings: pass
  • cargo test -p codexbar: 2195 passed, 0 failed, 1 ignored (unit); main 1 passed; doc 0
  • cargo test -p codexbar-desktop-tauri: 461 passed, 1 failed. The failure is commands::tests::bootstrap_payload_exposes_every_provider_variant, which reads host settings; it is known and fixed by Make the bootstrap catalog test hermetic (#684) #711. The Tauri crate is not modified here.
  • New tests:
    • cost_reporting_period: upstream month table, leap year, DST, identity, bounds, migration, entries, serde, plus saved-zone resolution, the pin identifier and the process-wide bucket zone.
    • settings::cost_time_zone: normalization, settings file round trip, pinning.
    • core::jsonl_scanner cache_zone: zone mismatch rebuild, legacy unstamped cache kept, save stamp, status ignores another zone.
    • cost_scanner/tests/period.rs: window shapes, first partition, Codex All beyond a year, Codex MTD excludes previous month.
    • Bedrock daily_range.
  • The two exact-size cache tests now measure the stamped artifact, since saving adds the zone.
  • No file crossed from under 1000 lines to over 1000.

Affected areas

  • New files: rust/src/cost_reporting_period.rs, cost_scanner/window.rs, settings/cost_time_zone.rs.
  • Scanners: rust/src/cost_scanner.rs, cost_scanner/codex*, pi_session_cost.rs, codex_costs.rs, codex_costs/quota_windows.rs, codex_workspaces/indexer.rs.
  • Core: core/jsonl_scanner* (Codex cache zone stamp), core/timezone.rs.
  • Settings: settings.rs, settings/raw.rs.
  • Other: spend_contract/opencodex.rs, cli/serve/dashboard/source.rs, and providers opencodego, muse, antigravity, bedrock.

There are no Tauri or frontend changes and no new dependencies.

UI proof

Not applicable. No UI, tray, settings surface, float bar or theme change.

@coderabbitai

coderabbitai Bot commented Sep 29, 2026 •

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Advanced

Run ID: a8775d98-4b31-4671-a32f-cfd9ac1b539e

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
  • Autopilot · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Autopilot is currently an internal CodeRabbit preview.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@Finesssee

Copy link
Copy Markdown
Collaborator Author

Reviewed by Codex gpt-6-luna (xhigh); verified and validated by Claude

Thermo-nuclear review findings:

  • P2 rust/src/cost_reporting_period.rs: a non-string cost_reporting_period value (number, object) made deserialization fail for the whole settings file, so settings loading could discard unrelated preferences. Fixed: unreadable values now read as the default rolling:30, and the tests cover 42 and an object.

No other findings.

@Finesssee

Copy link
Copy Markdown
Collaborator Author

The P2 finding is fixed and pushed (rust/src/cost_reporting_period.rs plus test cases). Nothing left open.

Commands: cargo +1.98.0 fmt --all; clippy --manifest-path rust/Cargo.toml --all-targets -- -D warnings clean; cargo test cost_reporting 13 passed; cargo test settings run on the rust crate. Rust crate only, no frontend or Tauri change.

Port upstream 0.67.0 tokenCostUsageBucketTimeZone: a saved IANA zone (settings cost_usage_bucket_time_zone) buckets local cost history for Codex, Claude, Pi, Muse, Antigravity and OpenCodex, so day keys and month-to-date bounds survive a machine zone change. Codex caches record the zone and rebuild when it changes; unstamped caches are kept. Grok and OpenCode Go stay on the machine calendar as upstream.
@Finesssee

Copy link
Copy Markdown
Collaborator Author

Lane B review: fixes at 72fafbf

I reviewed this PR against upstream v0.67.0: CostReportingPeriod.swift, CostProvenance.swift (CostUsageBucketTimeZone), SettingsStore / SettingsStore+Defaults, CostUsageFetcher.swift, the vendored CostUsageScanner / CostUsageCacheModels / CostUsageClaudeCache, MuseLocalUsageCache.swift, the Grok and OpenCode Go readers, and CLICostCommand / CLIServeCommand.

Gap fixed in this push: the PR deferred the pinned cost time zone, but the 0.67.0 bullet requires it ("keep rolling windows and the pinned cost time zone"). Upstream buckets local cost history in a saved zone, so day keys and month-to-date bounds do not move when the machine zone changes. This push ports it:

  • Settings. Settings.cost_usage_bucket_time_zone matches upstream tokenCostUsageBucketTimeZone. Values are trimmed on load, and an invalid zone reads as empty, like the upstream setter.
    • pin_cost_usage_bucket_time_zone() matches upstream pinCostUsageBucketTimeZoneIfNeeded. It never persists the UTC fallback when Windows cannot report its zone.
    • apply_cost_usage_bucket_zone() applies the saved zone.
  • Process-wide zone. cost_bucket_zone() defaults to the machine zone, so this PR alone changes no behavior. The desktop pins and applies it at startup in Port upstream 0.67.0: History window setting and period-aware spend surfaces (stacked on #648) #665; the CLI applies it without pinning in Port upstream 0.67.0: cost --period and period-aware serve /cost (stacked on #648) #653, as upstream.
  • Sources moved to the pinned zone. These sources follow upstream's pinned calendar:
    • Codex: day keys, today, windows, pending-scan context, quota-window slices, workspaces index
    • Claude and Pi: day keys
    • Muse
    • Antigravity
    • OpenCodex daily buckets
    • the serve dashboard's today
  • Sources left on the machine calendar. Grok and OpenCode Go stay on the machine calendar, as upstream does (Calendar.current).
  • Cache stamps.
    • The Codex cache now records bucket_time_zone (upstream timeZoneIdentifier). A cache from another zone is rebuilt, and status reads ignore it. Unstamped legacy caches are kept, because they were bucketed in the machine zone that first launch pins.
    • The Muse cache stamp is now the zone identifier instead of the UTC offset.
  • Tests.
    • New: saved-zone resolution, the pin identifier, settings normalization and pinning, and Codex cache zone rebuild, keep, stamp and status (core/jsonl_scanner/tests/cache_zone.rs).
    • Updated: the two exact-size cache tests now measure the stamped artifact.

Divergences, listed in the PR body:

  • No zone picker; upstream has none either.
  • Windows Cursor CSV stays UTC.
  • The OpenCodex weekday/hour heatmap stays in local time.
  • Claude and Pi rolling windows keep their legacy UTC reported start.
  • There is no calendarIdentifier stamp; the Gregorian calendar is the only one used.

Validation (+1.98.0):

  • cargo fmt --all --check: clean.
  • cargo clippy --workspace --all-targets -- -D warnings: pass.
  • cargo test -p codexbar: 2195 passed, 0 failed, 1 ignored.
  • cargo test -p codexbar-desktop-tauri: 461 passed, 1 failed. The failure is bootstrap_payload_exposes_every_provider_variant, which reads host settings; it is known and fixed by Make the bootstrap catalog test hermetic (#684) #711.

UI proof: not applicable, because nothing in the UI changes.

Finesssee added a commit that referenced this pull request Oct 2, 2026
Finesssee added a commit that referenced this pull request Oct 2, 2026
Finesssee added a commit that referenced this pull request Oct 2, 2026
@Finesssee

Copy link
Copy Markdown
Collaborator Author

Adversarial validation passed at 545d39d

Scope: add CostReportingPeriod core and range-aware cost scanners (#648, upstream 0.67.0), validated as merged into release/v0.70.0 (merge 545d39d).

Attacks (highest-risk semantics, from the merged tree):

  • Bounds arithmetic is DST-safe: bounds(now, tz) for MonthToDate uses calendar arithmetic (first of the month 00:00 in the cost time zone through today), not 24 h multiplication — the audit's exact requirement for leap years and 23/25-hour DST days. The pinned cost-bucket-zone tests (process-wide cost_bucket_zone, per-provider calendars) exercise a zone where the DST transitions actually move the boundary.
  • Identity string is cache-safe: identity(now, tz) = raw|tz|start|end covers all inputs the cache key depends on, so a changed tz or period can't reuse a stale cache — the cache-key tests cover both axes.
  • Migration path: an old Some(days) settings value migrates to rolling:N unchanged, covered by the settings test family; missing key defaults to 30.
  • Range-aware scanners do not re-derive their own window: the scanner tests assert each provider's scan respects the passed day range rather than re-deriving from now — the Grok and OpenCode Go machine-calendar exceptions are pinned as the audit lists them.
  • Windows-specific: the cost-bucket zone is process-wide, and the Codex cache zone stamp is recorded per file so a tz change invalidates only the affected provider's cache — covered by the stamp tests.

No defects found. READY for the un-draft rule.

@Finesssee

Copy link
Copy Markdown
Collaborator Author

Shipped in v0.70.0: this PR's head is included in main via #735 (merge commit 9d0a37a). Closing as integrated.

@Finesssee Finesssee closed this Oct 3, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant