workbuddy-context-handoff · git:20260925.32ce6c0 · 2026-09-25 · sha256 c3dc386450eff90f

workbuddy-context-handoff git:20260925.32ce6c0A

Immutable. This exact content never changes and is served at /api/v1/blob/c3dc386450eff90f.

---
name: workbuddy-context-handoff
description: "给 WorkBuddy(桌面端 CodeBuddy Code 运行时)配置基于 hooks 的上下文 handoff 门禁 —— 上下文占用达阈值时自动强制产出 handoff 文档,新会话自动注入继续干活。也用于排查 WorkBuddy hooks / 上下文压缩阈值 / transcript 用量相关问题。触发场景:上下文到 N% 自动生成 handoff、交接文档、新会话续接、hooks 怎么配、为什么自动压缩了、上下文用量怎么算、context handoff、auto compact。注意:本技能的平台路径与 hook 事件名是 WorkBuddy 专有,不适用于其它 agent 运行时。"
agent_created: true
---

# WorkBuddy 上下文 handoff 门禁

## Purpose

在 WorkBuddy 桌面端搭一条"上下文快满 -> 强制产出 handoff 文档 -> 新会话自动续接"的流水线。

- **触发端**:Stop hook,按 transcript 里的 token 用量判断是否到阈值,到了就用
  `{"continue": false, "reason": "..."}` 强制模型先写 handoff。
- **注入端**:SessionStart hook,把本项目最新未被消费的 handoff 注入新会话上下文。
- 附带用途:解释/排查 WorkBuddy 的自动压缩阈值、hooks 事件与 transcript 用量计算。

## When to Use

- 用户要"上下文到 N% 自动生成 handoff / 交接文档 / 新会话续接"。
- 用户问 WorkBuddy hooks 怎么配、为什么自动压缩了、上下文用量怎么算。
- 需要定位 WorkBuddy 桌面端的运行时行为(阈值、config option、hook 事件)。
- 触发词:上下文交接、handoff、上下文压缩、自动压缩、hooks 配置、新会话续接、
  context handoff、auto compact。

## When NOT to Use

- 用户只是想知道"我现在用了多少上下文" -> 直接按 transcript 算一次回答,不必配 hook。
- 目标运行时不是 WorkBuddy 桌面端(路径、事件名、环境变量都不同)-> 不适用,先确认运行时。
- 用户要的是一份手工交接文档 -> 直接写,不需要这套门禁。

## Workflow

1. **确认位置与备份**:定位 settings(用户级 `~/.workbuddy/settings.json` 或项目级
   `<proj>/.workbuddy/settings.json`),先备份,再读现有 `hooks` 键。
2. **写三个脚本**到 `~/.workbuddy/hooks/`:`handoff-trigger.mjs`(Stop)、
   `handoff-resume.mjs`(SessionStart)、`run-hook.sh`。各文件职责与防死循环要求见
   `references/hook-implementation.md`。
3. **注册 hooks**(合并进 settings 的 `hooks` 键,不要整体覆盖):

```json
"hooks": {
  "Stop": [
    { "hooks": [ { "type": "command", "command": "<abs>/run-hook.sh <abs>/handoff-trigger.mjs", "timeout": 15 } ] }
  ],
  "SessionStart": [
    { "matcher": "startup|clear",
      "hooks": [ { "type": "command", "command": "<abs>/run-hook.sh <abs>/handoff-resume.mjs", "timeout": 15 } ] }
  ]
}
```

4. **校验 JSON**:用 `JSON.parse` 解析整个 settings 文件,确认其余键未丢。
5. **实测触发**:手动复现单个 hook —— `echo '<payload json>' | /bin/bash -lc '<command>'`,
   再跑一轮真实会话确认 handoff 产出与注入都发生。
6. **清理测试产物**:删掉 `hooks/state/` 与 `handoffs/` 下的测试文件。

## Decision Rules

- **触发点只能选 Stop**:它是唯一能强制模型继续的事件;**注入点只能选 SessionStart**:它的 stdout
  或 `hookSpecificOutput.additionalContext` 是唯一能进新会话上下文的位置。**PreCompact 不能当
  handoff 触发器**(stdout 只进压缩指令,exit 2 只给用户看消息)。
- **阈值调整优先级**:先看是不是被内置 50% pre-message 压缩截断 —— 开桌面端 **Max Mode**,
  或设 `CODEBUDDY_PRE_MESSAGE_COMPACT_PCT=80`;确认能到目标比例后,再调
  `handoff.config.json` 里的 handoff 阈值。
- **contextWindow 兜底顺序**:`handoff.config.json.contextWindow` 留 null 时依次取
  `settings.autoCompactWindow` -> `CODEBUDDY_AUTO_COMPACT_WINDOW` -> `200000`。
- **需要确认事件名 / matcher 语义 / hook 入参 / 阈值常量** -> 读 `references/runtime-facts.md`。
- **要写或改 hook 脚本本身** -> 读 `references/hook-implementation.md`。
- **hook 不触发 / 到不了阈值 / 搜不到字符串** -> 见 `## Troubleshooting`。

## Constraints

- 改 `~/.workbuddy/settings.json` 前先备份;改完必须 `JSON.parse` 校验,并确认其余键未丢。
- **所有 hook 脚本的异常分支都必须 `exit 0`**,否则会卡住对话。
- 每 session 只触发一次:Stop 脚本要检查 `stop_hook_active===true` 直接 return,并用
  `state/<sid>.fired` 去重。
- handoff 目录按 `sha1(cwd).slice(0,12)` 分项目,不要依赖 app 的 cwd-slug(不同版本大小写规则不一致)。
- 脚本内不写 Unicode 制表/装饰字符(会乱码);中文可正常写。
- 测完清理 `hooks/state/` 与 `handoffs/` 下的测试产物。
- **不要用 PreCompact 做 handoff 触发**;不要把 hook 的 stdout 当成能进用户可见消息的通道。

## Priority

1. **不卡住对话**(hook 任何异常一律 `exit 0`)> "一定要触发 handoff"。
2. **先解除原生 50% 压缩**,再调 handoff 阈值 —— 否则阈值永远达不到。
3. **防死循环**(`stop_hook_active` 检查 + 每 session 一次)> 尽量多触发。

## Stop Conditions

- handoff 到不了阈值 -> 不要反复下调阈值;先确认是否被 50% 原生压缩截断、或用户没开 Max Mode。
- `grep app.asar` 搜不到目标字符串 -> 不要反复 grep;改用
  `npx @electron/asar extract <asar> <dir>` 后再 grep。
- 改完 settings 后 `JSON.parse` 失败 -> 立即还原备份,不要继续追加改动。

## Verification

交付前逐项确认:

- [ ] `~/.workbuddy/settings.json` 已备份,且 `JSON.parse` 通过、其余键未丢。
- [ ] 三个脚本就位且可执行;每个异常分支都 `exit 0`。
- [ ] Stop 注册带 `stop_hook_active` 与 `state/<sid>.fired` 双重防死循环。
- [ ] SessionStart 注册的 matcher 为 `startup|clear`,且 `source==="compact"` 时跳过。
- [ ] 实测确认:达标时产出 handoff,新会话读到且只读一次。
- [ ] `hooks/state/` 与 `handoffs/` 下的测试产物已清理。

## Output

- **Outcome**:一条可用的 handoff 门禁 —— 上下文达阈值时自动产出 handoff 文档,
  新会话(startup / clear)自动注入该文档继续干活。
- **Done when**:`## Verification` 全部勾选,且用户能在真实会话里看到 handoff 产出与注入。
- **Evidence**:settings 中新增的 `hooks` 段;`handoffs/` 下带 `<file>.consumed` 标记的文件;
  `state/<sid>.fired` 标记;一轮实测会话的前后对比。

## References

按需加载,不要预先全读:

- `references/runtime-facts.md` — 需要确认 hook 事件名、matcher 语义、hook 入参、transcript 路径
  或内置压缩阈值常量时读。
- `references/hook-implementation.md` — 要写或修改 `handoff-trigger.mjs` / `handoff-resume.mjs` /
  `run-hook.sh` 时读。

## Troubleshooting

- **hook 不触发**:确认 `~/.workbuddy/settings.json` JSON 合法、命令用绝对路径、脚本可执行。
  手动复现:`echo '<payload json>' | /bin/bash -lc '<command>'`。
- **到不了阈值**:几乎一定是被内置 50% pre-message 压缩截断 —— 开 Max Mode,或设
  `CODEBUDDY_PRE_MESSAGE_COMPACT_PCT=80`,或降低 handoff 阈值。
- **grep app.asar 搜不到字符串**:284MB 大文件直接 grep 会漏。用
  `npx @electron/asar extract <asar> <dir>`(3-4s,约 800MB)解包后再 grep。