Chengshu@skadai · 2026.10.20
RSS
1,809 字 · 373 词 · 约 6 分钟

handoff 精读:让工作『旅行』,而不是把对话再讲一遍

handoff 把当前对话压成一份交接文档,存进操作系统的临时目录,让另一个 agent 接着干。本文说明它给的是可携带性而不是压缩、它和 implement-spec 的『分支 + worktree』是同一条线的两端、以及它为什么坚持只写指针不抄内容、坚持落在 temp、坚持不自动触发——还有那个写在文档正文明文里的代价。

(本文读的是 mattpocock/skills 快照 5beb35b,2026-10-19。引用原文都能按文中路径回仓库核对。这是「逐个技能精读」系列的第十篇,聚焦 handoff 一个技能。)

全文十六行

skills/productivity/handoff/SKILL.md 全文:

---
name: handoff
description: Compact the current conversation into a handoff document for another agent to pick up.
argument-hint: "What will the next session be used for?"
disable-model-invocation: true
---

Write a handoff document summarising the current conversation so a fresh agent can continue the work. Save to the temporary directory of the user's OS (`$TMPDIR`, else `/tmp`; `%TEMP%` on Windows) - not the current workspace.

Include a "suggested skills" section in the document, naming which skills the next agent should call the Skill tool for.

Do not duplicate content already captured in other artifacts (specs, plans, ADRs, issues, commits, diffs). Reference them by path or URL instead.

Redact any sensitive information, such as API keys, passwords, or personally identifiable information.

If the user passed arguments, treat them as a description of what the next session will focus on and tailor the doc accordingly.

触发方式是 /handoff <说明下一次会话要干什么>。产出是一个 markdown 文件,落在你操作系统的临时目录里;除了「在飞的事、为什么、下一步」,它还固定带一节 suggested skills——点名下一个 agent 该调哪些技能。密钥、token 这类东西在落盘前先抹掉。

它和「一般技能」的差别:它给的是可携带性,不是压缩

理解这个技能最容易走偏的地方,是把它当成「更好的 /compact」。SKILL.md 的 description 写着 “Compact the current conversation”——读起来像「压缩并继续」。文档纠正得很直接:

What it gives you is portability, not compression, so the skill is narrower than it sounds. You need a file only when the work has to travel: to a new harness, a new directory, a colleague, or a side task you want to fork off. If nothing is travelling, you do not need a handoff.

所以它有且只有四种触发情况:换 harness(Claude → Codex)、换目录或换仓库、交给同事、从当前工作里分叉出一个支线。其余在阶段边界上的需求——同一个 harness、同一个目录、你只是刚 grill 完要转去实现——用 /compact 就好。

三个选项保留的东西不一样,这是选型的全部依据:/compact 保留你的意图,/clear 什么也不保留,/handoff 保留工作移动的能力。文档还有一句值得抄的观察:这三种都是把一手来源(对话本身)变成二手来源(它的摘要),唯一不这么做的是「继续」,所以要先把它排除掉。

四种触发里,被跳得最多、也最值钱的是分叉。因为技能介绍读起来像「写总结、结束、在别处恢复」,很多人就把它当成一个更麻烦的 /compact。真正的用法相反:你留在原会话里,把攒下来的上下文复制一份给第二个 agent 并行干。

prototype 的往返就是这条路的实例——你在设计对话里撞上一个只有跑代码才能回答的问题,又不想把攒起来的线索花在试错上:hand off 给一个 prototype 会话,拿回答案,再从原对话里引用它。工作来回过了两趟,你的原对话一直活着,而且没重新解释任何东西。

它和 implement-spec 是同一条线的两端

implement-spec 有一条几乎一字不差的规则:

Communication to and from subagents should be sparse. Communicate primarily through context pointers: to the spec, tickets, research notes, and previous commits. Don’t duplicate information already available via pointers.

handoff 用的是同一把尺子,只是量在不同的刻度上:

Do not duplicate content already captured in other artifacts (specs, plans, ADRs, issues, commits, diffs). Reference them by path or URL instead.

两者面对的是同一个问题:上下文边界之外的工作,要靠什么传过去。 implement-spec 的答案是「worktree + 分支」——每个子代理在自己的工作树里干自己那一票,成果作为一条分支旅行,编排者只拿指针(spec、工单、研究笔记、先前的 commit)说话。handoff 的答案是「一份文档 + 它指向的路径」——工作作为一份可以带走的文件旅行,而已经写在别处的东西(spec、ADR、工单、commit、diff)一律只留链接。

两条线还能接上:implement-spec 的实现者在自己的分支上干活,那条分支本身就是可被交接的东西;而一次跑了一半、要换 harness 或换机器的实现,恰恰需要 handoff 把「在飞的事、为什么、下一步」写下来,再用指针把分支和工单串起来。分支是给 git 用的交接,文档是给人和其他 harness 用的交接。

它有意识摒弃了什么

一、摒弃了写进工作目录。 SKILL.md 把这句写得很硬:not the current workspace。理由是定位——“Temp is deliberate, because a handoff is a document in transit, not an artifact you maintain.” 它是一次转场,不是一份要维护的产物。要长期留下的东西不属于它,文档给了一条更锋利的判据:「下个月还是真的吗?」 是,就写进 CLAUDE.md;只是一件在飞的事,才写进 handoff。

二、摒弃了抄已有的信息。 见上。文档把这条的价值拆成两层:文件因此更小,而且已定的细节只有一个出处,不会出现两份互相漂移的副本。

三、摒弃了密钥。 这条被写进 SKILL.md 正文,而不是留给用户自觉,因为 in-progress/claude-handoff(同源的进阶层版本)里的文档会直接变成下一个 agent 的 prompt——它写明 “since the summary becomes the agent’s prompt”。

四、摒弃了自动触发。 disable-model-invocation: true。什么时候需要让工作「旅行」,是一个人的判断,不是模型能替你定的。

五、摒弃了在分叉时动原会话。 分叉的用法里,你留下来继续干,第二个 agent 拿着副本走。所以「回来之后原会话没有变化」被列成了一条验收判据。

三个它没藏起来的代价

一、它写的是 what,不是 why。 文档说这条批评是中肯的,给出的缓解手法很实用:调用时把参数给全(告诉它下一次会话要干什么),它就会保留和那件事相关的推理,而不是把一切压平;另外,读一遍再交出去,把会话里没验证过的自信断言降级——“a belief written as a fact becomes a false premise for everything that follows”。

二、temp 不持久。 Codex 会在会话之间清 temp,重启会清空 /private/tmp。如果下一个会话不是一小时内开始、或者要换 harness,就自己把文件复制到有出处的目录;同时注意文件里指向的东西也得能跟上,指向 temp 里其它文件的派发,下一个 agent 是读不到的。这条是真实 bug 修出来的:4f4e943 让技能「每次用同一种方式定位临时目录并报出路径」,因为之前 “agents took several attempts to find the temp dir on Windows, and wrote to a different place on each run on macOS”;紧接着 0f5e033 又把修正的措辞削成「命名 OS 临时目录的位置,好让 agents stop guessing」——连一版 changeset 说明都在往「不许猜」上收。

三、交给下一个 agent 的方式很容易做错。 文档专门说:指向那个文件,别把摘要贴进 shell 命令。因为 claude "<summary>" 会把摘要里的反引号或 $(...) 展开,通常是静默截断而不是报错,于是新 agent 带着一份不完整的简报开工,而没有任何东西告诉你。

原文证据

「它在工作」的判据里,前两条正好划出了它的边界:

  • The document is a small fraction of the conversation, and the specs, issues and diffs appear in it as paths and URLs rather than as copied text.
  • You can read it cold, without the original session open, and know what to do next.

而它和 harness 的关系,在 fc73c04 里做了切割。那次提交把 in-progress/claude-handoff 泛化成一个 spawn 技能,提交信息说:

SKILL.md now only defines the handoff; per-agent launch mechanics (claude –bg vs backgrounded codex exec) live in a bundled spawn.sh the agent runs but never reads.

技能只定义交接,启动机制交给脚本。 这和 implement 把测试规则推给 tdd 是同一种洁癖:具体 harness 的细节不进散文,散文只管那些每个 harness 都成立的部分。

可迁移结论

  1. 先问「工作移动了吗」。 没移动就别写文件;/compact 更便宜,也更少出错。
  2. 交接文档里只放指针。 已经写在 spec、ADR、工单、commit 里的内容,一律用路径或 URL 引用——文件更小,而且不会出现第二份会过期的副本。
  3. 把交接产物放进 temp。 临时位置暴露了它的性质:一次转场,不是一件要维护的资产。长期成立的事实属于 CLAUDE.md。
  4. 文档会变成 prompt,所以风险要在源头处理。 抹掉密钥、检查断言,因为你写下的每一句都会被下一个 agent 当成事实。
  5. 分叉比恢复更值钱。 你不必结束当前会话就能开一条支线,也不用重新讲一遍背景——这才是「可携带」在日常里最常用的一面。

handoff 是这个系列里最像「运维工具」的一篇:它不谈设计、不给原则,只解决一个物理问题——上下文装不下的工作,怎么完整地换个地方继续。

讨论

用 GitHub 账号留言;评论保存在公开仓库chengshu-blog-discussions的 Discussions 里。也可通过 RSS 订阅后续文章。