Skip to content

refactor(adapter): unify JSONL parsing with typed structs and a shared helper - #1327

Merged
ryoppippi merged 13 commits into
mainfrom
refactor/unify-adapter-jsonl-parsing
Jun 15, 2026
Merged

ryoppippi merged 13 commits into
mainfrom
refactor/unify-adapter-jsonl-parsing

Conversation

@ryoppippi

@ryoppippi ryoppippi commented Jun 15, 2026 •

Copy link
Copy Markdown
Member

Summary

Unifies how the agent adapters parse their log files and brings every adapter onto the same optimized fast path. Previously the optimization level was inconsistent: the Claude loader read whole files as bytes, prefiltered lines with memmem, and deserialized into typed structs, while most other adapters parsed every line into a dynamic serde_json::Value and hand-navigated it with Value::get (and several allocated a String per line via BufReader::lines()).

Supersedes #1326 — folds that PR's LinePrefilter (multi-marker AND/OR prefilter) into this broader typed-parsing refactor.

What changed

  • New adapter/jsonl.rs:
    • records::<T>(content, prefilter) — iterate byte-lines (no per-line String allocation), skip lines rejected by a reusable LinePrefilter before any JSON parsing, then from_slice survivors into a typed struct (unused fields skipped, no intermediate Value tree).
    • lenient_u64 / lenient_i64 / lenient_f64 / non_empty_string — deserialize_with helpers that exactly reproduce Value::as_u64 / as_i64 / as_f64 / non_empty_json_string leniency, so typed structs tolerate unexpectedly encoded fields instead of failing the whole line.
  • New fast::LinePrefilter (from perf(adapter): unify JSONL prefilter and byte_lines across agents #1326): builds the memmem needles once and supports "contains all markers" / "contains any marker" modes. Restores precise prefilters: pi (usage + message), kimi (StatusUpdate + token_usage), openclaw (any of model_change / model-snapshot / usage, previously unfiltered), qwen, copilot.
  • Converted adapters to typed #[derive(Deserialize)] structs + the shared helpers: amp, codebuff, copilot, gemini, goose, kilo, kimi, openclaw, opencode, pi, qwen. (Claude and Codex already used typed parsing.)
  • A few polymorphic / multi-alias leaves are intentionally kept as Value where typing them would change numeric coercion or drop records (documented inline).

Why

  • Consistent performance across all agents (byte-line reads, precise prefilter, no full Value tree per line).
  • One shared, well-tested implementation of the parse loop, the prefilter, and the lenient field accessors instead of per-adapter copies.

Behavior parity

Output is preserved exactly:

  • Lenient deserialize_with helpers match the old Value accessor semantics.
  • Prefilter markers never drop an accepted line.
  • Parse / non-object handling keeps the same skip-vs-error outcomes. Droid is intentionally left on its existing path (no JSONL hot path; typing it would add edge divergences with no benefit).

Verified --json output for daily/monthly/weekly/session is byte-for-byte identical to both the previous main build and ccusage@latest, for every agent with local logs (claude, codex, amp, copilot, gemini, opencode, pi). Adapters without local logs (codebuff, droid, goose, kilo, kimi, openclaw, qwen, hermes) are covered by the existing fixture-backed tests.

Note: adapters now read files as bytes (fs::read) rather than fs::read_to_string; a file with invalid UTF-8 is now skipped gracefully per-line instead of failing the whole command — a minor, intentional robustness improvement consistent with the Claude loader.

Testing

  • cargo test -p ccusage — 276 passed
  • cargo clippy -p ccusage — clean
  • cargo fmt --check — clean
  • Manual output parity diff vs main and ccusage@latest (see above)

Summary by CodeRabbit

Release Notes

  • New Features

    • Added newline-log prefiltering to skip irrelevant records before full parsing, improving import speed.
  • Refactor

    • Improved robustness of usage-log import across multiple adapters by moving to typed, lenient JSON/JSONL parsing with shared ingestion helpers.
    • Updated file ingestion to read raw data and decode directly for more consistent handling of missing/optional fields.
  • Bug Fixes / Tests

    • Strengthened parsing to ignore malformed or unexpected elements (including empty/malformed ledgers, non-object entries, and invalid cost shapes) without failing the whole import.
    • Added unit test coverage for these scenarios.

Convert the agent adapters from dynamic `serde_json::Value` parsing plus
hand-written `Value::get` navigation to typed `#[derive(Deserialize)]`
structs parsed through the shared `adapter::jsonl` helper. Previously each
adapter built a full `Value` tree per line (and several allocated a
`String` per line via `BufReader::lines()`); they now read the file once,
prefilter lines with `memmem`, and deserialize only the fields ccusage
consumes.

Converted: amp, codebuff, copilot, gemini, goose, kilo, kimi, openclaw,
opencode, pi, qwen. Claude and Codex already used typed parsing.

Behavior is preserved exactly: the lenient `deserialize_with` helpers match
the old `Value` accessor semantics, prefilter markers are chosen to never
drop an accepted line (or `None` where no always-present substring exists),
and parse/non-object handling keeps the same skip-vs-error outcomes.
Adapters with no line-delimited JSON hot path (single-object or SQLite
sources) reuse only the leniency helpers. Droid is intentionally left on
its existing path: it has no JSONL hot path, so typing it would add edge
divergences with no performance benefit.

Verified output parity (daily/monthly/weekly/session, --json) against both
the previous main build and `ccusage@latest` for every agent with local
logs (claude, codex, amp, copilot, gemini, opencode, pi): byte-for-byte
identical.
@pullfrog

pullfrog Bot commented Jun 15, 2026 •

Copy link
Copy Markdown
Contributor

no API key found — this repo is configured to use deepseek/deepseek-v4-pro, which needs DEEPSEEK_API_KEY, but the runner has no key for it.

To fix: add the key as a GitHub Actions secret (referenced from your workflow's env: block) or as a Pullfrog secret in the console — or switch this repo to a different model (free models need no key).

Open repo secrets → · Configure model → · Setup docs → · Ask in Discord →

Pullfrog  | Rerun failed job ➔ | View workflow run | via Pullfrog | Using DeepSeek Pro (free via Pullfrog for OSS) | 𝕏

…refilter

Adopt the `LinePrefilter` helper (originally proposed in #1326) so the
typed `jsonl::records` path can prefilter on multiple markers with AND/OR
semantics instead of a single substring. `records` now takes
`Option<&LinePrefilter>`.

This restores the precise prefilters that single-marker filtering had to
drop:

- pi: require both `"usage"` and `"message"` (was `"usage"` only)
- kimi: require both `"StatusUpdate"` and `"token_usage"` (was
  `"token_usage"` only)
- openclaw: admit any of `"model_change"` / `"model-snapshot"` / `"usage"`
  (previously had no prefilter at all)
- qwen / copilot: unchanged markers, now via LinePrefilter

`LinePrefilter` builds the `memmem` needles once and reuses them across
every line. Output remains byte-for-byte identical (verified against
ccusage@latest for all agents with local logs). Supersedes #1326 by folding
its prefilter into the typed-parsing refactor.
@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Jun 15, 2026 •

Copy link
Copy Markdown

Deploying with  Cloudflare Workers  Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

Status Name Latest Commit Preview URL Updated (UTC)
✅ Deployment successful!
View logs
ccusage-guide 6eb56ca Commit Preview URL

Branch Preview URL
Jun 15 2026, 04:21 PM

@coderabbitai

coderabbitai Bot commented Jun 15, 2026 •

Copy link
Copy Markdown

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 79f1c9c3-0be7-4cc4-97eb-082304b94f4a

📥 Commits

Reviewing files that changed from the base of the PR and between ef4a485 and 011a3ed.

📒 Files selected for processing (2)
  • rust/crates/ccusage/src/adapter/claude/mod.rs
  • rust/crates/ccusage/src/adapter/openclaw/parser.rs
✅ Files skipped from review due to trivial changes (1)
  • rust/crates/ccusage/src/adapter/claude/mod.rs
🚧 Files skipped from review as they are similar to previous changes (1)
  • rust/crates/ccusage/src/adapter/openclaw/parser.rs

📝 Walkthrough

Walkthrough

A new shared jsonl adapter module and LinePrefilter utility are introduced, then applied uniformly across all adapter parsers (amp, codebuff, copilot, gemini, goose, kilo, kimi, openclaw, opencode, pi, qwen). Each adapter replaces manual serde_json::Value field traversal with #[derive(Deserialize)] structs, switches file reads to bytes, and uses the shared jsonl::records iterator with optional prefiltering to skip irrelevant lines before deserialization.

Changes

Typed serde refactor across adapter parsers

Layer / File(s) Summary
LinePrefilter and shared jsonl module
rust/crates/ccusage/src/fast.rs, rust/crates/ccusage/src/adapter/jsonl.rs, rust/crates/ccusage/src/adapter/mod.rs
LinePrefilter with precompiled memmem::Finder needles (All/Any modes) is added to fast.rs. A new adapter/jsonl.rs module provides the records iterator (byte-split → prefilter → typed deserialization, silently skipping failures) and seven lenient serde helpers (lenient_u64, lenient_i64, lenient_f64, lenient_object, lenient_array, lenient_vec, non_empty_string). Module registered in adapter/mod.rs.
codebuff, goose, kilo, and kilo/loader typed refactors
rust/crates/ccusage/src/adapter/codebuff/parser.rs, rust/crates/ccusage/src/adapter/goose/parser.rs, rust/crates/ccusage/src/adapter/kilo/parser.rs, rust/crates/ccusage/src/adapter/kilo/loader.rs
codebuff: load_chat_file switches to fs::read + from_slice. goose: adds GooseModelConfig struct and simplifies parse_goose_model_config. kilo: introduces KiloMessage with nested KiloTokens/KiloCache/KiloTime; message_value_to_entry accepts &KiloMessage and extracts usage from typed fields; kilo/loader's SQLite path deserializes into KiloMessage; tests updated.
opencode parser and loader typed refactor
rust/crates/ccusage/src/adapter/opencode/parser.rs, rust/crates/ccusage/src/adapter/opencode/loader.rs
opencode introduces OpenCodeMessage with nested OpenCodeTokens/OpenCodeCache/OpenCodeTime; message_value_to_entry signature updated to accept typed message and extract usage from typed fields; both SQLite and file-based loaders deserialize into OpenCodeMessage; tests refactored with local message(...) deserializer helper.
pi parser typed refactor
rust/crates/ccusage/src/adapter/pi/parser.rs
Introduces PiLine/PiMessage/PiUsage/PiCost structs with lenient cost deserialization; read_session_file reads bytes with LinePrefilter for "usage"/"message" and uses typed jsonl::records; extraction logic updated to use typed field accessors; is_pi_message_usage accepts &PiLine and validates using typed fields.
qwen parser typed refactor
rust/crates/ccusage/src/adapter/qwen/parser.rs
Introduces QwenLine and QwenUsageMetadata structs with lenient numeric deserialization; read_chat_file switches to whole-file read with LinePrefilter for usageMetadata and uses jsonl::records; parse_line accepts typed record, filters by role, extracts token usage from typed fields; timestamp/session_id/model derivation uses typed fields with fallbacks.
openclaw parser typed refactor
rust/crates/ccusage/src/adapter/openclaw/parser.rs
Introduces OpenClawLine/OpenClawMessage/OpenClawUsage/OpenClawCost with lenient nested deserializers; parse_session_file switches to full-file read with LinePrefilter + typed jsonl::records; new is_model_change helper; parse_message_entry refactored to accept typed OpenClawLine; timestamp/model/provider extraction updated; tests refactored for typed records.
kimi parser typed refactor
rust/crates/ccusage/src/adapter/kimi/parser.rs
Introduces KimiConfig/KimiWireLine/KimiWireMessage/KimiWirePayload/KimiTokenUsage typed structs; read_wire_file switches to byte-read with LinePrefilter and jsonl::records; read_model_from_config deserializes into typed struct; wire_line_to_entry accepts typed record, filters via typed fields, extracts from token_usage struct.
copilot parser typed refactor
rust/crates/ccusage/src/adapter/copilot/parser.rs
Introduces CopilotRecord struct with serde renames and optional fields; parse_otel_file switches to byte-read with LinePrefilter for "attributes" and uses jsonl::records; downstream parsing (trace contexts, span classification, ID extraction, timestamp resolution) updated to operate on typed CopilotRecord; all predicates and helpers rewritten for typed access.
amp parser typed refactor
rust/crates/ccusage/src/adapter/amp/parser.rs
Introduces AmpThread/AmpUsageLedger/AmpLedgerEvent/AmpMessage with camelCase mapping and lenient custom deserializers; read_thread_file reads bytes, deserializes into AmpThread with message fallback when ledger unusable; ledger and message parsing functions accept typed slices; cache-token lookup operates on &[AmpMessage]; tests verify fallback and non-object element skipping.
gemini parser typed refactor
rust/crates/ccusage/src/adapter/gemini/parser.rs
Introduces GeminiRecord envelope with lenient string handling and accessor methods (session_id(), stats()); parse_json_file deserializes entire file and branches by messages[], envelope type, or stats(); parse_jsonl_file switches to byte-read with typed jsonl::records::<GeminiRecord> and id-based deduplication; new parse_direct_event_record extracts tokens and delegates to build_event.

Sequence Diagram(s)

sequenceDiagram
  participant FileSystem
  participant Adapter
  participant LinePrefilter
  participant jsonl_records
  participant serde_json
  participant Parser

  FileSystem->>Adapter: fs::read(file)
  Adapter->>Adapter: Create LinePrefilter::all/any(markers)
  Adapter->>jsonl_records: records::<T>(bytes, Some(prefilter))
  
  loop For each line in bytes
    jsonl_records->>Adapter: Split by newline
    jsonl_records->>LinePrefilter: matches(line)
    alt Line matches prefilter
      LinePrefilter-->>jsonl_records: true
      jsonl_records->>serde_json: from_slice::<T>(line)
      alt Parse succeeds
        serde_json-->>jsonl_records: Ok(T)
        jsonl_records-->>Parser: Yield T
      else Parse fails
        serde_json-->>jsonl_records: Err (skip silently)
      end
    else Line does not match
      LinePrefilter-->>jsonl_records: false (skip)
    end
  end
  
  Parser->>Parser: Process typed T records
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

  • ccusage/ccusage#1326: Both PRs refactor the same JSONL ingestion path by introducing/using fast::LinePrefilter and updating agent parsers (notably copilot/parser.rs::parse_otel_file and fast.rs) to filter byte lines before parsing.

Poem

🐇 No more Value::get for me,
typed structs now set my data free!
A LinePrefilter skips the noise,
lenient_u64 finds its poise.
Across all adapters, clean and bright —
serde's derive makes parsing right! ✨

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and specifically describes the main change: unifying JSONL parsing across adapters by introducing typed structs and a shared helper module.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch refactor/unify-adapter-jsonl-parsing

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

@ryoppippi

Copy link
Copy Markdown
Member Author

@coderabbitai review

@cubic-dev-ai cubic-dev-ai Bot left a comment •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

7 issues found and verified against the latest diff

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="rust/crates/ccusage/src/adapter/codebuff/parser.rs">

<violation number="1" location="rust/crates/ccusage/src/adapter/codebuff/parser.rs:42">
P2: Invalid UTF-8 chat files are now silently treated as empty data. This can hide corrupted inputs and undercount usage with no error signal.</violation>
</file>

Tip: instead of fixing issues one by one fix them all with cubic

Re-trigger cubic

};
let Some(messages) = messages.as_array() else {
let content = fs::read(path)?;
let Ok(messages) = serde_json::from_slice::<Vec<Value>>(&content) else {

@cubic-dev-ai cubic-dev-ai Bot Jun 15, 2026 •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P2: Invalid UTF-8 chat files are now silently treated as empty data. This can hide corrupted inputs and undercount usage with no error signal.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At rust/crates/ccusage/src/adapter/codebuff/parser.rs, line 42:

<comment>Invalid UTF-8 chat files are now silently treated as empty data. This can hide corrupted inputs and undercount usage with no error signal.</comment>

<file context>
@@ -38,11 +38,8 @@ struct CodebuffContext {
-    };
-    let Some(messages) = messages.as_array() else {
+    let content = fs::read(path)?;
+    let Ok(messages) = serde_json::from_slice::<Vec<Value>>(&content) else {
         return Ok(Vec::new());
     };
</file context>
Fix with cubic

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This byte-read/skip-gracefully behavior is the PR's explicit, documented design choice (consistent with the Claude loader and all other adapters); the old codebuff hard-errored on invalid UTF-8, and reverting only codebuff would reintroduce cross-adapter inconsistency.

Comment thread rust/crates/ccusage/src/adapter/openclaw/parser.rs
Comment thread rust/crates/ccusage/src/adapter/gemini/parser.rs Outdated
Comment thread rust/crates/ccusage/src/adapter/kilo/parser.rs
Comment thread rust/crates/ccusage/src/adapter/opencode/parser.rs
Comment thread rust/crates/ccusage/src/adapter/amp/parser.rs Outdated
Comment thread rust/crates/ccusage/src/adapter/amp/parser.rs
…r-jsonl-parsing

# Conflicts:
#	rust/crates/ccusage/src/adapter/copilot/parser.rs
#	rust/crates/ccusage/src/adapter/kimi/parser.rs
#	rust/crates/ccusage/src/adapter/openclaw/parser.rs
#	rust/crates/ccusage/src/adapter/pi/parser.rs
#	rust/crates/ccusage/src/adapter/qwen/parser.rs
#	rust/crates/ccusage/src/fast.rs
@github-actions

Copy link
Copy Markdown
Contributor

ccusage performance comparison

PR SHA: b2bdf4f7b31a
Base SHA: cd5d58fcaacf

This compares the PR package against the configured base package on the same CI runner.

Package runtime diagnostics

Compares the PR package wrapper, the installed native optional dependency binary, and the workspace release binary on the same large fixture. This identifies whether slow package results come from JavaScript wrapper overhead, the published native binary build, or the Rust core itself.

Fixtures: Claude /home/runner/_work/_temp/ccusage-large-fixture (1.01 GiB, 2597 files), Codex /home/runner/_work/_temp/ccusage-large-codex-fixture (1.01 GiB, 2597 files)
All rows run --offline --json, measured by hyperfine with 0 warmups and 1 runs. This isolates wrapper overhead from the installed native optional dependency and the workspace release binary built on the runner.

Command Runtime Input Median Throughput Samples
claude --offline --json Package wrapper 1.01 GiB 254.5ms 3.96 GiB/s 1
claude --offline --json Installed native binary 1.01 GiB 226.7ms 4.44 GiB/s 1
codex --offline --json Package wrapper 1.01 GiB 112.6ms 8.94 GiB/s 1
codex --offline --json Installed native binary 1.01 GiB 88.2ms 11.42 GiB/s 1

Committed fixture performance

Committed small fixtures for stable PR-to-PR feedback and explicit Claude/Codex command coverage.

Fixtures: Claude apps/ccusage/test/fixtures/claude (0.00 MiB, 2 files), Codex apps/ccusage/test/fixtures/codex (0.00 MiB, 1 files)
Base runs the published ccusage package from pkg.pr.new, installed before measurement; PR runs the published ccusage package from pkg.pr.new, installed before measurement. Both run --offline --json, measured by hyperfine with 2 warmups and 7 runs.
Peak RSS is measured separately with /usr/bin/time using 1 runs. Lower RSS ratios are better.

Command Input Base median PR median PR vs base Base peak RSS PR peak RSS PR/base RSS Base throughput PR throughput
claude daily --offline --json 0.00 MiB 29.2ms 28.3ms 1.03x 53.75 MiB 54.25 MiB 1.01x 0.05 MiB/s 0.05 MiB/s
claude session --offline --json 0.00 MiB 29.1ms 28.1ms 1.04x 54.00 MiB 53.75 MiB 1.00x 0.05 MiB/s 0.06 MiB/s
codex daily --offline --json 0.00 MiB 25.8ms 28.9ms 0.89x 53.75 MiB 53.75 MiB 1.00x 0.03 MiB/s 0.03 MiB/s
codex session --offline --json 0.00 MiB 29.5ms 28.0ms 1.05x 53.75 MiB 53.75 MiB 1.00x 0.03 MiB/s 0.03 MiB/s

Large real-world-shaped fixture performance

Generated fixtures shaped from aggregate local log statistics: thousands of JSONL files, many small sessions, and a long tail of larger sessions. No real prompts, paths, or outputs are stored in the fixtures.

Fixtures: Claude /home/runner/_work/_temp/ccusage-large-fixture (1.01 GiB, 2597 files), Codex /home/runner/_work/_temp/ccusage-large-codex-fixture (1.01 GiB, 2597 files)
Base runs the published ccusage package from pkg.pr.new, installed before measurement; PR runs the published ccusage package from pkg.pr.new, installed before measurement. Both run --offline --json, measured by hyperfine with 0 warmups and 1 runs.
Peak RSS is measured separately with /usr/bin/time using 1 runs. Lower RSS ratios are better.

Command Input Base median PR median PR vs base Base peak RSS PR peak RSS PR/base RSS Base throughput PR throughput
claude --offline --json 1.01 GiB 265.6ms 255.6ms 1.04x 902.32 MiB 1.02 GiB 1.15x 3.79 GiB/s 3.94 GiB/s
codex --offline --json 1.01 GiB 119.1ms 115.9ms 1.03x 435.03 MiB 413.29 MiB 0.95x 8.45 GiB/s 8.69 GiB/s

Artifact size

Artifact Base PR Delta Ratio
packed ccusage-*.tgz 18.08 KiB 18.08 KiB -0.00 KiB 1.00x
installed native package binary 3851.00 KiB 3974.44 KiB +123.44 KiB 0.97x

Lower medians and smaller artifacts are better. CI runner noise still applies; use same-run ratios as directional PR feedback, not release guarantees.

@github-actions

Copy link
Copy Markdown
Contributor

ccusage performance comparison

PR SHA: b2bdf4f7b31a
Base SHA: cd5d58fcaacf

This compares the Rust PR release binary against the configured base package on the same CI runner.

Package runtime diagnostics

Compares the PR package wrapper, the installed native optional dependency binary, and the workspace release binary on the same large fixture. This identifies whether slow package results come from JavaScript wrapper overhead, the published native binary build, or the Rust core itself.

Fixtures: Claude /home/runner/_work/_temp/ccusage-large-fixture (1.01 GiB, 2597 files), Codex /home/runner/_work/_temp/ccusage-large-codex-fixture (1.01 GiB, 2597 files)
All rows run --offline --json, measured by hyperfine with 0 warmups and 1 runs. This isolates wrapper overhead from the installed native optional dependency and the workspace release binary built on the runner.

Command Runtime Input Median Throughput Samples
claude --offline --json Package wrapper 1.01 GiB 252.0ms 4.00 GiB/s 1
claude --offline --json Installed native binary 1.01 GiB 209.4ms 4.81 GiB/s 1
codex --offline --json Package wrapper 1.01 GiB 112.2ms 8.97 GiB/s 1
codex --offline --json Installed native binary 1.01 GiB 103.6ms 9.72 GiB/s 1

Committed fixture performance

Committed small fixtures for stable PR-to-PR feedback and explicit Claude/Codex command coverage.

Fixtures: Claude apps/ccusage/test/fixtures/claude (0.00 MiB, 2 files), Codex apps/ccusage/test/fixtures/codex (0.00 MiB, 1 files)
Base runs the published ccusage package from pkg.pr.new, installed before measurement; PR runs the published native ccusage binary from pkg.pr.new, installed before measurement. Both run --offline --json, measured by hyperfine with 2 warmups and 7 runs.
Peak RSS is measured separately with /usr/bin/time using 1 runs. Lower RSS ratios are better.

Command Input Base median PR median PR vs base Base peak RSS PR peak RSS PR/base RSS Base throughput PR throughput
claude daily --offline --json 0.00 MiB 27.4ms 4.7ms 5.80x 54.00 MiB 10.20 MiB 0.19x 0.06 MiB/s 0.33 MiB/s
claude session --offline --json 0.00 MiB 29.2ms 3.7ms 7.78x 54.00 MiB 10.20 MiB 0.19x 0.05 MiB/s 0.41 MiB/s
codex daily --offline --json 0.00 MiB 25.4ms 2.6ms 9.93x 53.75 MiB 8.19 MiB 0.15x 0.03 MiB/s 0.34 MiB/s
codex session --offline --json 0.00 MiB 26.1ms 2.5ms 10.36x 54.00 MiB 8.19 MiB 0.15x 0.03 MiB/s 0.34 MiB/s

Large real-world-shaped fixture performance

Generated fixtures shaped from aggregate local log statistics: thousands of JSONL files, many small sessions, and a long tail of larger sessions. No real prompts, paths, or outputs are stored in the fixtures.

Fixtures: Claude /home/runner/_work/_temp/ccusage-large-fixture (1.01 GiB, 2597 files), Codex /home/runner/_work/_temp/ccusage-large-codex-fixture (1.01 GiB, 2597 files)
Base runs the published ccusage package from pkg.pr.new, installed before measurement; PR runs the published native ccusage binary from pkg.pr.new, installed before measurement. Both run --offline --json, measured by hyperfine with 0 warmups and 1 runs.
Peak RSS is measured separately with /usr/bin/time using 1 runs. Lower RSS ratios are better.

Command Input Base median PR median PR vs base Base peak RSS PR peak RSS PR/base RSS Base throughput PR throughput
claude --offline --json 1.01 GiB 273.0ms 212.4ms 1.29x 946.32 MiB 954.34 MiB 1.01x 3.69 GiB/s 4.74 GiB/s
codex --offline --json 1.01 GiB 120.7ms 90.4ms 1.34x 415.03 MiB 405.28 MiB 0.98x 8.34 GiB/s 11.14 GiB/s

Artifact size

Artifact Base PR Delta Ratio
packed ccusage-*.tgz 18.08 KiB 18.08 KiB -0.00 KiB 1.00x
installed native package binary 3851.00 KiB 3974.44 KiB +123.44 KiB 0.97x

Lower medians and smaller artifacts are better. CI runner noise still applies; use same-run ratios as directional PR feedback, not release guarantees.

@coderabbitai coderabbitai 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.

Actionable comments posted: 3

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
rust/crates/ccusage/src/adapter/amp/parser.rs (1)

86-96: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Fall back to message usage when the ledger yields no entries.

Because usage_ledger is Some even for an empty/defaulted ledger, this branch returns an empty result and skips valid message-level usage. Parse the ledger first, but only return it when it produced entries; otherwise continue to parse_message_usage.

Proposed fix
     if let Some(ledger) = thread.usage_ledger.as_ref() {
         let cache_tokens = cache_tokens_by_message_id(messages);
-        return Ok(parse_ledger_events(
+        let ledger_entries = parse_ledger_events(
             &ledger.events,
             &cache_tokens,
             &thread_id,
             tz,
             mode,
             pricing,
-        ));
+        );
+        if !ledger_entries.is_empty() {
+            return Ok(ledger_entries);
+        }
     }
 
     Ok(parse_message_usage(messages, &thread_id, tz, mode, pricing))
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@rust/crates/ccusage/src/adapter/amp/parser.rs` around lines 86 - 96, The
current code returns immediately when usage_ledger is Some, but this doesn't
account for empty or defaulted ledgers. Modify the logic to parse the ledger
using parse_ledger_events, but only return that result if it produced non-empty
entries. If the parsed ledger result is empty, the code should continue to the
fallback logic (parse_message_usage) instead of returning an empty result. Check
whether the result from parse_ledger_events contains entries before deciding
whether to return it or fall through to the message usage parsing.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@rust/crates/ccusage/src/adapter/copilot/parser.rs`:
- Around line 481-489: In the timestamp_from_record function, add an additional
or_else clause to parse record.time as a scalar value using
timestamp_from_scalar, in addition to the existing attempt to parse it as parts
using timestamp_from_parts. This should be inserted after the or_else call for
timestamp_from_parts(record.time.as_ref()) to ensure that both structured and
scalar representations of the time field are tried before falling back to other
timestamp fields. This prevents numeric scalar time values from being ignored
and avoids misdating records.

In `@rust/crates/ccusage/src/adapter/gemini/parser.rs`:
- Around line 144-160: The code currently uses fallback_timestamp as the default
timestamp for records without explicit timestamp/created_at fields, but it
should use session_timestamp instead. The session_timestamp already resolves
startTime/lastUpdated from the session, which is more reliable than falling back
to file mtime. Replace the fallback_timestamp parameter with session_timestamp
in both the parse_direct_event_record call and the parse_stats_events call to
ensure records without timestamps are assigned the correct session-level
timestamp.

In `@rust/crates/ccusage/src/adapter/pi/parser.rs`:
- Around line 55-56: The `cost` field in the `PiLine` struct uses strict object
deserialization, which causes the entire line to be dropped if the upstream data
emits `cost` as a non-object shape (string, number, null, etc.). Modify the
deserialization of the `cost: Option<PiCost>` field to be more lenient and
gracefully handle non-object cost values without failing the deserialization of
the entire `PiLine` record, allowing valid usage data to be retained even when
the cost field is malformed or unexpected.

---

Outside diff comments:
In `@rust/crates/ccusage/src/adapter/amp/parser.rs`:
- Around line 86-96: The current code returns immediately when usage_ledger is
Some, but this doesn't account for empty or defaulted ledgers. Modify the logic
to parse the ledger using parse_ledger_events, but only return that result if it
produced non-empty entries. If the parsed ledger result is empty, the code
should continue to the fallback logic (parse_message_usage) instead of returning
an empty result. Check whether the result from parse_ledger_events contains
entries before deciding whether to return it or fall through to the message
usage parsing.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 4acbd115-db78-4b73-9d32-3de421f182c5

📥 Commits

Reviewing files that changed from the base of the PR and between cd5d58f and d9c8ff2.

📒 Files selected for processing (16)
  • rust/crates/ccusage/src/adapter/amp/parser.rs
  • rust/crates/ccusage/src/adapter/codebuff/parser.rs
  • rust/crates/ccusage/src/adapter/copilot/parser.rs
  • rust/crates/ccusage/src/adapter/gemini/parser.rs
  • rust/crates/ccusage/src/adapter/goose/parser.rs
  • rust/crates/ccusage/src/adapter/jsonl.rs
  • rust/crates/ccusage/src/adapter/kilo/loader.rs
  • rust/crates/ccusage/src/adapter/kilo/parser.rs
  • rust/crates/ccusage/src/adapter/kimi/parser.rs
  • rust/crates/ccusage/src/adapter/mod.rs
  • rust/crates/ccusage/src/adapter/openclaw/parser.rs
  • rust/crates/ccusage/src/adapter/opencode/loader.rs
  • rust/crates/ccusage/src/adapter/opencode/parser.rs
  • rust/crates/ccusage/src/adapter/pi/parser.rs
  • rust/crates/ccusage/src/adapter/qwen/parser.rs
  • rust/crates/ccusage/src/fast.rs

Comment thread rust/crates/ccusage/src/adapter/copilot/parser.rs
Comment thread rust/crates/ccusage/src/adapter/gemini/parser.rs
Comment thread rust/crates/ccusage/src/adapter/pi/parser.rs
@pkg-pr-new

pkg-pr-new Bot commented Jun 15, 2026 •

Copy link
Copy Markdown

Open in StackBlitz

ccusage

npx https://pkg.pr.new/ccusage@1327

@ccusage/ccusage-darwin-arm64

npx https://pkg.pr.new/@ccusage/ccusage-darwin-arm64@1327

@ccusage/ccusage-darwin-x64

npx https://pkg.pr.new/@ccusage/ccusage-darwin-x64@1327

@ccusage/ccusage-linux-arm64

npx https://pkg.pr.new/@ccusage/ccusage-linux-arm64@1327

@ccusage/ccusage-linux-x64

npx https://pkg.pr.new/@ccusage/ccusage-linux-x64@1327

@ccusage/ccusage-win32-x64

npx https://pkg.pr.new/@ccusage/ccusage-win32-x64@1327

commit: 011a3ed

A malformed non-object nested field (e.g. "cache": 5) made the typed
struct fail to deserialize and silently dropped the whole usage record
via records().ok(). The pre-refactor Value navigation treated such a
field as absent and kept the otherwise usable tokens/cost. Add a shared
jsonl::lenient_object helper and apply it to the nested tokens/time/cache
fields in the opencode and kilo parsers to restore that behavior.

Co-authored-by: Codesmith <[email protected]>
@coderabbitai

coderabbitai Bot commented Jun 15, 2026 •

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@github-actions

Copy link
Copy Markdown
Contributor

ccusage performance comparison

PR SHA: d9c8ff249ae3
Base SHA: cd5d58fcaacf

This compares the Rust PR release binary against the configured base package on the same CI runner.

Package runtime diagnostics

Compares the PR package wrapper, the installed native optional dependency binary, and the workspace release binary on the same large fixture. This identifies whether slow package results come from JavaScript wrapper overhead, the published native binary build, or the Rust core itself.

Fixtures: Claude /home/runner/_work/_temp/ccusage-large-fixture (1.01 GiB, 2597 files), Codex /home/runner/_work/_temp/ccusage-large-codex-fixture (1.01 GiB, 2597 files)
All rows run --offline --json, measured by hyperfine with 0 warmups and 1 runs. This isolates wrapper overhead from the installed native optional dependency and the workspace release binary built on the runner.

Command Runtime Input Median Throughput Samples
claude --offline --json Package wrapper 1.01 GiB 245.9ms 4.09 GiB/s 1
claude --offline --json Installed native binary 1.01 GiB 220.6ms 4.56 GiB/s 1
codex --offline --json Package wrapper 1.01 GiB 103.2ms 9.76 GiB/s 1
codex --offline --json Installed native binary 1.01 GiB 83.7ms 12.03 GiB/s 1

Committed fixture performance

Committed small fixtures for stable PR-to-PR feedback and explicit Claude/Codex command coverage.

Fixtures: Claude apps/ccusage/test/fixtures/claude (0.00 MiB, 2 files), Codex apps/ccusage/test/fixtures/codex (0.00 MiB, 1 files)
Base runs the published ccusage package from pkg.pr.new, installed before measurement; PR runs the published native ccusage binary from pkg.pr.new, installed before measurement. Both run --offline --json, measured by hyperfine with 2 warmups and 7 runs.
Peak RSS is measured separately with /usr/bin/time using 1 runs. Lower RSS ratios are better.

Command Input Base median PR median PR vs base Base peak RSS PR peak RSS PR/base RSS Base throughput PR throughput
claude daily --offline --json 0.00 MiB 26.7ms 3.6ms 7.40x 54.00 MiB 10.20 MiB 0.19x 0.06 MiB/s 0.43 MiB/s
claude session --offline --json 0.00 MiB 25.0ms 2.5ms 10.00x 53.50 MiB 10.20 MiB 0.19x 0.06 MiB/s 0.62 MiB/s
codex daily --offline --json 0.00 MiB 23.4ms 2.0ms 11.57x 53.50 MiB 8.20 MiB 0.15x 0.04 MiB/s 0.42 MiB/s
codex session --offline --json 0.00 MiB 23.9ms 2.1ms 11.20x 53.50 MiB 8.20 MiB 0.15x 0.04 MiB/s 0.40 MiB/s

Large real-world-shaped fixture performance

Generated fixtures shaped from aggregate local log statistics: thousands of JSONL files, many small sessions, and a long tail of larger sessions. No real prompts, paths, or outputs are stored in the fixtures.

Fixtures: Claude /home/runner/_work/_temp/ccusage-large-fixture (1.01 GiB, 2597 files), Codex /home/runner/_work/_temp/ccusage-large-codex-fixture (1.01 GiB, 2597 files)
Base runs the published ccusage package from pkg.pr.new, installed before measurement; PR runs the published native ccusage binary from pkg.pr.new, installed before measurement. Both run --offline --json, measured by hyperfine with 0 warmups and 1 runs.
Peak RSS is measured separately with /usr/bin/time using 1 runs. Lower RSS ratios are better.

Command Input Base median PR median PR vs base Base peak RSS PR peak RSS PR/base RSS Base throughput PR throughput
claude --offline --json 1.01 GiB 269.4ms 232.6ms 1.16x 944.33 MiB 942.34 MiB 1.00x 3.74 GiB/s 4.33 GiB/s
codex --offline --json 1.01 GiB 104.8ms 80.6ms 1.30x 423.04 MiB 433.30 MiB 1.02x 9.61 GiB/s 12.49 GiB/s

Artifact size

Artifact Base PR Delta Ratio
packed ccusage-*.tgz 18.08 KiB 18.08 KiB -0.00 KiB 1.00x
installed native package binary 3851.00 KiB 3981.75 KiB +130.75 KiB 0.97x

Lower medians and smaller artifacts are better. CI runner noise still applies; use same-run ratios as directional PR feedback, not release guarantees.

@github-actions

Copy link
Copy Markdown
Contributor

ccusage performance comparison

PR SHA: d9c8ff249ae3
Base SHA: cd5d58fcaacf

This compares the PR package against the configured base package on the same CI runner.

Package runtime diagnostics

Compares the PR package wrapper, the installed native optional dependency binary, and the workspace release binary on the same large fixture. This identifies whether slow package results come from JavaScript wrapper overhead, the published native binary build, or the Rust core itself.

Fixtures: Claude /home/runner/_work/_temp/ccusage-large-fixture (1.01 GiB, 2597 files), Codex /home/runner/_work/_temp/ccusage-large-codex-fixture (1.01 GiB, 2597 files)
All rows run --offline --json, measured by hyperfine with 0 warmups and 1 runs. This isolates wrapper overhead from the installed native optional dependency and the workspace release binary built on the runner.

Command Runtime Input Median Throughput Samples
claude --offline --json Package wrapper 1.01 GiB 251.4ms 4.00 GiB/s 1
claude --offline --json Installed native binary 1.01 GiB 220.5ms 4.57 GiB/s 1
codex --offline --json Package wrapper 1.01 GiB 115.1ms 8.74 GiB/s 1
codex --offline --json Installed native binary 1.01 GiB 91.9ms 10.95 GiB/s 1

Committed fixture performance

Committed small fixtures for stable PR-to-PR feedback and explicit Claude/Codex command coverage.

Fixtures: Claude apps/ccusage/test/fixtures/claude (0.00 MiB, 2 files), Codex apps/ccusage/test/fixtures/codex (0.00 MiB, 1 files)
Base runs the published ccusage package from pkg.pr.new, installed before measurement; PR runs the published ccusage package from pkg.pr.new, installed before measurement. Both run --offline --json, measured by hyperfine with 2 warmups and 7 runs.
Peak RSS is measured separately with /usr/bin/time using 1 runs. Lower RSS ratios are better.

Command Input Base median PR median PR vs base Base peak RSS PR peak RSS PR/base RSS Base throughput PR throughput
claude daily --offline --json 0.00 MiB 33.1ms 29.2ms 1.13x 54.00 MiB 54.00 MiB 1.00x 0.05 MiB/s 0.05 MiB/s
claude session --offline --json 0.00 MiB 26.9ms 32.0ms 0.84x 53.75 MiB 53.75 MiB 1.00x 0.06 MiB/s 0.05 MiB/s
codex daily --offline --json 0.00 MiB 28.1ms 27.0ms 1.04x 53.75 MiB 53.75 MiB 1.00x 0.03 MiB/s 0.03 MiB/s
codex session --offline --json 0.00 MiB 26.2ms 26.1ms 1.00x 53.75 MiB 53.50 MiB 1.00x 0.03 MiB/s 0.03 MiB/s

Large real-world-shaped fixture performance

Generated fixtures shaped from aggregate local log statistics: thousands of JSONL files, many small sessions, and a long tail of larger sessions. No real prompts, paths, or outputs are stored in the fixtures.

Fixtures: Claude /home/runner/_work/_temp/ccusage-large-fixture (1.01 GiB, 2597 files), Codex /home/runner/_work/_temp/ccusage-large-codex-fixture (1.01 GiB, 2597 files)
Base runs the published ccusage package from pkg.pr.new, installed before measurement; PR runs the published ccusage package from pkg.pr.new, installed before measurement. Both run --offline --json, measured by hyperfine with 0 warmups and 1 runs.
Peak RSS is measured separately with /usr/bin/time using 1 runs. Lower RSS ratios are better.

Command Input Base median PR median PR vs base Base peak RSS PR peak RSS PR/base RSS Base throughput PR throughput
claude --offline --json 1.01 GiB 263.1ms 262.9ms 1.00x 946.32 MiB 938.34 MiB 0.99x 3.83 GiB/s 3.83 GiB/s
codex --offline --json 1.01 GiB 106.2ms 117.0ms 0.91x 401.03 MiB 407.29 MiB 1.02x 9.48 GiB/s 8.61 GiB/s

Artifact size

Artifact Base PR Delta Ratio
packed ccusage-*.tgz 18.08 KiB 18.08 KiB -0.00 KiB 1.00x
installed native package binary 3851.00 KiB 3981.75 KiB +130.75 KiB 0.97x

Lower medians and smaller artifacts are better. CI runner noise still applies; use same-run ratios as directional PR feedback, not release guarantees.

ryoppippi and others added 3 commits June 15, 2026 17:08
…sing' into refactor/unify-adapter-jsonl-parsing
The typed refactor read the gemini record discriminator with the
trimming non_empty_string helper, but the pre-refactor code compared it
with a raw Value::as_str (record.get("type").and_then(Value::as_str) ==
Some("gemini")). Trimming let padded values like " gemini " spuriously
match the "gemini" discriminator. Use the local untrimmed lenient_str
helper, which mirrors Value::as_str: strings verbatim, non-strings to
None without failing the line, so records still fall through to stats
parsing.

Co-authored-by: Codesmith <[email protected]>
…sing' into refactor/unify-adapter-jsonl-parsing
@pullfrog

pullfrog Bot commented Jun 15, 2026 •

Copy link
Copy Markdown
Contributor

no API key found — this repo is configured to use deepseek/deepseek-v4-pro, which needs DEEPSEEK_API_KEY, but the runner has no key for it.

To fix: add the key as a GitHub Actions secret (referenced from your workflow's env: block) or as a Pullfrog secret in the console — or switch this repo to a different model (free models need no key).

Open repo secrets → · Configure model → · Setup docs → · Ask in Discord →

Pullfrog  | Rerun failed job ➔ | View workflow run | via Pullfrog | Using DeepSeek Pro (free via Pullfrog for OSS) | 𝕏

ryoppippi and others added 6 commits June 15, 2026 17:14
A non-object `cost` payload made `PiUsage` deserialization fail, which
dropped the whole usage record via `jsonl::records().ok()`. The pre-refactor
`Value` navigation treated a non-object cost as absent display cost while
keeping the record's tokens. Apply `jsonl::lenient_object` to the nested
`cost` field to restore that behavior.

Addresses a CodeRabbit review finding on PR #1327.
The typed refactor regressed three lenient-navigation behaviors:

- amp messages were strict-deserialized as Vec<AmpMessage>, so a single
  non-object element (or a non-array messages field) dropped the entire
  thread. Parse them with the new jsonl::lenient_vec so bad elements are
  skipped, matching the old Value::as_array navigation.
- amp ledger precedence fired on any usageLedger object (including {}),
  so threads with no usable events array stopped falling back to message
  usage. Track events as Option<Vec<_>> via jsonl::lenient_array and only
  take the ledger branch when the events array is present, and read
  usage_ledger via jsonl::lenient_object so a non-object usageLedger no
  longer fails the whole thread.
- pi usage.cost was strict-typed, so a non-object cost dropped an
  otherwise usable record; read it via jsonl::lenient_object.

Adds jsonl::lenient_array / jsonl::lenient_vec helpers plus regression
tests for each path.

Co-authored-by: Codesmith <[email protected]>
A non-object message field made OpenClawLine deserialization fail, so
jsonl::records().ok() dropped the whole line. For a model_change or
model-snapshot record that also carried a malformed message, this lost
the model/provider state update that subsequent usage entries rely on.
The pre-refactor Value navigation treated a non-object message as no
message while keeping the line, so apply jsonl::lenient_object to
restore that behavior.

Co-authored-by: Codesmith <[email protected]>
…sing' into refactor/unify-adapter-jsonl-parsing
`nix flake check`'s treefmt wants the first `matches!` arm in
`is_unsupported_nullable_field` split across lines like the rest. Apply the
pinned formatter so the check passes; no behavior change.
…sing' into refactor/unify-adapter-jsonl-parsing
@github-actions

Copy link
Copy Markdown
Contributor

ccusage performance comparison

PR SHA: 5a7de7595824
Base SHA: 135ef29a6fae

This compares the PR package against the configured base package on the same CI runner.

Package runtime diagnostics

Compares the PR package wrapper, the installed native optional dependency binary, and the workspace release binary on the same large fixture. This identifies whether slow package results come from JavaScript wrapper overhead, the published native binary build, or the Rust core itself.

Fixtures: Claude /home/runner/_work/_temp/ccusage-large-fixture (1.01 GiB, 2597 files), Codex /home/runner/_work/_temp/ccusage-large-codex-fixture (1.01 GiB, 2597 files)
All rows run --offline --json, measured by hyperfine with 0 warmups and 1 runs. This isolates wrapper overhead from the installed native optional dependency and the workspace release binary built on the runner.

Command Runtime Input Median Throughput Samples
claude --offline --json Package wrapper 1.01 GiB 258.3ms 3.90 GiB/s 1
claude --offline --json Installed native binary 1.01 GiB 232.5ms 4.33 GiB/s 1
codex --offline --json Package wrapper 1.01 GiB 101.1ms 9.96 GiB/s 1
codex --offline --json Installed native binary 1.01 GiB 84.8ms 11.87 GiB/s 1

Committed fixture performance

Committed small fixtures for stable PR-to-PR feedback and explicit Claude/Codex command coverage.

Fixtures: Claude apps/ccusage/test/fixtures/claude (0.00 MiB, 2 files), Codex apps/ccusage/test/fixtures/codex (0.00 MiB, 1 files)
Base runs the published ccusage package from pkg.pr.new, installed before measurement; PR runs the published ccusage package from pkg.pr.new, installed before measurement. Both run --offline --json, measured by hyperfine with 2 warmups and 7 runs.
Peak RSS is measured separately with /usr/bin/time using 1 runs. Lower RSS ratios are better.

Command Input Base median PR median PR vs base Base peak RSS PR peak RSS PR/base RSS Base throughput PR throughput
claude daily --offline --json 0.00 MiB 25.6ms 25.2ms 1.02x 53.50 MiB 53.50 MiB 1.00x 0.06 MiB/s 0.06 MiB/s
claude session --offline --json 0.00 MiB 25.6ms 24.5ms 1.05x 53.50 MiB 53.75 MiB 1.00x 0.06 MiB/s 0.06 MiB/s
codex daily --offline --json 0.00 MiB 22.5ms 23.6ms 0.96x 53.50 MiB 53.50 MiB 1.00x 0.04 MiB/s 0.04 MiB/s
codex session --offline --json 0.00 MiB 22.9ms 23.6ms 0.97x 53.75 MiB 53.25 MiB 0.99x 0.04 MiB/s 0.04 MiB/s

Large real-world-shaped fixture performance

Generated fixtures shaped from aggregate local log statistics: thousands of JSONL files, many small sessions, and a long tail of larger sessions. No real prompts, paths, or outputs are stored in the fixtures.

Fixtures: Claude /home/runner/_work/_temp/ccusage-large-fixture (1.01 GiB, 2597 files), Codex /home/runner/_work/_temp/ccusage-large-codex-fixture (1.01 GiB, 2597 files)
Base runs the published ccusage package from pkg.pr.new, installed before measurement; PR runs the published ccusage package from pkg.pr.new, installed before measurement. Both run --offline --json, measured by hyperfine with 0 warmups and 1 runs.
Peak RSS is measured separately with /usr/bin/time using 1 runs. Lower RSS ratios are better.

Command Input Base median PR median PR vs base Base peak RSS PR peak RSS PR/base RSS Base throughput PR throughput
claude --offline --json 1.01 GiB 261.3ms 252.8ms 1.03x 938.33 MiB 942.33 MiB 1.00x 3.85 GiB/s 3.98 GiB/s
codex --offline --json 1.01 GiB 105.0ms 106.9ms 0.98x 437.04 MiB 425.04 MiB 0.97x 9.59 GiB/s 9.42 GiB/s

Artifact size

Artifact Base PR Delta Ratio
packed ccusage-*.tgz 18.08 KiB 18.08 KiB +0.00 KiB 1.00x
installed native package binary 3858.62 KiB 3976.06 KiB +117.44 KiB 0.97x

Lower medians and smaller artifacts are better. CI runner noise still applies; use same-run ratios as directional PR feedback, not release guarantees.

@github-actions

Copy link
Copy Markdown
Contributor

ccusage performance comparison

PR SHA: 5a7de7595824
Base SHA: 135ef29a6fae

This compares the Rust PR release binary against the configured base package on the same CI runner.

Package runtime diagnostics

Compares the PR package wrapper, the installed native optional dependency binary, and the workspace release binary on the same large fixture. This identifies whether slow package results come from JavaScript wrapper overhead, the published native binary build, or the Rust core itself.

Fixtures: Claude /home/runner/_work/_temp/ccusage-large-fixture (1.01 GiB, 2597 files), Codex /home/runner/_work/_temp/ccusage-large-codex-fixture (1.01 GiB, 2597 files)
All rows run --offline --json, measured by hyperfine with 0 warmups and 1 runs. This isolates wrapper overhead from the installed native optional dependency and the workspace release binary built on the runner.

Command Runtime Input Median Throughput Samples
claude --offline --json Package wrapper 1.01 GiB 257.0ms 3.92 GiB/s 1
claude --offline --json Installed native binary 1.01 GiB 209.3ms 4.81 GiB/s 1
codex --offline --json Package wrapper 1.01 GiB 113.2ms 8.89 GiB/s 1
codex --offline --json Installed native binary 1.01 GiB 85.4ms 11.78 GiB/s 1

Committed fixture performance

Committed small fixtures for stable PR-to-PR feedback and explicit Claude/Codex command coverage.

Fixtures: Claude apps/ccusage/test/fixtures/claude (0.00 MiB, 2 files), Codex apps/ccusage/test/fixtures/codex (0.00 MiB, 1 files)
Base runs the published ccusage package from pkg.pr.new, installed before measurement; PR runs the published native ccusage binary from pkg.pr.new, installed before measurement. Both run --offline --json, measured by hyperfine with 2 warmups and 7 runs.
Peak RSS is measured separately with /usr/bin/time using 1 runs. Lower RSS ratios are better.

Command Input Base median PR median PR vs base Base peak RSS PR peak RSS PR/base RSS Base throughput PR throughput
claude daily --offline --json 0.00 MiB 29.1ms 4.0ms 7.37x 53.75 MiB 10.19 MiB 0.19x 0.05 MiB/s 0.39 MiB/s
claude session --offline --json 0.00 MiB 29.3ms 4.2ms 7.06x 53.75 MiB 10.20 MiB 0.19x 0.05 MiB/s 0.37 MiB/s
codex daily --offline --json 0.00 MiB 27.5ms 3.8ms 7.24x 53.75 MiB 8.18 MiB 0.15x 0.03 MiB/s 0.23 MiB/s
codex session --offline --json 0.00 MiB 26.4ms 2.9ms 9.00x 53.75 MiB 8.18 MiB 0.15x 0.03 MiB/s 0.29 MiB/s

Large real-world-shaped fixture performance

Generated fixtures shaped from aggregate local log statistics: thousands of JSONL files, many small sessions, and a long tail of larger sessions. No real prompts, paths, or outputs are stored in the fixtures.

Fixtures: Claude /home/runner/_work/_temp/ccusage-large-fixture (1.01 GiB, 2597 files), Codex /home/runner/_work/_temp/ccusage-large-codex-fixture (1.01 GiB, 2597 files)
Base runs the published ccusage package from pkg.pr.new, installed before measurement; PR runs the published native ccusage binary from pkg.pr.new, installed before measurement. Both run --offline --json, measured by hyperfine with 0 warmups and 1 runs.
Peak RSS is measured separately with /usr/bin/time using 1 runs. Lower RSS ratios are better.

Command Input Base median PR median PR vs base Base peak RSS PR peak RSS PR/base RSS Base throughput PR throughput
claude --offline --json 1.01 GiB 269.1ms 235.8ms 1.14x 930.33 MiB 942.33 MiB 1.01x 3.74 GiB/s 4.27 GiB/s
codex --offline --json 1.01 GiB 108.2ms 87.1ms 1.24x 419.04 MiB 417.02 MiB 1.00x 9.30 GiB/s 11.56 GiB/s

Artifact size

Artifact Base PR Delta Ratio
packed ccusage-*.tgz 18.08 KiB 18.08 KiB +0.00 KiB 1.00x
installed native package binary 3858.62 KiB 3976.06 KiB +117.44 KiB 0.97x

Lower medians and smaller artifacts are better. CI runner noise still applies; use same-run ratios as directional PR feedback, not release guarantees.

@github-actions

Copy link
Copy Markdown
Contributor

ccusage performance comparison

PR SHA: ef4a4855ccbe
Base SHA: 135ef29a6fae

This compares the PR package against the configured base package on the same CI runner.

Package runtime diagnostics

Compares the PR package wrapper, the installed native optional dependency binary, and the workspace release binary on the same large fixture. This identifies whether slow package results come from JavaScript wrapper overhead, the published native binary build, or the Rust core itself.

Fixtures: Claude /home/runner/_work/_temp/ccusage-large-fixture (1.01 GiB, 2597 files), Codex /home/runner/_work/_temp/ccusage-large-codex-fixture (1.01 GiB, 2597 files)
All rows run --offline --json, measured by hyperfine with 0 warmups and 1 runs. This isolates wrapper overhead from the installed native optional dependency and the workspace release binary built on the runner.

Command Runtime Input Median Throughput Samples
claude --offline --json Package wrapper 1.01 GiB 271.4ms 3.71 GiB/s 1
claude --offline --json Installed native binary 1.01 GiB 252.5ms 3.99 GiB/s 1
codex --offline --json Package wrapper 1.01 GiB 127.4ms 7.90 GiB/s 1
codex --offline --json Installed native binary 1.01 GiB 105.8ms 9.52 GiB/s 1

Committed fixture performance

Committed small fixtures for stable PR-to-PR feedback and explicit Claude/Codex command coverage.

Fixtures: Claude apps/ccusage/test/fixtures/claude (0.00 MiB, 2 files), Codex apps/ccusage/test/fixtures/codex (0.00 MiB, 1 files)
Base runs the published ccusage package from pkg.pr.new, installed before measurement; PR runs the published ccusage package from pkg.pr.new, installed before measurement. Both run --offline --json, measured by hyperfine with 2 warmups and 7 runs.
Peak RSS is measured separately with /usr/bin/time using 1 runs. Lower RSS ratios are better.

Command Input Base median PR median PR vs base Base peak RSS PR peak RSS PR/base RSS Base throughput PR throughput
claude daily --offline --json 0.00 MiB 33.2ms 35.4ms 0.94x 53.75 MiB 53.50 MiB 1.00x 0.05 MiB/s 0.04 MiB/s
claude session --offline --json 0.00 MiB 30.6ms 32.9ms 0.93x 53.75 MiB 53.75 MiB 1.00x 0.05 MiB/s 0.05 MiB/s
codex daily --offline --json 0.00 MiB 30.2ms 30.0ms 1.01x 53.75 MiB 53.75 MiB 1.00x 0.03 MiB/s 0.03 MiB/s
codex session --offline --json 0.00 MiB 30.9ms 28.0ms 1.10x 53.50 MiB 53.50 MiB 1.00x 0.03 MiB/s 0.03 MiB/s

Large real-world-shaped fixture performance

Generated fixtures shaped from aggregate local log statistics: thousands of JSONL files, many small sessions, and a long tail of larger sessions. No real prompts, paths, or outputs are stored in the fixtures.

Fixtures: Claude /home/runner/_work/_temp/ccusage-large-fixture (1.01 GiB, 2597 files), Codex /home/runner/_work/_temp/ccusage-large-codex-fixture (1.01 GiB, 2597 files)
Base runs the published ccusage package from pkg.pr.new, installed before measurement; PR runs the published ccusage package from pkg.pr.new, installed before measurement. Both run --offline --json, measured by hyperfine with 0 warmups and 1 runs.
Peak RSS is measured separately with /usr/bin/time using 1 runs. Lower RSS ratios are better.

Command Input Base median PR median PR vs base Base peak RSS PR peak RSS PR/base RSS Base throughput PR throughput
claude --offline --json 1.01 GiB 319.9ms 277.6ms 1.15x 934.34 MiB 942.33 MiB 1.01x 3.15 GiB/s 3.63 GiB/s
codex --offline --json 1.01 GiB 111.4ms 120.5ms 0.92x 423.03 MiB 405.03 MiB 0.96x 9.04 GiB/s 8.35 GiB/s

Artifact size

Artifact Base PR Delta Ratio
packed ccusage-*.tgz 18.08 KiB 18.08 KiB +0.00 KiB 1.00x
installed native package binary 3858.62 KiB 3976.50 KiB +117.88 KiB 0.97x

Lower medians and smaller artifacts are better. CI runner noise still applies; use same-run ratios as directional PR feedback, not release guarantees.

@github-actions

Copy link
Copy Markdown
Contributor

ccusage performance comparison

PR SHA: ef4a4855ccbe
Base SHA: 135ef29a6fae

This compares the Rust PR release binary against the configured base package on the same CI runner.

Package runtime diagnostics

Compares the PR package wrapper, the installed native optional dependency binary, and the workspace release binary on the same large fixture. This identifies whether slow package results come from JavaScript wrapper overhead, the published native binary build, or the Rust core itself.

Fixtures: Claude /home/runner/_work/_temp/ccusage-large-fixture (1.01 GiB, 2597 files), Codex /home/runner/_work/_temp/ccusage-large-codex-fixture (1.01 GiB, 2597 files)
All rows run --offline --json, measured by hyperfine with 0 warmups and 1 runs. This isolates wrapper overhead from the installed native optional dependency and the workspace release binary built on the runner.

Command Runtime Input Median Throughput Samples
claude --offline --json Package wrapper 1.01 GiB 251.8ms 4.00 GiB/s 1
claude --offline --json Installed native binary 1.01 GiB 254.2ms 3.96 GiB/s 1
codex --offline --json Package wrapper 1.01 GiB 111.3ms 9.04 GiB/s 1
codex --offline --json Installed native binary 1.01 GiB 89.7ms 11.22 GiB/s 1

Committed fixture performance

Committed small fixtures for stable PR-to-PR feedback and explicit Claude/Codex command coverage.

Fixtures: Claude apps/ccusage/test/fixtures/claude (0.00 MiB, 2 files), Codex apps/ccusage/test/fixtures/codex (0.00 MiB, 1 files)
Base runs the published ccusage package from pkg.pr.new, installed before measurement; PR runs the published native ccusage binary from pkg.pr.new, installed before measurement. Both run --offline --json, measured by hyperfine with 2 warmups and 7 runs.
Peak RSS is measured separately with /usr/bin/time using 1 runs. Lower RSS ratios are better.

Command Input Base median PR median PR vs base Base peak RSS PR peak RSS PR/base RSS Base throughput PR throughput
claude daily --offline --json 0.00 MiB 32.6ms 6.9ms 4.71x 53.75 MiB 10.19 MiB 0.19x 0.05 MiB/s 0.22 MiB/s
claude session --offline --json 0.00 MiB 34.3ms 3.5ms 9.79x 53.75 MiB 10.19 MiB 0.19x 0.05 MiB/s 0.44 MiB/s
codex daily --offline --json 0.00 MiB 37.4ms 3.2ms 11.79x 53.75 MiB 8.19 MiB 0.15x 0.02 MiB/s 0.27 MiB/s
codex session --offline --json 0.00 MiB 29.5ms 2.9ms 10.18x 53.50 MiB 8.18 MiB 0.15x 0.03 MiB/s 0.30 MiB/s

Large real-world-shaped fixture performance

Generated fixtures shaped from aggregate local log statistics: thousands of JSONL files, many small sessions, and a long tail of larger sessions. No real prompts, paths, or outputs are stored in the fixtures.

Fixtures: Claude /home/runner/_work/_temp/ccusage-large-fixture (1.01 GiB, 2597 files), Codex /home/runner/_work/_temp/ccusage-large-codex-fixture (1.01 GiB, 2597 files)
Base runs the published ccusage package from pkg.pr.new, installed before measurement; PR runs the published native ccusage binary from pkg.pr.new, installed before measurement. Both run --offline --json, measured by hyperfine with 0 warmups and 1 runs.
Peak RSS is measured separately with /usr/bin/time using 1 runs. Lower RSS ratios are better.

Command Input Base median PR median PR vs base Base peak RSS PR peak RSS PR/base RSS Base throughput PR throughput
claude --offline --json 1.01 GiB 335.8ms 247.4ms 1.36x 938.33 MiB 924.33 MiB 0.99x 3.00 GiB/s 4.07 GiB/s
codex --offline --json 1.01 GiB 111.0ms 90.4ms 1.23x 421.03 MiB 413.04 MiB 0.98x 9.07 GiB/s 11.14 GiB/s

Artifact size

Artifact Base PR Delta Ratio
packed ccusage-*.tgz 18.08 KiB 18.08 KiB +0.00 KiB 1.00x
installed native package binary 3858.62 KiB 3976.50 KiB +117.88 KiB 0.97x

Lower medians and smaller artifacts are better. CI runner noise still applies; use same-run ratios as directional PR feedback, not release guarantees.

@github-actions

Copy link
Copy Markdown
Contributor

ccusage performance comparison

PR SHA: 6eb56ca42602
Base SHA: 135ef29a6fae

This compares the PR package against the configured base package on the same CI runner.

Package runtime diagnostics

Compares the PR package wrapper, the installed native optional dependency binary, and the workspace release binary on the same large fixture. This identifies whether slow package results come from JavaScript wrapper overhead, the published native binary build, or the Rust core itself.

Fixtures: Claude /home/runner/_work/_temp/ccusage-large-fixture (1.01 GiB, 2597 files), Codex /home/runner/_work/_temp/ccusage-large-codex-fixture (1.01 GiB, 2597 files)
All rows run --offline --json, measured by hyperfine with 0 warmups and 1 runs. This isolates wrapper overhead from the installed native optional dependency and the workspace release binary built on the runner.

Command Runtime Input Median Throughput Samples
claude --offline --json Package wrapper 1.01 GiB 330.0ms 3.05 GiB/s 1
claude --offline --json Installed native binary 1.01 GiB 245.8ms 4.10 GiB/s 1
codex --offline --json Package wrapper 1.01 GiB 112.0ms 8.99 GiB/s 1
codex --offline --json Installed native binary 1.01 GiB 88.9ms 11.33 GiB/s 1

Committed fixture performance

Committed small fixtures for stable PR-to-PR feedback and explicit Claude/Codex command coverage.

Fixtures: Claude apps/ccusage/test/fixtures/claude (0.00 MiB, 2 files), Codex apps/ccusage/test/fixtures/codex (0.00 MiB, 1 files)
Base runs the published ccusage package from pkg.pr.new, installed before measurement; PR runs the published ccusage package from pkg.pr.new, installed before measurement. Both run --offline --json, measured by hyperfine with 2 warmups and 7 runs.
Peak RSS is measured separately with /usr/bin/time using 1 runs. Lower RSS ratios are better.

Command Input Base median PR median PR vs base Base peak RSS PR peak RSS PR/base RSS Base throughput PR throughput
claude daily --offline --json 0.00 MiB 37.4ms 34.9ms 1.07x 53.75 MiB 53.50 MiB 1.00x 0.04 MiB/s 0.04 MiB/s
claude session --offline --json 0.00 MiB 37.7ms 30.0ms 1.26x 54.00 MiB 53.75 MiB 1.00x 0.04 MiB/s 0.05 MiB/s
codex daily --offline --json 0.00 MiB 29.4ms 27.3ms 1.08x 53.50 MiB 53.50 MiB 1.00x 0.03 MiB/s 0.03 MiB/s
codex session --offline --json 0.00 MiB 30.3ms 30.8ms 0.98x 53.75 MiB 53.50 MiB 1.00x 0.03 MiB/s 0.03 MiB/s

Large real-world-shaped fixture performance

Generated fixtures shaped from aggregate local log statistics: thousands of JSONL files, many small sessions, and a long tail of larger sessions. No real prompts, paths, or outputs are stored in the fixtures.

Fixtures: Claude /home/runner/_work/_temp/ccusage-large-fixture (1.01 GiB, 2597 files), Codex /home/runner/_work/_temp/ccusage-large-codex-fixture (1.01 GiB, 2597 files)
Base runs the published ccusage package from pkg.pr.new, installed before measurement; PR runs the published ccusage package from pkg.pr.new, installed before measurement. Both run --offline --json, measured by hyperfine with 0 warmups and 1 runs.
Peak RSS is measured separately with /usr/bin/time using 1 runs. Lower RSS ratios are better.

Command Input Base median PR median PR vs base Base peak RSS PR peak RSS PR/base RSS Base throughput PR throughput
claude --offline --json 1.01 GiB 330.6ms 299.7ms 1.10x 956.34 MiB 958.58 MiB 1.00x 3.05 GiB/s 3.36 GiB/s
codex --offline --json 1.01 GiB 112.5ms 117.0ms 0.96x 409.04 MiB 401.27 MiB 0.98x 8.95 GiB/s 8.60 GiB/s

Artifact size

Artifact Base PR Delta Ratio
packed ccusage-*.tgz 18.08 KiB 18.08 KiB +0.00 KiB 1.00x
installed native package binary 3858.62 KiB 3975.50 KiB +116.88 KiB 0.97x

Lower medians and smaller artifacts are better. CI runner noise still applies; use same-run ratios as directional PR feedback, not release guarantees.

@github-actions

Copy link
Copy Markdown
Contributor

ccusage performance comparison

PR SHA: 6eb56ca42602
Base SHA: 135ef29a6fae

This compares the Rust PR release binary against the configured base package on the same CI runner.

Package runtime diagnostics

Compares the PR package wrapper, the installed native optional dependency binary, and the workspace release binary on the same large fixture. This identifies whether slow package results come from JavaScript wrapper overhead, the published native binary build, or the Rust core itself.

Fixtures: Claude /home/runner/_work/_temp/ccusage-large-fixture (1.01 GiB, 2597 files), Codex /home/runner/_work/_temp/ccusage-large-codex-fixture (1.01 GiB, 2597 files)
All rows run --offline --json, measured by hyperfine with 0 warmups and 1 runs. This isolates wrapper overhead from the installed native optional dependency and the workspace release binary built on the runner.

Command Runtime Input Median Throughput Samples
claude --offline --json Package wrapper 1.01 GiB 258.2ms 3.90 GiB/s 1
claude --offline --json Installed native binary 1.01 GiB 276.2ms 3.65 GiB/s 1
codex --offline --json Package wrapper 1.01 GiB 113.7ms 8.85 GiB/s 1
codex --offline --json Installed native binary 1.01 GiB 88.4ms 11.38 GiB/s 1

Committed fixture performance

Committed small fixtures for stable PR-to-PR feedback and explicit Claude/Codex command coverage.

Fixtures: Claude apps/ccusage/test/fixtures/claude (0.00 MiB, 2 files), Codex apps/ccusage/test/fixtures/codex (0.00 MiB, 1 files)
Base runs the published ccusage package from pkg.pr.new, installed before measurement; PR runs the published native ccusage binary from pkg.pr.new, installed before measurement. Both run --offline --json, measured by hyperfine with 2 warmups and 7 runs.
Peak RSS is measured separately with /usr/bin/time using 1 runs. Lower RSS ratios are better.

Command Input Base median PR median PR vs base Base peak RSS PR peak RSS PR/base RSS Base throughput PR throughput
claude daily --offline --json 0.00 MiB 35.1ms 8.1ms 4.36x 53.50 MiB 10.20 MiB 0.19x 0.04 MiB/s 0.19 MiB/s
claude session --offline --json 0.00 MiB 32.6ms 3.5ms 9.31x 53.50 MiB 10.19 MiB 0.19x 0.05 MiB/s 0.44 MiB/s
codex daily --offline --json 0.00 MiB 29.1ms 2.8ms 10.33x 53.50 MiB 8.18 MiB 0.15x 0.03 MiB/s 0.30 MiB/s
codex session --offline --json 0.00 MiB 27.3ms 2.7ms 10.09x 53.50 MiB 8.18 MiB 0.15x 0.03 MiB/s 0.32 MiB/s

Large real-world-shaped fixture performance

Generated fixtures shaped from aggregate local log statistics: thousands of JSONL files, many small sessions, and a long tail of larger sessions. No real prompts, paths, or outputs are stored in the fixtures.

Fixtures: Claude /home/runner/_work/_temp/ccusage-large-fixture (1.01 GiB, 2597 files), Codex /home/runner/_work/_temp/ccusage-large-codex-fixture (1.01 GiB, 2597 files)
Base runs the published ccusage package from pkg.pr.new, installed before measurement; PR runs the published native ccusage binary from pkg.pr.new, installed before measurement. Both run --offline --json, measured by hyperfine with 0 warmups and 1 runs.
Peak RSS is measured separately with /usr/bin/time using 1 runs. Lower RSS ratios are better.

Command Input Base median PR median PR vs base Base peak RSS PR peak RSS PR/base RSS Base throughput PR throughput
claude --offline --json 1.01 GiB 326.8ms 270.0ms 1.21x 942.33 MiB 958.57 MiB 1.02x 3.08 GiB/s 3.73 GiB/s
codex --offline --json 1.01 GiB 110.0ms 93.5ms 1.18x 409.04 MiB 413.28 MiB 1.01x 9.15 GiB/s 10.77 GiB/s

Artifact size

Artifact Base PR Delta Ratio
packed ccusage-*.tgz 18.08 KiB 18.08 KiB +0.00 KiB 1.00x
installed native package binary 3858.62 KiB 3975.50 KiB +116.88 KiB 0.97x

Lower medians and smaller artifacts are better. CI runner noise still applies; use same-run ratios as directional PR feedback, not release guarantees.

@github-actions

Copy link
Copy Markdown
Contributor

ccusage performance comparison

PR SHA: 011a3edf934d
Base SHA: 135ef29a6fae

This compares the Rust PR release binary against the configured base package on the same CI runner.

Package runtime diagnostics

Compares the PR package wrapper, the installed native optional dependency binary, and the workspace release binary on the same large fixture. This identifies whether slow package results come from JavaScript wrapper overhead, the published native binary build, or the Rust core itself.

Fixtures: Claude /home/runner/_work/_temp/ccusage-large-fixture (1.01 GiB, 2597 files), Codex /home/runner/_work/_temp/ccusage-large-codex-fixture (1.01 GiB, 2597 files)
All rows run --offline --json, measured by hyperfine with 0 warmups and 1 runs. This isolates wrapper overhead from the installed native optional dependency and the workspace release binary built on the runner.

Command Runtime Input Median Throughput Samples
claude --offline --json Package wrapper 1.01 GiB 318.9ms 3.16 GiB/s 1
claude --offline --json Installed native binary 1.01 GiB 268.1ms 3.76 GiB/s 1
codex --offline --json Package wrapper 1.01 GiB 111.1ms 9.06 GiB/s 1
codex --offline --json Installed native binary 1.01 GiB 87.4ms 11.52 GiB/s 1

Committed fixture performance

Committed small fixtures for stable PR-to-PR feedback and explicit Claude/Codex command coverage.

Fixtures: Claude apps/ccusage/test/fixtures/claude (0.00 MiB, 2 files), Codex apps/ccusage/test/fixtures/codex (0.00 MiB, 1 files)
Base runs the published ccusage package from pkg.pr.new, installed before measurement; PR runs the published native ccusage binary from pkg.pr.new, installed before measurement. Both run --offline --json, measured by hyperfine with 2 warmups and 7 runs.
Peak RSS is measured separately with /usr/bin/time using 1 runs. Lower RSS ratios are better.

Command Input Base median PR median PR vs base Base peak RSS PR peak RSS PR/base RSS Base throughput PR throughput
claude daily --offline --json 0.00 MiB 35.3ms 7.6ms 4.62x 53.50 MiB 10.19 MiB 0.19x 0.04 MiB/s 0.20 MiB/s
claude session --offline --json 0.00 MiB 32.1ms 5.7ms 5.63x 54.00 MiB 10.19 MiB 0.19x 0.05 MiB/s 0.27 MiB/s
codex daily --offline --json 0.00 MiB 32.7ms 3.2ms 10.15x 53.50 MiB 8.18 MiB 0.15x 0.03 MiB/s 0.27 MiB/s
codex session --offline --json 0.00 MiB 33.3ms 2.9ms 11.54x 53.75 MiB 8.18 MiB 0.15x 0.03 MiB/s 0.30 MiB/s

Large real-world-shaped fixture performance

Generated fixtures shaped from aggregate local log statistics: thousands of JSONL files, many small sessions, and a long tail of larger sessions. No real prompts, paths, or outputs are stored in the fixtures.

Fixtures: Claude /home/runner/_work/_temp/ccusage-large-fixture (1.01 GiB, 2597 files), Codex /home/runner/_work/_temp/ccusage-large-codex-fixture (1.01 GiB, 2597 files)
Base runs the published ccusage package from pkg.pr.new, installed before measurement; PR runs the published native ccusage binary from pkg.pr.new, installed before measurement. Both run --offline --json, measured by hyperfine with 0 warmups and 1 runs.
Peak RSS is measured separately with /usr/bin/time using 1 runs. Lower RSS ratios are better.

Command Input Base median PR median PR vs base Base peak RSS PR peak RSS PR/base RSS Base throughput PR throughput
claude --offline --json 1.01 GiB 337.0ms 229.0ms 1.47x 950.34 MiB 944.32 MiB 0.99x 2.99 GiB/s 4.40 GiB/s
codex --offline --json 1.01 GiB 127.8ms 88.8ms 1.44x 393.03 MiB 403.28 MiB 1.03x 7.88 GiB/s 11.34 GiB/s

Artifact size

Artifact Base PR Delta Ratio
packed ccusage-*.tgz 18.08 KiB 18.08 KiB -0.00 KiB 1.00x
installed native package binary 3858.62 KiB 3975.50 KiB +116.88 KiB 0.97x

Lower medians and smaller artifacts are better. CI runner noise still applies; use same-run ratios as directional PR feedback, not release guarantees.

@github-actions

Copy link
Copy Markdown
Contributor

ccusage performance comparison

PR SHA: 011a3edf934d
Base SHA: 135ef29a6fae

This compares the PR package against the configured base package on the same CI runner.

Package runtime diagnostics

Compares the PR package wrapper, the installed native optional dependency binary, and the workspace release binary on the same large fixture. This identifies whether slow package results come from JavaScript wrapper overhead, the published native binary build, or the Rust core itself.

Fixtures: Claude /home/runner/_work/_temp/ccusage-large-fixture (1.01 GiB, 2597 files), Codex /home/runner/_work/_temp/ccusage-large-codex-fixture (1.01 GiB, 2597 files)
All rows run --offline --json, measured by hyperfine with 0 warmups and 1 runs. This isolates wrapper overhead from the installed native optional dependency and the workspace release binary built on the runner.

Command Runtime Input Median Throughput Samples
claude --offline --json Package wrapper 1.01 GiB 274.8ms 3.66 GiB/s 1
claude --offline --json Installed native binary 1.01 GiB 246.4ms 4.09 GiB/s 1
codex --offline --json Package wrapper 1.01 GiB 115.4ms 8.73 GiB/s 1
codex --offline --json Installed native binary 1.01 GiB 87.9ms 11.45 GiB/s 1

Committed fixture performance

Committed small fixtures for stable PR-to-PR feedback and explicit Claude/Codex command coverage.

Fixtures: Claude apps/ccusage/test/fixtures/claude (0.00 MiB, 2 files), Codex apps/ccusage/test/fixtures/codex (0.00 MiB, 1 files)
Base runs the published ccusage package from pkg.pr.new, installed before measurement; PR runs the published ccusage package from pkg.pr.new, installed before measurement. Both run --offline --json, measured by hyperfine with 2 warmups and 7 runs.
Peak RSS is measured separately with /usr/bin/time using 1 runs. Lower RSS ratios are better.

Command Input Base median PR median PR vs base Base peak RSS PR peak RSS PR/base RSS Base throughput PR throughput
claude daily --offline --json 0.00 MiB 32.0ms 32.0ms 1.00x 53.50 MiB 53.50 MiB 1.00x 0.05 MiB/s 0.05 MiB/s
claude session --offline --json 0.00 MiB 28.9ms 27.8ms 1.04x 53.75 MiB 53.75 MiB 1.00x 0.05 MiB/s 0.06 MiB/s
codex daily --offline --json 0.00 MiB 27.8ms 27.8ms 1.00x 53.75 MiB 53.75 MiB 1.00x 0.03 MiB/s 0.03 MiB/s
codex session --offline --json 0.00 MiB 29.0ms 26.8ms 1.08x 53.75 MiB 53.75 MiB 1.00x 0.03 MiB/s 0.03 MiB/s

Large real-world-shaped fixture performance

Generated fixtures shaped from aggregate local log statistics: thousands of JSONL files, many small sessions, and a long tail of larger sessions. No real prompts, paths, or outputs are stored in the fixtures.

Fixtures: Claude /home/runner/_work/_temp/ccusage-large-fixture (1.01 GiB, 2597 files), Codex /home/runner/_work/_temp/ccusage-large-codex-fixture (1.01 GiB, 2597 files)
Base runs the published ccusage package from pkg.pr.new, installed before measurement; PR runs the published ccusage package from pkg.pr.new, installed before measurement. Both run --offline --json, measured by hyperfine with 0 warmups and 1 runs.
Peak RSS is measured separately with /usr/bin/time using 1 runs. Lower RSS ratios are better.

Command Input Base median PR median PR vs base Base peak RSS PR peak RSS PR/base RSS Base throughput PR throughput
claude --offline --json 1.01 GiB 309.2ms 278.5ms 1.11x 964.34 MiB 950.57 MiB 0.99x 3.26 GiB/s 3.62 GiB/s
codex --offline --json 1.01 GiB 114.4ms 116.8ms 0.98x 395.03 MiB 419.28 MiB 1.06x 8.80 GiB/s 8.62 GiB/s

Artifact size

Artifact Base PR Delta Ratio
packed ccusage-*.tgz 18.08 KiB 18.08 KiB -0.00 KiB 1.00x
installed native package binary 3858.62 KiB 3975.50 KiB +116.88 KiB 0.97x

Lower medians and smaller artifacts are better. CI runner noise still applies; use same-run ratios as directional PR feedback, not release guarantees.

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