随笔

Claude Code 源码解读 09:可观测性怎样让会话能恢复和复盘

从 transcript、parentUuid 链、content replacement、resume、background task 和 session state 看 Claude Code 的恢复与审计能力。

2026-05-03 Claude Code源码解读可观测性

可观测性不是“有日志”,而是哪些消息能恢复、哪些只是 UI progress、大工具结果如何替代、resume 恢复哪些运行事实。

快速了解

  • 可观测性不是有日志,而是有可恢复的消息链。
  • progress 是 UI 状态,不应该污染 transcript 恢复链。
  • resume 恢复的不只是 messages,还包括 worktree、agent、mode、cost、context collapse 等运行事实。
  • content replacement 让大工具输出既不挤爆上下文,又能保持恢复一致性。

如果只想快速理解,可以先读这一节;如果要看源码机制,再往下看“主线与场景插入分类”和机制图。

机制图
机制图

设问与回应

设问回应
recordTranscript() 写的是日志,还是可恢复消息链?recordTranscript() 写的不是普通日志,而是可恢复消息链。它记录用户消息、assistant 输出、附件、system 等可进入恢复轨迹的事实。
为什么 progress 不属于 transcript message?progress 不属于 transcript message,因为它是 UI 进度状态,不是会话语义事件。把 progress 写进恢复链,会污染 parentUuid chain,甚至造成恢复分叉。
content replacement 如何同时解决大输出和恢复一致性?content replacement 的价值在于把大工具输出替换成可引用、可恢复的内容形态:上下文不会被大输出挤爆,恢复时仍能知道原始结果如何被承接。
processResumedConversation() 恢复的状态为什么不只是 messages?processResumedConversation() 恢复的不只是 messages,因为 Harness 行为还依赖 session id、metadata、worktree、agent、mode、cost、context collapse 等运行事实。只恢复文本,系统不一定还能以同样边界继续执行。

主线与场景插入分类

机制/模块分类我保留的判断
recordTranscript()主线 + 可观测/恢复支撑用户消息、assistant、attachment、system 等可恢复消息写入 JSONL
isTranscriptMessage()主线恢复支撑定义 transcript 可恢复消息类型;progress 不属于恢复链
parentUuid chain可观测/恢复支撑通过父子链恢复会话轨迹,跳过 progress 防止 chain fork
processResumedConversation()主线恢复支撑恢复 session id、metadata、worktree、agent、mode、cost、context collapse
notifySessionStateChanged()场景插入 + 可观测支撑向 CCR / SDK / bridge 暴露 idle、running、requires_action 等状态

恢复不是把历史消息再发一次

真正的 resume 需要恢复运行环境的一部分事实:session id、metadata、worktree、agent、mode、cost、context collapse 等。如果只恢复 messages,模型也许能继续说话,但 Harness 行为已经变了。

系列收束:从 Codex 差异看 Harness 的可恢复性

先找主线功能,再找场景插入,再找治理横切,最后看恢复支撑。这套拆法也适用于 Codex:比较重点不在模型能力,而在 harness 如何组织协议、状态、权限、失败路径和审计材料。

在可观测性这一篇里,Codex 对比应该拆成几个更具体的问题:哪些事件会进入可恢复消息链,哪些只属于 UI progress;resume 恢复的是文本历史,还是 session id、worktree、agent、mode 这类运行事实;大工具结果被替换后,是否还保留足够证据支撑复盘;异常中断后,系统能否沿 parent chain 回到同一条协作轨迹。这个对比带来的设计理解是:agent harness 的可观测性不是记录越多越好,而是把“未来恢复还需要的事实”从“当下展示用的状态”里分离出来。