Skip to content

Port upstream 0.65.0: scan claude-swap session homes in Claude cost (stacked on #610) - #670

Open
Finesssee wants to merge 5 commits into
codex/integrate-reviewed-ports-20260923from
port/micro-0.65.0-claude-swap-cost-homes
Open

Finesssee wants to merge 5 commits into
codex/integrate-reviewed-ports-20260923from
port/micro-0.65.0-claude-swap-cost-homes

Conversation

@Finesssee

Copy link
Copy Markdown
Collaborator

Summary

The Claude local cost scan now also reads claude-swap session homes, so Usage & Spend totals include Claude usage written while a swapped session was active. Roots are <home>/.claude-swap-backup/sessions/<N>-<label>/projects (immediate children of sessions, <N> a positive integer, projects a directory, sorted by path). They are added beside the existing profile root, including when CLAUDE_CONFIG_DIR is set (the literal profile root is kept).

  • Roots are de-duplicated by resolved path, so a shared-history symlink or junction is scanned once.
  • A missing or dangling profile directory is skipped and does not stop the other roots.
  • All roots share the existing seen de-duplication set, so a row copied between homes counts once. Totals are aggregate only; there is no per-account attribution.
  • has_cost_usage_sources, the summary scan, the chart snapshot and both daily-history scans (cost and tokens) use the same resolver. finalize_claude_summary coverage is true when any root exists.

The resolver lives in a new rust/src/cost_scanner/claude_roots.rs (pure function over CLAUDE_CONFIG_DIR and the home directory) instead of growing cost_scanner.rs, which was already over 1000 lines.

Upstream reference

  • Release: CodexBar 0.65.0, claude-swap cost homes (triage item Harden CLI binary resolution against CWD hijacking #26).
  • Tag-pinned (v0.65.0): Sources/CodexBarCore/Providers/Claude/ClaudeConfigPaths.swift (costProjectsRoots), Tests/CodexBarTests/CostUsageScannerClaudeSwapTests.swift, CostUsageScanner+Claude.swift, SettingsStore+TokenCost.swift, docs/claude.md "Source roots".

Ported / Deferred

Ported: the legacy ~/.claude-swap-backup root (the only root claude-swap uses on Windows and macOS).

Deferred / not needed:

  • The Linux/WSL XDG root ($XDG_DATA_HOME/claude-swap/sessions): Windows-only build.
  • Upstream also lists ~/.config/claude/projects and Claude Desktop project roots beside ~/.claude/projects when CLAUDE_CONFIG_DIR is unset. The local scanner keeps its existing single-root fallback (~/.claude/projects, else ~/.config/claude/projects); that is unrelated to this item.

Validation

Toolchain cargo +1.98.0, slot-6 target dir, E-core wrappers.

  • cargo fmt --all: clean
  • cargo clippy --workspace --all-targets -- -D warnings: pass
  • cargo test -p codexbar cost_scanner: 126 passed, 0 failed
  • cargo test -p codexbar claude_: 93 passed, 0 failed (includes 7 claude_roots resolver tests and the end-to-end claude_swap_homes_are_scanned_once_beside_a_literal_config_dir)
  • cargo test -p codexbar (full): 2235 passed, 0 failed, 1 ignored (pre-existing)

Fixtures are synthetic re-creations of the upstream cases: two homes with one row each, a copied row in both, a symlink to shared history (skipped only if the OS refuses to create it), a dangling profile directory, a literal CLAUDE_CONFIG_DIR plus swap homes, and non-numeric / 0-foo directories ignored.

Affected areas

  • Rust backend (rust/src/cost_scanner)
  • Tauri shell
  • Frontend
  • Settings / tray / float bar

UI proof

Not applicable. Totals change; the dashboard and tray consume the same summary.

@coderabbitai

coderabbitai Bot commented Sep 29, 2026 •

Copy link
Copy Markdown

Important

Review skipped

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

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

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Advanced

Run ID: 137bc4fc-900f-44fa-a60a-5fe702d1f696

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

Use the checkbox below for a quick retry:

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

Autopilot is currently an internal CodeRabbit preview.


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

❤️ Share

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

@Finesssee

Copy link
Copy Markdown
Collaborator Author

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

Thermo-nuclear review of #670 (claude-swap session homes in the Claude cost scan). Findings:

  • Medium, cost_scanner/claude_roots.rs and cost_scanner.rs: errors while discovering swap homes (unreadable sessions directory, unreadable entries, failed metadata) were silently ignored, so a partial total could look complete. Fixed: discovery failures are counted as read failures in every scan path (summary, quota scan, daily cost and token history) and count toward has_cost_usage_sources.
  • Low, claude_roots.rs: a slot name with an empty label (3-) passed validation although the spec requires <N>-<label>. Fixed, test updated.
  • Low, claude_roots.rs: resolved-root de-duplication used a linear search. Fixed with a HashSet.

@Finesssee

Copy link
Copy Markdown
Collaborator Author

Follow-up: all three findings above are fixed and pushed. Nothing left open. The change is backend only (no user-visible UI change), so no CUA pass was needed.

Commands run (Rust 1.98.0, E-cores): cargo fmt --all; clippy --all-targets -D warnings on rust and apps/desktop-tauri/src-tauri (clean); cargo test --lib cost_scanner (127 passed), including the new test that an unreadable sessions path is reported as a read failure.

Accept an empty label after the slot number (upstream splits with
omittingEmptySubsequences: false), translate both
CostUsageScannerClaudeSwapTests cases with their token and cost
assertions and the two-minute rescan, and cover shared-history
de-duplication through a junction, which needs no symlink privilege.
@Finesssee

Copy link
Copy Markdown
Collaborator Author

Lane A review: fixes at 2808b1b

Reviewed the whole diff against the base (#610 at 15f1091) and against upstream v0.65.0 (ClaudeConfigPaths.costProjectsRoots, CostUsageScanner+Claude.swift, CostUsageScannerClaudeSwapTests.swift).

What changed on the branch:

  • 3f3fccf: merged the current Integrate reviewed provider, history, and tray ports #610 head (15f1091) into the branch, so the stack no longer trails its base.
  • 610da6c: slot names now match upstream's split(separator: "-", maxSplits: 1, omittingEmptySubsequences: false) plus Int(parts[0]) > 0. An empty label (3-) and a signed number (+5-plus) are accepted like Swift; 0-x, -1-x, abc-1, 7, hidden entries and an overflowing number are rejected. The previous code rejected an empty label.
  • 610da6c: translated both upstream CostUsageScannerClaudeSwapTests cases into cost_scanner/tests/claude_swap.rs: two swap homes contribute once across copied and shared history (decoys unrelated and 0-zero, 320 tokens, $0.0012, stable on a rescan), and a literal CLAUDE_CONFIG_DIR containing a comma stays beside discovered swap homes (a missing extra root counts as one read failure).
  • 610da6c: added a junction-based shared-history test. Symlinks need a privilege on Windows, so the symlink test skips on most machines; the junction test always runs and proves resolved-path de-duplication.
  • 2808b1b: clippy let_underscore_must_use fix in the optional symlink fixture.

Checked and unchanged: base root first, then swap homes sorted by path; missing roots omitted; discovery failures counted so a partial history is not reported as a complete zero; only projects directories are read (no claude-swap credentials or metadata). ~/.config/claude/projects and Claude Desktop roots are a pre-existing 0.64.1 gap, not part of this PR, as the body says.

Validation at 2808b1b (toolchain 1.98.0, W:\cargo-target\lane-a):

UI proof: not needed. The change is backend only (cost scan roots); the cost totals it feeds are rendered by existing surfaces.

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