Claude Code 把 checkpoint 数量限制在最近 100 个、生命周期 30 天(默认可配),这不是两个孤立的数字,而是对 Agent Loop 中两类资源——磁盘 I/O 与会话状态成本——的显式权衡。100 是给”高频回溯”的窗口,30 天是给”低频复盘”的时间,二者分别解决不同的问题。下面拆开它们的取值逻辑,以及为什么这两个数字放在一起能稳定支撑一个长会话。
一、checkpoint 解决的是什么问题
Claude Code 在每个用户提示前,自动给被编辑过的文件做一次快照存盘,这条快照与对话本身绑定——回溯会话、跨进程 resume 之后,/rewind 仍然能跳到这些点上。它不是版本控制,也不是 git 替代品,而是 Agent Loop 的”会话级撤销”,让模型在”我刚才往哪走了”上不会失忆。
checkpoint 的工作流可以拆成五步:
- 收到用户提示后,识别本轮可能被改动的文件集合;
- 对集合内每个文件做全量快照,存到
~/.claude/projects/<session>/; - 把提示内容、当前对话位置、文件快照索引一起写入会话 JSONL;
- Claude 编辑期间,所有写入仅作用于工作树,不污染快照;
- 用户触发 /rewind 时,按选中的 checkpoint 恢复文件或对话或两者。
实现上,checkpoint 由”提示前的文件状态”加上”对话历史”两部分组成,二者都进 JSONL 存储,落到 ~/.claude/projects/ 下。回溯操作 /rewind(或空 prompt 时双击 Esc)调出这个列表,可选恢复代码、恢复对话、恢复代码+对话、从此处/此处前总结。
# 在 v2.1.117 起,cleanupPeriodDays 同时管四类缓存:
# ~/.claude/projects/ 会话与 checkpoint
# ~/.claude/tasks/ 持久任务列表
# ~/.claude/shell-snapshots/ shell 环境快照
# ~/.claude/backups/ settings/CLAUDE.md 滚动备份
把”文件快照”和”对话快照”绑定存储,意味着 checkpoint 不能只管文件——一旦对话被清掉,文件快照也失去回溯语义,只能落得个”磁盘垃圾”的下场。这是为什么 30 天是一个会话级数字,而不是文件级数字。
二、100 这个数量:对磁盘 I/O 的取舍
每个 checkpoint 都要把被改过的文件复制一份存盘,如果一个会话改了 30 个文件、平均 5KB,100 个 checkpoint 就意味着 15MB 的冗余存储——这是保守估计,真实场景里大文件、频繁编辑会让数字迅速膨胀。Claude Code 不做”差异存储”,而是用全量文件快照,理由很简单:全量快照恢复时不依赖任何前置 checkpoint,语义最简单。
| 数量档位 | 存储压力(估) | 回溯窗口 | 适用场景 |
|---|---|---|---|
| 100 个(默认) | 数十 MB 级 | 最近 100 次提示 | 普通开发会话 |
| 20 个 | 几 MB 级 | 最近 20 次提示 | 短任务、CI 自动化 |
| 500 个 | 数百 MB 级 | 最近 500 次提示 | 大型重构、长跑 session |
100 这个数字的隐性约束是”高频回溯”:典型用户在一次会话里,能回溯到的最远合理点大约是 50–80 个提示之前(再往前,上下文记忆已经显著退化,回溯语义也模糊)。再往上堆,就是”为了存而存”。Claude Code 直接砍掉 100 之后更早的快照,既不浪费磁盘,也不会让 /rewind 菜单变成无意义的历史浏览。
淘汰机制不是简单的 FIFO,被淘汰的 checkpoint 引用的快照文件,只要没有其他 checkpoint 引用,就一并删除,VS Code 扩展还会保留每个文件的”首份快照”作为 diff 基准;v2.1.208 之前这些被替代的快照要等会话结束才删,现在变成随时回收,显著降低了磁盘占用抖动。
三、30 天这个时间:对会话状态成本的取舍
会话级 checkpoint 不同于 git 提交,它承载的是”我与这个项目的当前状态”,不是”这个项目历史上的所有状态”。一旦停止维护这个会话超过 30 天,继续保留它就是双输:磁盘成本继续累加,但用户回来的概率已经低到不值得为它保留回溯能力。
30 天的另一个隐性含义是覆盖常见的”中断-复盘”周期:用户可能在周末开一个新会话,下周一回来 /resume 找上个会话的细节;30 天足够覆盖这种节奏,再长就属于”归档”范畴,git 才是正确的工具,而不是 checkpoint。同时,v2.1.117 把 30 天扩展到四类缓存(任务列表、shell 快照、CLAUDE.md 备份、会话 checkpoint)统一管理,避免”会话清了但任务列表还在”这种半清理的尴尬。
// settings.json
{
"cleanupPeriodDays": 30
}
配置点集中在 cleanupPeriodDays 一个字段,默认值 30,用户改这个值时四类缓存同步生效。这种”一刀切”的清理策略简化了用户心智模型——你不用记”哪种文件保留多久”,只需要问自己”我还会回来吗”,30 天是大多数人”不回来”的合理上界。
四、Agent Loop 中的两类资源权衡
把上面两点合起来看,100 和 30 天并不是随意拍出来的两个数字,而是 Agent Loop 中两类显式资源的工程平衡。
| 资源 | 约束体现 | 取舍点 |
|---|---|---|
| 磁盘 I/O(快照存储) | checkpoint 数量上限 100 | 多了浪费空间,少了回溯窗口太短 |
| 会话状态成本(留存的会话数) | 30 天保留窗口 | 长了磁盘累积,短了复盘窗口不够 |
Claude Code 的设计哲学是”以中等会话为主、极端会话交给用户显式选择”:绝大多数开发会话在 100 个 prompt、30 天内能结束,默认值刚好覆盖;对于真的需要超长会话、跨月留存的少数场景,用户该用 git 提交 + 分支而不是依赖 checkpoint。这个分层把”高频撤销”和”长期版本”明确切开,避免一个机制试图同时解决两类问题。
五、与 git 的关系:分层而非替代
很多团队一开始会把 checkpoint 当成 git 替代品,然后在某次 rm -rf 后失望——bash 命令的文件变更不会被跟踪,subagent 跨 worktree 改的文件也不在 checkpoint 范围里。这是设计上的取舍,不是缺陷:checkpoint 只追踪 Claude Code 自己的文件编辑工具产生的变更,git 负责追踪一切(包括 bash、子代理、外部编辑器)。
把”会话级撤销”留给 checkpoint,把”项目级版本”留给 git,二者配合形成的双层结构是:checkpoint 兜住 30 秒到 20 分钟内的”刚才那个改动是错的”,git 兜住”上个月那个版本怎么回滚”。前者讲速度,后者讲持久。
六、落地时的两个易错点
第一,不要因为有 checkpoint 就省掉 git commit。v2.1.216 之后,/rewind 还会跳过通过符号链接/硬链接解析的路径,这是为了防止误删跨工作区文件——但反过来,任何依赖符号链接的开发环境,checkpoint 都不完整;这类项目必须用 git 兜底。第二,把 cleanupPeriodDays 调到 0 看似”立刻清”,实际是”立刻全清”,所有 checkpoint 与任务列表同步消失,正在编辑的项目也会丢快照,通常只该在排查”为什么磁盘被吃满”时临时开启,日常用默认 30 天即可。
常见问题(FAQ)
Q1:超过 100 个 checkpoint 会怎样?
排在最前的几个会直接淘汰,引用到的快照文件若没有其他引用一并删除,VS Code 扩展的”首份快照”保留作为 diff 基准,不会因为数满而报错。
Q2:30 天从哪天开始算?
从会话最后活跃时间算,不是从创建时间;持续编辑的会话窗口会顺延,真正闲置到 30 天才清理。
Q3:能不能只调大 checkpoint 数量而不动 30 天?
没有单独的配置项;100 是硬上限,调不了;30 天可独立调,但调大后磁盘累积会按四类缓存同步放大,需同步关注 ~/.claude/ 占用。