Repository navigation
perf(rust): split the workspace into independently cached crates - #1428
Conversation
Move shared types, pricing, configuration, output, date, and block logic into ccusage-core so adapter crates can depend on an acyclic foundation. Keep the ccusage package focused on command dispatch and move Codex-owned types into its adapter boundary. Co-authored-by: ryoppippi <[email protected]>
|
Note Reviews pausedIt 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 Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
📝 WalkthroughWalkthroughThe PR extracts Rust agent implementations into dedicated adapter crates, centralizes shared APIs in ChangesAdapter crate migration
Build and repository integration
Estimated code review effort: 5 (Critical) | ~120 minutes Possibly related PRs
Suggested reviewers: 🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
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. Comment |
Deploying with
|
| Status | Name | Latest Commit | Preview URL | Updated (UTC) |
|---|---|---|---|---|
| ✅ Deployment successful! View logs |
ccusage-guide | 79a958e | Commit Preview URL Branch Preview URL |
Jul 27 2026, 11:02 AM |
Keep Insta snapshot ownership aligned with the new ccusage-core crate so its existing characterization tests continue to compare the same outputs. Co-authored-by: ryoppippi <[email protected]>
Move JSONL parsing, file walking, date filtering, size-balanced chunking, and ordered parallel reads into ccusage-adapter-common. This removes the Claude adapter as an infrastructure dependency before agent crates are split. Co-authored-by: ryoppippi <[email protected]>
ccusage
@ccusage/ccusage-darwin-arm64
@ccusage/ccusage-darwin-x64
@ccusage/ccusage-linux-arm64
@ccusage/ccusage-linux-x64
@ccusage/ccusage-win32-x64
commit: |
ccusage performance comparisonPR SHA: This compares the Rust PR release binary against the configured base package on the same CI runner. Package runtime diagnosticsCompares 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
Committed fixture performanceCommitted small fixtures for stable PR-to-PR feedback and explicit Claude/Codex command coverage. Fixtures: Claude
Large real-world-shaped fixture performanceGenerated 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
Artifact size
Lower medians and smaller artifacts are better. CI runner noise still applies; use same-run ratios as directional PR feedback, not release guarantees. |
ccusage performance comparisonPR SHA: This compares the PR package against the configured base package on the same CI runner. Package runtime diagnosticsCompares 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
Committed fixture performanceCommitted small fixtures for stable PR-to-PR feedback and explicit Claude/Codex command coverage. Fixtures: Claude
Large real-world-shaped fixture performanceGenerated 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
Artifact size
Lower medians and smaller artifacts are better. CI runner noise still applies; use same-run ratios as directional PR feedback, not release guarantees. |
ccusage performance comparisonPR SHA: This compares the PR package against the configured base package on the same CI runner. Package runtime diagnosticsCompares 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
Committed fixture performanceCommitted small fixtures for stable PR-to-PR feedback and explicit Claude/Codex command coverage. Fixtures: Claude
Large real-world-shaped fixture performanceGenerated 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
Artifact size
Lower medians and smaller artifacts are better. CI runner noise still applies; use same-run ratios as directional PR feedback, not release guarantees. |
ccusage performance comparisonPR SHA: This compares the Rust PR release binary against the configured base package on the same CI runner. Package runtime diagnosticsCompares 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
Committed fixture performanceCommitted small fixtures for stable PR-to-PR feedback and explicit Claude/Codex command coverage. Fixtures: Claude
Large real-world-shaped fixture performanceGenerated 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
Artifact size
Lower medians and smaller artifacts are better. CI runner noise still applies; use same-run ratios as directional PR feedback, not release guarantees. |
ccusage performance comparisonPR SHA: This compares the Rust PR release binary against the configured base package on the same CI runner. Package runtime diagnosticsCompares 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
Committed fixture performanceCommitted small fixtures for stable PR-to-PR feedback and explicit Claude/Codex command coverage. Fixtures: Claude
Large real-world-shaped fixture performanceGenerated 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
Artifact size
Lower medians and smaller artifacts are better. CI runner noise still applies; use same-run ratios as directional PR feedback, not release guarantees. |
ccusage performance comparisonPR SHA: This compares the PR package against the configured base package on the same CI runner. Package runtime diagnosticsCompares 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
Committed fixture performanceCommitted small fixtures for stable PR-to-PR feedback and explicit Claude/Codex command coverage. Fixtures: Claude
Large real-world-shaped fixture performanceGenerated 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
Artifact size
Lower medians and smaller artifacts are better. CI runner noise still applies; use same-run ratios as directional PR feedback, not release guarantees. |
Move every agent adapter and the unified all-agent aggregator into dedicated ccusage-adapter-* crates. Preserve their tests and snapshots with each owning crate, keep shared infrastructure in ccusage-adapter-common, and leave ccusage as thin command wiring. Co-authored-by: ryoppippi <[email protected]>
Build the shared foundation and each adapter as filtered Crane cargoBuild derivations, merge their real target artifacts, and compile only the unified adapter plus thin binary at the final layers. Apply the same target-specific graph to native, musl, and Darwin cross packages. Co-authored-by: ryoppippi <[email protected]>
ccusage performance comparisonPR SHA: This compares the PR package against the configured base package on the same CI runner. Package runtime diagnosticsCompares 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
Committed fixture performanceCommitted small fixtures for stable PR-to-PR feedback and explicit Claude/Codex command coverage. Fixtures: Claude
Large real-world-shaped fixture performanceGenerated 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
Artifact size
Lower medians and smaller artifacts are better. CI runner noise still applies; use same-run ratios as directional PR feedback, not release guarantees. |
ccusage performance comparisonPR SHA: This compares the Rust PR release binary against the configured base package on the same CI runner. Package runtime diagnosticsCompares 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
Committed fixture performanceCommitted small fixtures for stable PR-to-PR feedback and explicit Claude/Codex command coverage. Fixtures: Claude
Large real-world-shaped fixture performanceGenerated 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
Artifact size
Lower medians and smaller artifacts are better. CI runner noise still applies; use same-run ratios as directional PR feedback, not release guarantees. |
Co-authored-by: ryoppippi <[email protected]>
Update repository guidance and source references for the new ccusage-adapter-* workspace layout and shared core/common boundaries. Co-authored-by: ryoppippi <[email protected]>
Crane emits full artifact archives on macOS, so merging sibling adapter archives can overwrite shared crate variants. Keep per-adapter layers on Linux and fall back to the safe dependency-only cache for native and cross Darwin packages. Co-authored-by: ryoppippi <[email protected]>
ccusage performance comparisonPR SHA: This compares the Rust PR release binary against the configured base package on the same CI runner. Package runtime diagnosticsCompares 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
Committed fixture performanceCommitted small fixtures for stable PR-to-PR feedback and explicit Claude/Codex command coverage. Fixtures: Claude
Large real-world-shaped fixture performanceGenerated 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
Artifact size
Lower medians and smaller artifacts are better. CI runner noise still applies; use same-run ratios as directional PR feedback, not release guarantees. |
ccusage performance comparisonPR SHA: This compares the PR package against the configured base package on the same CI runner. Package runtime diagnosticsCompares 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
Committed fixture performanceCommitted small fixtures for stable PR-to-PR feedback and explicit Claude/Codex command coverage. Fixtures: Claude
Large real-world-shaped fixture performanceGenerated 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
Artifact size
Lower medians and smaller artifacts are better. CI runner noise still applies; use same-run ratios as directional PR feedback, not release guarantees. |
Move generic JSON row and period helpers from the OpenCode adapter into ccusage-core. Remove the resulting OpenCode dependency from leaf adapters and trim obsolete direct dependencies from the binary crate.
Regenerate, format, diff, and stage models.dev and Codex fallback snapshots at their new owning crate paths. Watch the moved config schema source in treefmt as well.
Build independent adapters directly from the shared foundation. Root the current Linux native artifact graph and garbage-collect superseded store generations before saving that cache, preventing per-commit artifact accumulation.
ccusage performance comparisonPR SHA: This compares the Rust PR release binary against the configured base package on the same CI runner. Package runtime diagnosticsCompares 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
Committed fixture performanceCommitted small fixtures for stable PR-to-PR feedback and explicit Claude/Codex command coverage. Fixtures: Claude
Large real-world-shaped fixture performanceGenerated 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
Artifact size
Lower medians and smaller artifacts are better. CI runner noise still applies; use same-run ratios as directional PR feedback, not release guarantees. |
ccusage performance comparisonPR SHA: This compares the PR package against the configured base package on the same CI runner. Package runtime diagnosticsCompares 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
Committed fixture performanceCommitted small fixtures for stable PR-to-PR feedback and explicit Claude/Codex command coverage. Fixtures: Claude
Large real-world-shaped fixture performanceGenerated 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
Artifact size
Lower medians and smaller artifacts are better. CI runner noise still applies; use same-run ratios as directional PR feedback, not release guarantees. |
Expose only each adapter's intentional command and loader surface while keeping parser, path, report, and implementation modules crate-private.
ccusage performance comparisonPR SHA: This compares the Rust PR release binary against the configured base package on the same CI runner. Package runtime diagnosticsCompares 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
Committed fixture performanceCommitted small fixtures for stable PR-to-PR feedback and explicit Claude/Codex command coverage. Fixtures: Claude
Large real-world-shaped fixture performanceGenerated 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
Artifact size
Lower medians and smaller artifacts are better. CI runner noise still applies; use same-run ratios as directional PR feedback, not release guarantees. |
ccusage performance comparisonPR SHA: This compares the PR package against the configured base package on the same CI runner. Package runtime diagnosticsCompares 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
Committed fixture performanceCommitted small fixtures for stable PR-to-PR feedback and explicit Claude/Codex command coverage. Fixtures: Claude
Large real-world-shaped fixture performanceGenerated 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
Artifact size
Lower medians and smaller artifacts are better. CI runner noise still applies; use same-run ratios as directional PR feedback, not release guarantees. |
ccusage performance comparisonPR SHA: This compares the Rust PR release binary against the configured base package on the same CI runner. Package runtime diagnosticsCompares 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
Committed fixture performanceCommitted small fixtures for stable PR-to-PR feedback and explicit Claude/Codex command coverage. Fixtures: Claude
Large real-world-shaped fixture performanceGenerated 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
Artifact size
Lower medians and smaller artifacts are better. CI runner noise still applies; use same-run ratios as directional PR feedback, not release guarantees. |
hawk lints a Cargo workspace for `pub` that the workspace itself never needs, which is exactly the surface a 24-crate split creates. It uses `rustc_private` and must run on the toolchain it was built against, so the pin moves to 1.97.1; the dev shell picks that up from rust-toolchain.toml with no flake change. hawk.toml names the two production entry points rather than letting hawk assume every binary target is shipped, so the reason each one is an entry point is written down. 1.97.1's clippy also wants a byte-string literal in parse_timezone_offset.
Splitting one crate into 24 turned a lot of module-private items into `pub`, because that is what crossing a crate boundary needs — but only some of them are actually crossed. hawk's closed-world analysis over the two production entry points found 85 such declarations; this applies the 68 machine-applicable ones, turning `pub` into `pub(crate)` and dropping restricted modifiers that no longer restrict anything.
The remaining hawk findings were public re-exports that no other crate reaches: each adapter re-exported report_from_rows even though only its own run() calls it, claude re-exported three path helpers used only inside itself, and codex re-exported two types its own parser and replay modules use. Splitting each statement keeps every name visible where it is used and stops advertising it past the crate boundary. Cli::parse had no callers at all — the binary goes through parse_from_with_config because it must pass a config context — so it is gone and its parse_from shorthand is now test-only. hawk reports no findings.
A 24-crate workspace needs a stated rule for `pub`, or the next crate will export whatever compiles. The rust skill now says what the rule is, how `just hawk` checks it, that hawk must be installed separately because it is not in nixpkgs, why the toolchain is pinned to 1.97.1, and that its findings are suggestions rather than a gate — it is explicitly experimental.
The schema-gen treefmt formatter still listed config_schema.rs under ccusage-core and the generator under the ccusage package, so after both moved to ccusage-config it never ran on the files whose changes should regenerate the schema. The config-schema flake check would still have caught the drift, but only after a failing build instead of a formatting pass.
There was a problem hiding this comment.
1 issue found across 37 files (changes from recent commits).
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/adapters/qwen/src/lib.rs">
<violation number="1" location="rust/adapters/qwen/src/lib.rs:18">
P2: The README still documents `report::report_from_rows` as part of the public surface, but this change makes it `pub(crate)` only. Since the function is only used internally (in `run()` and tests), the narrower visibility is fine — please update the README's Public surface list to remove this entry so the docs stay accurate.</violation>
</file>
Tip: Review your code locally with the cubic CLI to iterate faster.
Re-trigger cubic
| pub(crate) use loader::load_entries; | ||
| pub(crate) use report::{report_from_rows, summarize_entries}; | ||
| pub use loader::load_entries; | ||
| pub(crate) use report::report_from_rows; |
There was a problem hiding this comment.
P2: The README still documents report::report_from_rows as part of the public surface, but this change makes it pub(crate) only. Since the function is only used internally (in run() and tests), the narrower visibility is fine — please update the README's Public surface list to remove this entry so the docs stay accurate.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At rust/adapters/qwen/src/lib.rs, line 18:
<comment>The README still documents `report::report_from_rows` as part of the public surface, but this change makes it `pub(crate)` only. Since the function is only used internally (in `run()` and tests), the narrower visibility is fine — please update the README's Public surface list to remove this entry so the docs stay accurate.</comment>
<file context>
@@ -15,7 +15,8 @@ use crate::{
pub use loader::load_entries;
-pub use report::{report_from_rows, summarize_entries};
+pub(crate) use report::report_from_rows;
+pub use report::summarize_entries;
</file context>
ccusage performance comparisonPR SHA: This compares the Rust PR release binary against the configured base package on the same CI runner. Package runtime diagnosticsCompares 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
Committed fixture performanceCommitted small fixtures for stable PR-to-PR feedback and explicit Claude/Codex command coverage. Fixtures: Claude
Large real-world-shaped fixture performanceGenerated 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
Artifact size
Lower medians and smaller artifacts are better. CI runner noise still applies; use same-run ratios as directional PR feedback, not release guarantees. |
ccusage performance comparisonPR SHA: This compares the PR package against the configured base package on the same CI runner. Package runtime diagnosticsCompares 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
Committed fixture performanceCommitted small fixtures for stable PR-to-PR feedback and explicit Claude/Codex command coverage. Fixtures: Claude
Large real-world-shaped fixture performanceGenerated 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
Artifact size
Lower medians and smaller artifacts are better. CI runner noise still applies; use same-run ratios as directional PR feedback, not release guarantees. |
There was a problem hiding this comment.
✅ No new issues found.
Reviewed changes — 4 commits since the last review: a systematic workspace visibility audit, hawk tooling for automated enforcement, and documentation of the new visibility discipline.
- Narrowed
pubtopub(crate)orfnacross all ~20 workspace crates — every item that no other crate uses lost itspubvisibility. Struct fields (Pricing::cache_create*,IsoDate::year/month/day,UtcParts::millisecond,TokenCounts::extra_total_tokens,CacheCreationRawfields), standalone functions (apply_config_*,parse_timezone_offset,parse_digits, calendar helpers,format_*), adapter exports (report_from_rows), and type helpers (merge_codex_service_tiers,aggregate_model_breakdowns,parse_project_aliases) were narrowed. Items the binary crate needs becamepub(load_entries,summarize_entries,run,calculate_cost_*,PricingMapmethods). - Dead code removed —
Cli::parse()(no callers; binary usesparse_from_with_config), unusedweekday_from_sundayandchecked_add_daysonIsoDate. - Hawk integration — toolchain bumped to 1.97.1,
rust/hawk.tomldeclares the two production entry points, andjust hawkreportspubitems nothing outside their crate needs. - Documented visibility discipline — the rust skill and AGENTS.md now state:
pubonly for what another crate requires,pub(crate)for everything else, withjust hawkas the automated check.
@v0 or keep the SHA fresh with Dependabot | View workflow run | Using DeepSeek Pro (free via Pullfrog for OSS) | 𝕏
There was a problem hiding this comment.
✅ No new issues found.
Reviewed changes — 1 commit since the last review: fixes the stale treefmt schema-gen includes paths that the prior review flagged.
- Fixed
treefmt.nixschema formatter paths —config_schema.rsnow points torust/crates/ccusage-config/src/config_schema.rs(wasccusage-core) andgenerate_config_schema.rsnow points torust/crates/ccusage-config/src/bin/generate_config_schema.rs(wasccusage/src/bin). Treefmt will once again auto-generate the schema when those files change locally.
@v0 or keep the SHA fresh with Dependabot | View workflow run | Using DeepSeek Pro (free via Pullfrog for OSS) | 𝕏
ccusage performance comparisonPR SHA: This compares the Rust PR release binary against the configured base package on the same CI runner. Package runtime diagnosticsCompares 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
Committed fixture performanceCommitted small fixtures for stable PR-to-PR feedback and explicit Claude/Codex command coverage. Fixtures: Claude
Large real-world-shaped fixture performanceGenerated 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
Artifact size
Lower medians and smaller artifacts are better. CI runner noise still applies; use same-run ratios as directional PR feedback, not release guarantees. |
ccusage performance comparisonPR SHA: This compares the PR package against the configured base package on the same CI runner. Package runtime diagnosticsCompares 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
Committed fixture performanceCommitted small fixtures for stable PR-to-PR feedback and explicit Claude/Codex command coverage. Fixtures: Claude
Large real-world-shaped fixture performanceGenerated 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
Artifact size
Lower medians and smaller artifacts are better. CI runner noise still applies; use same-run ratios as directional PR feedback, not release guarantees. |
…til-adapter Resolves the workspace split from #1428, which moved the files this branch touches into separate crates: - `date_utils` now lives in `ccusage-core`. The window helpers added here (`date_within_range`, `date_range_bounds_ms`) are `pub` rather than `pub(crate)`, because the opencode adapter that calls them is its own crate now; `parse_compact_date` and `start_of_day_ms` stay private. `parse_digits` keeps main's private, overflow-checked version. - `filter_loaded_entries_by_date` moved from the claude adapter to `ccusage-adapter-common`. Main's copy still inlined the date predicate, so it now calls the shared `date_within_range` instead — the point of collapsing the three copies in the first place. - `opencode::has_data` follows the adapter crate's new `pub` surface.
…1510) * fix(pricing): commit the codex fallback snapshot from its real path The models.dev job has failed on every hourly run since #1428 moved the adapter crates: fatal: pathspec 'rust/crates/ccusage-adapter-codex/src/codex-auto-review-fallbacks.json' did not match any files The file lives at rust/adapters/codex/src/codex-auto-review-fallbacks.json, so models.dev pricing has not been refreshed since that move; only the LiteLLM half of the workflow still worked. The path was duplicated between the update script and the workflow, which is how it drifted from the Rust tree in the first place. The script now reports the paths it owns through GITHUB_OUTPUT and the workflow passes that straight to the push action, so there is one list. The script also asserts up front that the snapshots exist, so the next move fails immediately instead of after a full regenerate-and-validate cycle. * refactor(pricing): share the GITHUB_OUTPUT report helper between lock scripts Both update scripts carried a byte-identical `report` definition, so any change to the GITHUB_OUTPUT format had to be made twice. Move it to a `.github/scripts/pricing-lock.nu` module both scripts import. Nushell resolves relative `use` paths against the importing file, so the module loads regardless of the workflow's working directory. Co-authored-by: Codesmith <[email protected]> --------- Co-authored-by: Codesmith <[email protected]>

Summary
Splits the Rust runtime so that a change recompiles what depends on it and nothing
else, and makes CI actually build the resulting graph in parallel.
each get their own crate (
ccusage-adapter-*,ccusage-adapter-common)ccusage-cli, which everyruntime crate depends on, for
ccusage-cli-parserccusage-configtakesconfig.rs+config_schema.rs, and with themschemars,out of
ccusage-coreccusage-coretoo, so it holds only what every crate genuinely sharesmax-jobs = 1was doing on 32-vCPU runnersgenerate-config-schemamoves toccusage-config, which takes the whole adaptergraph off the path to the preflight gate
rust/adapters/<agent>for the per-sourcecrates,
rust/cratesfor everything that is not tied to a single source, and[workspace.package]+[workspace.dependencies]for the settings all of themshare
What a change costs now
Units recompiled for
cargo build -p ccusage --bin ccusage, measured on a warmtarget directory:
cli-help.json)ccusage-coreThe Nix layers agree: editing
cli-help.jsonorccusage-configleaves thefoundationandadaptersartifact layers byte-identical, verified by comparingthe adapters layer derivation path before and after each edit.
ccusage-coreisthe one crate whose fan-out is unavoidable, which is why the bin-only parts left
it.
Why the layering changed shape
The first iteration built one artifact derivation per adapter. That could not be
used on macOS at all: Crane installs full rather than incremental archives there
(rust-lang/rust#115982), and
merging sibling full archives can overwrite shared path-crate artifacts with
incompatible variants, so macOS fell back to the dependency-only cache and
recompiled everything on every build.
A linear chain — dependencies → foundation → adapters → binary — only ever extends
the previous layer, so it needs no merging and works everywhere. The 15 adapters
share one layer because they are mutually independent: one
cargoBuildwith a-pper crate lets Cargo compile them concurrently, so the layer costs about as much as
its slowest adapter (measured: codex at 12.4s against 69.2s CPU for all 15), while
the fan-out added five serial Nix round-trips that each unpack and re-archive the
227 MB dependency target directory.
Why CI parallelism had to be fixed first
nix-quick-install-actionwrites thenix.confit is given verbatim, so Nix keptits documented default of
max-jobs = 1:nix flake check's independent checks andevery artifact layer were built one at a time. Without this, no layering shape can
pay off — and this one line helps
maintoo.What left ccusage-core
derives are where
schemarsenters the graph.cargo tree -p ccusage-corenolonger mentions it.
SessionBlock,BurnRate, andProjectiontravel with it.ureqentered through one 25-line function andbrought rustls, ring, webpki and ~20 more crates that had to compile before core,
and therefore before every adapter. Core now takes the fetcher as a function
pointer that
maininstalls. With none installed a refresh reports it isunavailable and the embedded snapshots are used, which is the existing
--offlinebehaviour.
progress::UsageLoadAgentwas a closed 16-variant enumplus a label match, so adding an agent meant editing the crate every adapter
depends on. It is a newtype around the label now; the labels are unchanged.
build.rs—minreqpulled rustls'saws-lc-rsprovider on top of the
ringproviderureqalready needed, and compilingaws-lc's C and assembly sources took longer than any other unit in the graph
(~66s CPU). No Nix build could ever use it, since the sandbox has no network and
every package passes
CCUSAGE_PRICING_JSON_PATH. It now sits behind anoff-by-default
fetch-litellm-pricingfeature that only the Windows release buildenables, and the dev shell exports the same pinned snapshot.
Also: the adapter manifests were copies of one dependency block, so
sqlitenowappears only in the four crates that open a database and
instaonly where asnapshot test exists;
print_table_for_agentmoved toccusage-adapter-common, whichremoved the goose and codebuff dependencies on the amp crate; and
ccusage-clippyno longer takes the whole repository as its source, so a README edit stops
invalidating the most expensive check in
nix flake check.The schema generator was pulling the adapter graph
generate-config-schemawas a secondbinof theccusagepackage, so building itcompiled every adapter even though it only calls into the config layer. It now lives
in
ccusage-config, where a clean build is 3 units, and its Nix derivation startsfrom the foundation artifacts instead of the adapter ones.
That is a wall-clock change rather than a tidiness one, because treefmt depends on
this generator and the preflight job builds treefmt — so every build job in the
matrix was waiting behind 15 adapters that the formatter never needed.
Measured effect on CI
Three consecutive
mainruns (the pre-split monolith, whose durations vary by onlya few seconds) against a warm-cache run of this branch. Cache state dominates
everything here, so the branch run used is one whose push changed no manifest,
meaning it restored the previous run's cache the way a normal push does.
mainWindows is measured separately because it is the one platform Nix cannot target: it
runs plain
cargo buildwithactions/cache. Itscargo buildstep, three runseach side:
maincargo buildThat is the split paying off directly — the monolith compiled as one crate with
codegen-units = 1, while 22 smaller crates compile concurrently. Whole-jobduration on that runner is not a useful signal: rustup toolchain provisioning took
4s in one run and 4m36s in another.
What the remaining wall clock is
Whole-workflow wall clock is roughly unchanged (
main9.7–10.3m, this branch 9.9m).The critical path, from job timestamps on one branch run:
The preflight gate in front of the build matrix is deliberate — the comment in
ci.yamlsays so: secret scan and lint should fail before any build or test workstarts. Making that gate cheap is exactly what the schema generator change buys.
Everything after the builds is inherent to testing a package the way a user gets
it: the npm publish has to happen before the e2e matrix can install from it.
Binary size
The split costs binary size, and this is the honest accounting: each crate
instantiates the shared generic machinery for its own types, and stable Rust cannot
share those instantiations across crates. Measured on the shipped Linux binary,
downloaded from the CI artifacts of each side:
mainmain--icf=allLinker dead-stripping recovers none of the 240,768 bytes (measured: byte-identical
output with
-Wl,-dead_strip), because the duplicates are reachable code rather thandead code. Identical-code folding is the lever that applies, and it recovers 158,720
of them, with another 20,928 coming from the narrowed visibility below; nothing in the tree depends on two
fnitems having distinct addresses.macOS therefore links with lld rather than ld64, because ld64 has no ICF. The Nix
build goes from 3,299,504 to 3,232,928 bytes (−2.0%), and lld also links more than
twice as fast: a relink of the release binary takes 20s against 42s with ld64. Both
macOS packages still link only
/usr/liband/System/Librarydylibs.Layout and manifests
Each adapter manifest used to repeat the same package fields and dependency
specs, so adding a crate meant copying a block and a version bump meant editing 23
files. Those now live in
[workspace.package]and[workspace.dependencies], anda crate manifest is a list of what that crate actually uses:
ccusage-adapter-commonsits with the adapters rather than beside the unifiedreport for a build reason as well as a conceptual one: a Cargo member glob that
matches nothing is an error, and the foundation artifact layer is built from a
source tree containing no other adapter.
Visibility, and the toolchain bump that checks it
A 24-crate split is a machine for producing
pubthat nothing outside the workspaceever needs, so the branch runs
hawk, aworkspace-aware Cargo lint for unnecessary Rust visibility. It found 85 such
declarations that the split had introduced, and the branch resolves all of them:
pubbecomespub(crate)wherever the workspace is the only client, and onefinding was genuinely dead code —
Cli::parsehad no callers, because the binaryparses through
parse_from_with_configto pass a config context — so it is gone.just hawkreports no findings.hawk uses
rustc_privateand must run on the toolchain it was built against, so thepin moves to Rust 1.97.1; the dev shell picks that up from
rust-toolchain.tomlwithno flake change.
rust/hawk.tomldeclares the two production entry points — theccusagebinary andgenerate-config-schema— rather than letting hawk infer thatevery binary target is shipped, so the reason each one counts as an entry point is
written down.
Documentation
Every crate has a
README.mdstating what it owns, what belongs elsewhere, itspublic surface, and which artifact layer it lands in — the last one being what
determines how much a change to it costs. The
rustskill points at them and statesthe rule the amp coupling broke: adapters do not depend on each other.
Testing
cargo test --manifest-path rust/Cargo.toml --workspace(48 suites)cargo clippy --manifest-path rust/Cargo.toml --workspace --all-targets -- -D warnings, with and without--features fetch-litellm-pricingnix build .#ccusage .#ccusage-tests .#checks.aarch64-darwin.ccusage-clippy .#checks.aarch64-darwin.config-schemajust fmt,just typecheckmainbinary built on the same machine:--help,codex --help,daily --help,blocks --help, and--json --offlinefor the unified, daily, and codex reports are byte-identical on local data--offlinedoes not)cargo tree -p ccusage-corecontains noschemars,ureq,rustls,ring,webpki,minreq, oraws-lc-sysFollow-up
pricing.rsis core's largest module at 2,850 LOC and is genuinely shared, but itmixes the rate tables, the models.dev cache, and the lookup logic; splitting it
inside core would let a rate-table tweak recompile less.