fix(core): use Claude cwd for workspace grouping - #30
Conversation
总体说明此 PR 解决了 Claude Code 工作区分组的规范化问题。Claude Code 将会话存储在编码的项目目录下(如 变更说明工作区元数据基础设施 在 sessions 模块中引入 Claude Code 条目级工作区支持
缓存 schema 版本更新
多客户端工作区聚合集成测试 新增 可能相关的 PR
估计审查工作量🎯 3 (中等) | ⏱️ ~20 分钟 此 PR 涉及多个文件的协调变更,但单个变更的逻辑密度不高。工作区元数据基础设施相对直观,Claude 解析的改动遵循清晰的优先级逻辑(条目级 cwd > 文件路径推导),缓存版本更新是机械性的,集成测试验证了关键行为。需要理解工作区规范化的业务背景,并跟踪条目级工作区信息在消息对象链中的传递路径。
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ 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 |
Reviewer's GuideImproves workspace grouping by preferring Claude Code cwd-derived workspace metadata, centralizing workspace key/label normalization, and invalidating caches so mixed-client sessions (Claude, Codex, Pi) merge correctly by workspace. Flow diagram for cwd-preferred workspace metadata selectionflowchart TD
A[ClaudeEntry parsed] --> B{entry.cwd is Some}
B -- yes --> C[workspace_metadata_from_key cwd]
C --> D{WorkspaceMetadata returned}
D -- yes --> E[set current_workspace_key and current_workspace_label from WorkspaceMetadata]
D -- no --> F[use workspace_key and workspace_label from claude_workspace_from_path]
B -- no --> F
E --> G[parse_claude_file_with_cache_and_home builds UnifiedMessage with current workspace]
F --> G
File-Level Changes
Possibly linked issues
Tips and commandsInteracting with Sourcery
Customizing Your ExperienceAccess your dashboard to:
Getting Help
|
There was a problem hiding this comment.
Code Review
This pull request enhances workspace metadata handling for Claude Code sessions by extracting and preferring the cwd field from individual log entries over the directory path. This allows for accurate workspace grouping across different clients (Claude, Codex, and Pi). The changes also include cache schema version bumps, helper utilities, and comprehensive tests. The reviewer feedback focuses on optimizing parsing performance by deferring and inlining the workspace key and label computations to avoid unnecessary string allocations and cloning on every line of the JSONL files.
Important
The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.
There was a problem hiding this comment.
Hey - I've found 1 issue, and left some high level feedback:
- In
workspace_parts_from_key, you now drop the workspace key entirely whenworkspace_label_from_keyreturnsNone, whereas the previous logic would still preserve a normalized key with aNonelabel; if there are edge cases where a label cannot be derived (e.g., root paths), consider keeping the key even when the label is missing to avoid silently losing workspace identity.
Prompt for AI Agents
Please address the comments from this code review:
## Overall Comments
- In `workspace_parts_from_key`, you now drop the workspace key entirely when `workspace_label_from_key` returns `None`, whereas the previous logic would still preserve a normalized key with a `None` label; if there are edge cases where a label cannot be derived (e.g., root paths), consider keeping the key even when the label is missing to avoid silently losing workspace identity.
## Individual Comments
### Comment 1
<location path="crates/tokscale-core/src/sessions/claudecode.rs" line_range="666-675" />
<code_context>
- let key = normalize_workspace_key(&window[2]);
- let label = key.as_deref().and_then(workspace_label_from_key);
- return (key, label);
+ return workspace_parts_from_key(&window[2]);
}
}
for window in components.windows(5) {
if window[0] == ".cc-mirror" && window[2] == "config" && window[3] == "projects" {
- let key = normalize_workspace_key(&window[4]);
- let label = key.as_deref().and_then(workspace_label_from_key);
- return (key, label);
+ return workspace_parts_from_key(&window[4]);
}
}
for window in components.windows(2).rev() {
if window[0] == "projects" {
- let key = normalize_workspace_key(&window[1]);
- let label = key.as_deref().and_then(workspace_label_from_key);
- return (key, label);
+ return workspace_parts_from_key(&window[1]);
}
}
</code_context>
<issue_to_address>
**issue (bug_risk):** Behavior change: keys are now dropped when a label cannot be derived
With the previous implementation, callers could still get a normalized key even if `workspace_label_from_key` failed; now `workspace_metadata_from_key` returns `None` in that case and `unwrap_or((None, None))` drops the key as well. If any callers depend on having a key when the label is missing, this could break existing behavior. If this change is intentional, consider auditing and documenting those call sites so the new semantics are explicit and surprises are avoided.
</issue_to_address>Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (1)
crates/tokscale-core/src/sessions/claudecode.rs (1)
2255-2287: ⚡ Quick win建议补一个“首条无
cwd、后续重复条目有cwd”回归测试现有新增测试覆盖了“有
cwd时优先于项目目录名”,但未覆盖去重场景下cwd后到达的情况。补这个用例可以直接防止上述回归。🤖 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 `@crates/tokscale-core/src/sessions/claudecode.rs` around lines 2255 - 2287, Add a regression test that covers the case where the first session entry lacks a cwd but a subsequent duplicate entry contains a cwd: create a new #[test] (e.g., test_workspace_metadata_prefers_later_entry_cwd_on_dedupe) which writes a session.jsonl with two messages (first without "cwd", second with the same content but including "cwd") using create_project_file, call parse_claude_file(&path), and assert that messages.len() == 1 (deduped) and that messages[0].workspace_key and messages[0].workspace_label reflect the cwd from the later entry (use the same assertion pattern as existing tests to check workspace_key and workspace_label).
🤖 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 `@crates/tokscale-core/src/sessions/claudecode.rs`:
- Around line 404-412: 去重分支在合并 token/provider 后直接 continue,导致如果首条重复项没有 cwd
而后续重复项有 cwd 时不会回写 workspace 信息;在合并重复块(涉及
entry、entry_workspace、workspace_metadata_from_key、workspace_key、workspace_label)之后应同步
workspace:检查被合并的 entry.cwd(或其 workspace_metadata_from_key 返回值),若存在则将目标条目的
workspace key/label 更新为该值(即在合并 token/provider 的分支里在 continue 前或替代 continue 的位置回写
current_workspace_key/current_workspace_label 或直接更新目标 entry 的 workspace
字段);同样修复在文件中其它类似去重分支(也适用于 458-467、508-560 区段)。
---
Nitpick comments:
In `@crates/tokscale-core/src/sessions/claudecode.rs`:
- Around line 2255-2287: Add a regression test that covers the case where the
first session entry lacks a cwd but a subsequent duplicate entry contains a cwd:
create a new #[test] (e.g.,
test_workspace_metadata_prefers_later_entry_cwd_on_dedupe) which writes a
session.jsonl with two messages (first without "cwd", second with the same
content but including "cwd") using create_project_file, call
parse_claude_file(&path), and assert that messages.len() == 1 (deduped) and that
messages[0].workspace_key and messages[0].workspace_label reflect the cwd from
the later entry (use the same assertion pattern as existing tests to check
workspace_key and workspace_label).
🪄 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: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: b887933b-a21c-4c7c-883d-bfef0c047d3a
📒 Files selected for processing (5)
crates/tokscale-cli/src/tui/cache.rscrates/tokscale-cli/tests/cli_tests.rscrates/tokscale-core/src/message_cache.rscrates/tokscale-core/src/sessions/claudecode.rscrates/tokscale-core/src/sessions/mod.rs
背景
修复 Claude Code / Codex / Pi 等本地客户端在
workspace,model分组下没有稳定归一到同一个 workspace 的问题。核心原因是 Claude Code JSONL 已经带有真实cwd,但之前主要从.claude/projects/...目录名推断 workspace,容易把-home-travis-01-workspace-...这种编码路径当成独立 workspace。关联 issue:#29
这次变更
cwd生成 workspace key/label,路径目录名只作为 fallback。cwd、后续重复消息带cwd,会把真实 workspace 回写到已合并消息。group_by渲染旧聚合数据导致一排Unknown workspace。choose_priority_columns:Model + TokensCost改为最高优先级 optional验证
cargo test:1653 passed,2 ignoredcargo build --release -p tokscale-cli/home/travis/.local/bin/tokscaletokscale --version:tokscale 3.0.0Review 处理
workspace_parts_from_key丢 key 的语义回归,并补测试覆盖 key 存在但 label 缺失的情况。cwd的问题,并补“首条无 cwd、后续重复条目有 cwd”的回归测试。