claw-code 会话分区机制解析:基于 FNV-1a 指纹的 Clone/Worktree 会话消歧契约 📅 发布时间:2026/9/18 22:49:33 👁 浏览次数: claw-code 会话分区机制解析基于 FNV-1a 指纹的 Clone/Worktree 会话消歧契约【免费下载链接】claw-codeAn agent-managed museum exhibit, built in Rust with Gajae-Code / LazyCodex — developed and maintained with no human intervention.项目地址: https://gitcode.com/gh_mirrors/claudeco/claw-code本技术指南围绕 claw-code 仓库的 G010「clone disambiguation metadata」契约文档展开讲解 Claw 会话状态如何按工作区克隆/worktree 进行隔离从.claw/sessions/workspace_fingerprint/目录布局、FNV-1a 指纹算法、SessionStore构造与解析 API到遗留会话的安全策略、CLI 包装与全套验证命令。读完本文你将掌握这套以工作区分区为身份边界、而非以裸 session id 为边界的设计并能在本地复现每一条契约验证。契约摘要会话分区的身份边界G010 契约文档docs/g010-clone-disambiguation-metadata.md首先确立了一个核心原则Claw 会话状态被有意限定在当前工作区 clone/worktree 内。运维人员与自动化工具应把会话分区session partition而不是裸 session id 或扁平的.claw/sessions/目录当作身份边界。其背后动机很直接同一台机器上可能同时存在同一个仓库的多个 clone 或多个 git worktree。若会话只按 session id 区分两个 clone 中的会话 id 一旦碰撞就会互相覆盖若把.claw/sessions/当作扁平的全局目录则在 CWD 不同的地方执行--resume就可能加载到错误工作区的会话。G010 的契约把工作区显式引入会话的命名空间从根上消除了这类串扰。契约要求的五类行为可归纳如下契约点行为要求工作区分区Workspace-bound partition托管会话存放于.claw/sessions/workspace_fingerprint/指纹是规范工作区路径的稳定 16 字符 FNV-1a 摘要规范路径输入Canonical path inputSessionStore::from_cwd与SessionStore::from_data_dir在计算分区前先规范化工作区路径避免/tmp/foovs/private/tmp/foo、相对 vs 绝对路径拼写产生两个存储克隆/worktree 隔离两个不同的 clone 或 worktree 必须获得不同的会话分区即使 session id 碰撞遗留安全Legacy safety扁平遗留会话仅在绑定到同一工作区、或未绑定但物理上位于当前工作区内时方可读取持久化workspace_root指向其他 clone 的会话以WorkspaceMismatch拒绝Fork 血统保持本地/session fork/ 托管会话 fork 将派生会话保留在同一工作区分区并记录 parent id 与可选分支名面向用户的消歧空会话提示信息指名实际指纹目录并说明其他 CWD 的会话被有意隐藏目录布局与指纹算法.claw/sessions/workspace_hash/的由来两个构造函数最终都生成data_dir/sessions/workspace_hash/的布局其中workspace_hash是规范工作区根路径的稳定十六进制摘要SessionStore::from_cwd(cwd)布局为cwd/.claw/sessions/workspace_hash/会话目录在首次成功保存时惰性创建SessionStore::from_data_dir(data_dir, workspace_root)布局为data_dir/sessions/workspace_hash/配合显式--data-dir标志使用。对应实现见 rust/crates/runtime/src/session_control.rs。from_cwd与from_data_dir都会先对路径执行fs::canonicalize失败时例如目录尚不存在回退到原始路径// #151: canonicalize so equivalent paths (symlinks, relative vs absolute, // /tmp vs /private/tmp on macOS) produce the same workspace_fingerprint. let canonical_cwd fs::canonicalize(cwd).unwrap_or_else(|_| cwd.to_path_buf());这一步解决了一个真实回归场景macOS 上/tmp是指向/private/tmp的符号链接若直接哈希原始路径字符串/tmp/foo与/private/tmp/foo会算出两个不同的指纹从而为同一个 clone 创建两个存储同时相对路径与绝对路径的不同拼写也会产生同样的问题。ROADMAP.md:5797-5902记录了这条规范化闭合 symlink/路径等价分裂的演进ROADMAP.md。FNV-1a 指纹的源码实现指纹算法定义于 session_control.rs 的workspace_fingerprint函数/// Stable hex fingerprint of a workspace path. /// Uses FNV-1a (64-bit) to produce a 16-char hex string that partitions the /// on-disk session directory per workspace root. pub fn workspace_fingerprint(workspace_root: Path) - String { let input workspace_root.to_string_lossy(); let mut hash 0xcbf2_9ce4_8422_2325_u64; for byte in input.as_bytes() { hash ^ u64::from(*byte); hash hash.wrapping_mul(0x0100_0000_01b3); } format!({hash:016x}) }要点说明采用 64 位FNV-1a哈希偏移基值0xcbf29ce484222325质数0x100000001b3对规范路径的字节序列逐字节异或后乘质数输出为{hash:016x}即固定 16 个十六进制字符64 位全覆盖这与契约中稳定 16 字符的描述完全一致该哈希是确定性的同一路径总是得到同一指纹不同路径几乎必然不同——单元测试workspace_fingerprint_is_deterministic_and_differs_per_pathsession_control.rs验证了同路径同指纹、异路径异指纹、长度恒为 16三条性质。另外注意指纹分区目录的父目录.claw/sessions/之上还存在一个全局会话根~/.claw/sessions/或$CLAW_CONFIG_HOME/sessions/由global_sessions_root()提供session_control.rs。latest别名在分区内找不到会话时会回退扫描全局根与项目本地父目录下的所有指纹子目录以支持跨工作区 resume见下文latest 与引用解析。托管会话的管理与解析 API契约要求 create/resolve/list/load/fork 等全部在活动分区内解析这组 API 集中在 session_control.rscreate_handle(session_id)在sessions_root下生成id.jsonl句柄resolve_reference(reference)/resolve_reference_excluding(reference, exclude_id)引用可为别名latest/last/recent或显式 session id/路径别名命中时委托给latest_session_excluding显式引用则先在分区内查找.jsonl/.json文件再回退到遗留目录并做工作区校验list_sessions()收集分区内及遗留目录中的会话摘要按updated_at_ms降序、再按文件 mtime、最后按 id 排序sort_managed_sessionssession_control.rslatest_session()/latest_session_excluding(exclude_id)优先在当前分区取最新且message_count 0的会话过滤掉 0 消息的空会话分区内为空时才回退scan_global_sessions()做全工作区扫描session_control.rsload_session(reference)加载后执行validate_loaded_session校验别名引用走load_session_excluding允许跨工作区 resume 并打印来源提示显式引用仍强制校验session_control.rsfork_session(session, branch_name)调用session.fork(branch_name)继承当前workspace_root写入同一分区的新id.jsonl并返回ForkedManagedSession { parent_session_id, handle, session, branch_name }session_control.rs。会话文件扩展名有明确约定session_control.rspub const PRIMARY_SESSION_EXTENSION: str jsonl; // 当前托管格式 pub const LEGACY_SESSION_EXTENSION: str json; // 遗留格式对应别名集合session_control.rspub const LATEST_SESSION_REFERENCE: str latest; const SESSION_REFERENCE_ALIASES: [str] [LATEST_SESSION_REFERENCE, last, recent];is_session_reference_alias对这些别名做大小写不敏感匹配session_control.rs。遗留会话的安全策略WorkspaceMismatch.claw/sessions/扁平目录下的旧版legacy会话并不被直接信任。加载路径上由validate_loaded_sessionsession_control.rs把关若会话带有workspace_root且与当前 store 的规范工作区匹配 → 允许读取若会话没有workspace_root未绑定但文件物理位于当前工作区路径内path_is_within_workspace→ 允许读取若持久化的workspace_root指向另一个 clone/worktree → 返回SessionControlError::WorkspaceMismatch { expected, actual }其中expected是当前工作区actual是会话原始工作区。该错误类型的显示格式为session workspace mismatch: expected 当前, found 原始session_control.rs。在 CLI 层面这个错误会原样透传给用户见下文 CLI 回归测试让用户立刻知道这个会话属于另一个工作区。这一设计同时解决了ROADMAP.md:1125-1129记录的会话文件按工作区指纹命名空间化错误工作区的会话访问被拒绝的产品要求。用户可见的消歧错误信息指名指纹目录契约专门要求空会话/找不到会话的提示不能误导用户去扁平目录里找。相关错误格式化函数session_control.rs都会从sessions_root中取出实际指纹目录名即.claw/sessions/下的最后一级目录并写进提示format_missing_session_referencesession not found: refHint: managed sessions live in .claw/sessions/fingerprint/ (workspace-specific partition).format_no_managed_sessionsno managed sessions found in .claw/sessions/fingerprint/并附带/resume latest会搜索所有工作区的说明format_all_sessions_emptyall sessions are empty (0 messages) in .claw/sessions/fingerprint/解释通常是新会话尚无消息format_legacy_session_missing_workspace_root指名遗留会话文件路径并提示回到原始工作区或从当前工作区重新保存。代码注释明确记录了意图#80show the actual workspace-fingerprint directory instead of lying about .claw/sessions/。这正是ROADMAP.md:1419-1441所要求的空/缺失会话消息必须暴露指纹目录而非暗示扁平目录搜索。CLI 包装同一分区的统一入口CLI 侧通过current_session_store()把会话命令统一路由到同一个SessionStore保证claw的 list/latest/load 与运行时共享同一分区逻辑rust/crates/rusty-claude-cli/src/main.rsfn current_session_store() - Resultruntime::SessionStore, Boxdyn std::error::Error { let cwd env::current_dir()?; runtime::SessionStore::from_cwd(cwd).map_err(|e| Box::new(e) as Boxdyn std::error::Error) } fn create_managed_session_handle(session_id: str) - ResultSessionHandle, Boxdyn std::error::Error { let handle current_session_store()?.create_handle(session_id); Ok(SessionHandle { id: handle.id, path: handle.path }) } fn resolve_session_reference(reference: str) - ResultSessionHandle, Boxdyn std::error::Error { let handle current_session_store()?.resolve_reference(reference) ...; }也就是说无论用户通过--resume latest、/session list、/session load还是latest/last/recent别名操作会话最终都落在以当前 CWD 的规范路径指纹为分区的同一套存储上。CLI 的 session-list 还渲染当前分区的 saved-only/dirty/abandoned 生命周期上下文main.rs。聚焦验证映射如何在本地复现契约G010 文档提供了完整的验证命令集全部指向 runtime 与 CLI 包的真实单元测试。以下逐条给出可复现命令与对应源码锚点契约声明验证命令测试实现锚点同一规范工作区的等价路径拼写共享一个分区cargo test --manifest-path rust/Cargo.toml -p runtime session_store_from_cwd_canonicalizes_equivalent_paths -- --nocapturesession_control.rs#151 回归不同 clone/worktree 互不可见对方会话cargo test --manifest-path rust/Cargo.toml -p runtime session_store_from_cwd_isolates_sessions_by_workspace -- --nocapturesession_control.rs显式 contenteditable="false">【免费下载链接】claw-codeAn agent-managed museum exhibit, built in Rust with Gajae-Code / LazyCodex — developed and maintained with no human intervention.项目地址: https://gitcode.com/gh_mirrors/claudeco/claw-code创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考