拿到一个 session 文件(.jsonl),你能从里面还原出多少当时的运行环境?逐行拆开后, 结论反直觉:transcript 只忠实记录对话本身,几乎不记录运行环境。 工具的参数 schema、当前的 CLAUDE.md、系统提示词都不落盘 —— 它们由 system-reminder 在 每次请求时动态注入、发完即弃。所以这些信息,从 session 文件里读不回来。
想完整重建当时的上下文,这三样环境信息缺一不可。 可从 session 文件里,它们还原得出来吗?
所有挂载的工具,它们的参数 schema 和说明文字,能从 transcript 里还原吗?
当前可用的全部 skill,连同它们的 description,还原得出来吗?
注入到上下文里的项目说明,作为环境的一部分,它被记下来了吗?
直觉说:既然换台机器也能 resume,这些环境信息必然被写进了 JSONL,等着被读回来。 逐行拆开后,三个答案是:只还原得出名字、名单加描述能还原、完全还原不出。
{ "type": "attachment", "attachment": { "type": "deferred_tools_delta", "addedNames": [ "CronCreate", "EnterPlanMode", "Monitor", "TaskCreate", "WebFetch", "WebSearch" … 仅工具名 ], "pendingMcpServers": [ "plugin:chrome-devtools-mcp:chrome-devtools" ] } } // 整行里没有任何 description / inputSchema 字段
完整的工具描述从不落盘。transcript 里只留下「有哪些工具可调」这张名单。
真正的参数 schema 和说明文字,是运行时由 ToolSearch 按需拉取的,
用完即弃。所以一个工具长什么样、怎么调,永远是当下从注册表现查,
而不是从历史里读出来。
这也解释了为什么 deferred_tools_delta 是 delta(增量):它只在工具集
发生变化时追加一行,记录这一刻多了哪些、少了哪些名字。它是一份变更日志,
不是工具的权威定义。
能还原:某一刻活跃的工具名单。还原不出:参数 schema 与说明 —— 全库 299 条 delta 无一携带。
{ "type": "attachment", "attachment": { "type": "skill_listing", "skillCount": 60, "isInitial": true, "names": ["caveman", "diagnose", "handoff" …×60], "content": "- caveman: Ultra-compressed communication…\n - diagnose: Disciplined diagnosis loop…\n - handoff: Compact the conversation into a handoff document…\n - prototype: Build a throwaway prototype…" } } // 每个 skill 的一行描述都在,只是不含 SKILL.md 正文
三个问题里,只有 skill 列表把内容真正写进了磁盘 — 而且写得很全。
content 是一段拼好的 Markdown:每个 skill 用于触发判断的那行描述
(来自 frontmatter)都完整在列,一个不少。names 给名单,
content 给描述。
它唯一没带上的,是各 skill 的完整 SKILL.md 正文 — 而这正是预期行为。
正文只在某个 skill 真正激活时才按需读取,没必要在开场就全塞进上下文。所以这份 listing
不是被截断的影子,而是分层加载里恰到好处的那一层:描述齐全,足够判断该不该用。
能还原:完整名单 + 每个 skill 的一行描述(全库 297 条)。还原不出: SKILL.md 正文 —— 按需加载,本就不在文件里。
<system-reminder> # claudeMd Contents of /…/code-journal/CLAUDE.md (project instructions, checked into the codebase): # code-journal Open-source tool to collect AI coding-agent sessions and turn them into work reports… </system-reminder> // 它不是独立的一行类型。每次请求现读磁盘, // 现拼进 message #1,发完即丢,不单独留档。
claudeMd — 而那 4 个正是研究本课题留下的 session,关键词出现在 grep 的 tool_result 里。
CLAUDE.md 没有自己的行类型,没有自己的 attachment。它根本不在 transcript 里。
它以 system-reminder 的形式,在每次请求时现读磁盘,
现拼进第一条 user 消息的开头,随这条消息发给模型,发完即丢。
JSONL 里不为它单独留一行档案。
最有力的证据是那个自我指涉的巧合:全库唯一能搜到 claudeMd 的,
是几个正在研究这个问题的会话,因为它们 grep 过自己。
对照本页 — 你现在读到的 CLAUDE.md 内容,也来自这样一条即时注入的 reminder。
可还原性为零:文件里没有它的一个字节。而 resume 照常带着 CLAUDE.md 工作 — 那它必然是每次重新生成的。
别问「环境存在哪」,问「从一个 transcript 里到底读得出什么」。答案很干脆:只读得出 不变的对话;环境每次请求动态构建,从来不在文件里。
已发生、不重算 — 唯一落盘的
transcript 是对话的忠实日志。能从它可靠读出的,只有这些「不会再变」的东西。
每次请求动态注入 — 不留存
由 system-reminder 在每次请求时即时拼装、注入、用完即弃。文件里偶尔出现的 skill_listing、tools 增量只是某一刻的注入快照,不是当前环境的可信副本。
所以一个 session 答得了「聊了什么」,答不了「环境是什么样」。 环境每次请求重新拼 — 这正是 resume 照常工作、且自动套用当下环境的原因。
打开任意一个 session,你会遇到的 attachment 记录类型就这 12 种 —— 元数据的「可还原面」到此为止。跨 312 个 transcript 的出现次数,前两名印证了结论:起手就记名单,从不记内容。
session 文件是对话的忠实档案,却是环境的盲区。当时挂着哪些工具、哪版 CLAUDE.md、 哪些 skill —— 这些都不在文件里留存,逐行翻遍也读不回来。
唯一的还原路径是回到运行现场:当时的磁盘、当时的 CLI 版本、当时活动的 MCP 连接。 而它们可能早已漂移 —— 这也正是为什么 resume 一个旧会话,模型读到的是此刻的环境, 而非当初那一份。