Retro
指令檔案靠堆積生長。每一條規則都是某人看著代理做錯事之後加上去的,而沒人衡量這條規則之後有沒有用。Retro 把這個迴圈閉合。它記錄護欄何時被打破,衡量每次工作階段花了多少 token,然後以 OpenSpec 的形式提出對指令、hook 和技能的具體修改,由你逐行核可或駁回。
什麼都不會離開這台機器。什麼都不會自動套用。
它記錄什麼
一個 Claude Code hook 解析 Claude Code 本來就保存在磁碟上的對話紀錄,並把中繼資料附加到 .coograph/signals.jsonl(被 git 忽略)。它在 SessionEnd 時執行,每次工作階段壓縮時也執行。第二個觸發點才讓長工作階段變得可讀:跑一整天的工作階段只會抵達一次結束,而且很晚,被強制結束就根本沒有;壓縮是它唯一會反覆觸發的事件。重複擷取是安全的,因為一次工作階段的紀錄是整體替換而不是附加。warn-scope.py 和 block-generated.py 這兩個 hook 每次觸發時也附加一筆記錄。
每筆違規都帶著它發生的那一刻,取自引發它的那則訊息,而不是擷取執行的時刻。
每筆記錄包含工具名、計數、規則 id、儲存庫相對檔案路徑、shell 命令的雜湊和程式名、時間戳記和 token 用量。它永遠不包含提示文字、助理文字、工具輸出、檔案內容或完整命令。允許清單寫在程式碼裡強制執行,還有一個哨兵測試:在一份對話紀錄的每個部分埋下標記字串,只要標記進了訊號檔案就失敗。
偵測器
| 偵測器 | 規則 | 信心 | 觸發條件 |
|---|---|---|---|
graph-first | graph-first | 確定性 | 在本次工作階段第一次存取程式碼圖之前(mcp__code-graph__* 工具,或同時包含 sqlite3 和 .code-graph 的 shell 命令)出現了 Grep 或 Glob 呼叫,且 .code-graph/graph.db 存在。會記錄之後是否碰到了程式碼圖(mcp-later、sqlite-later)還是從未碰到(total-bypass)。子代理的呼叫不計。 |
openspec-gate | openspec-gate | 啟發式 | 編輯了兩個以上原始檔,工作階段中沒有任何操作觸碰 openspec/changes,且不存在進行中的變更目錄 |
scope-warning | scope | 確定性 | warn-scope.py 印出了它的警告 |
generated-file-block | generated-files | 確定性 | block-generated.py 攔截了一次編輯 |
build-retry | 無 | 確定性 | 同一條 shell 命令跑了三次以上,其中至少兩次失敗 |
user-correction | user-correction | 啟發式 | 一則使用者訊息在工具呼叫之後符合某個糾正模式(只記錄模式 id)。圍繞同一主題反覆糾正,是「某條指令缺失或含糊」最清楚的證據,所以它們歸到自己的規則底下聚類 |
defect | defect | 確定性 | 工作階段時間窗內的一次修復提交,落在了過去 14 天內被非修復提交改過的檔案上。每對「修復提交 + 來源提交」出一筆訊號,只帶檔案清單和兩個短雜湊,其他什麼都不帶。專案根是儲存庫就掃根目錄,否則掃它下面一層的各個儲存庫,因此根目錄下放著 app/ 和 admin/ 的專案也涵蓋得到 |
new-dependency | no-new-deps | 確定性 | npm install <pkg>、pip install <pkg>、uv add、cargo add、go get 之類,或對依賴清單的編輯。安裝清單裡已有的東西不算:pip install -r requirements.txt、pip install -e .、不帶參數的 npm install |
session | 無 | 確定性 | 總是記錄:訊息數、用過的工具、編輯的檔案數、呼叫的技能、程式碼圖是否存在、開始與結束時間、按工作階段彙總的 token 用量,以及對話紀錄檔案大小 |
啟發式訊號永遠不會是一處改動的唯一證據。
各工具支援情況
只有 Claude Code 暴露對話紀錄和生命週期 hook。其他工具能拿到分析器和技能,但沒有擷取。
| 工具 | 對話紀錄偵測器 | hook 觸發的訊號 | 工作階段開始行 | 變更結束提示 |
|---|---|---|---|---|
| Claude Code / Cowork 外掛 | 全部 | scope、generated-files | 有 | 有 |
| Codex CLI | 無 | 無(僅 Bash 稽核日誌) | 無 | 有 |
| OpenCode | 無 | 無(僅 Bash 稽核日誌) | 無 | 有 |
| Cursor、Devin Desktop、Aider、Cline | 無 | 無 | 無 | 有 |
你怎麼知道
你不需要記住任何步驟。每一次 Claude Code 工作階段開始時都會印出一行,位置就在程式碼圖狀態已經出現的地方:
[retro] 12 sessions captured, 3 rules over threshold (graph-first 11x), run /coograph-retro
沒有任何規則超過閾值時,它會這麼說。Retro 沒啟用、但專案已經封存了十個以上 OpenSpec 變更時,它會說 run /coograph-retro to bootstrap。
coograph-archive 和 coograph-apply 也會在一次變更結束時問一次:Run /coograph-retro now? (n sessions since last retro)。預設是否。代理永遠不會未經提示就跑 retro。
第一天,不是第二十天
Claude Code 把每一份歷史對話紀錄保存在 ~/.claude/projects/<slug>/ 下,其中 <slug> 是專案的絕對路徑,把 A-Z a-z 0-9 之外的每個字元換成 -(例如 C--paul-code-app 或 -home-paul-app)。初始化時會問你要不要讀取它們,所以第一份報告裡立刻就有真實的工作階段:
python3 .claude/hooks/capture-signals.py --backfill ~/.claude/projects/<slug> --cwd .
從沒啟用過 Retro、但已經有至少十個封存變更的專案,可以直接執行 /coograph-retro 引導啟動。技能會初始化登錄檔,詢問對話紀錄目錄,回填,然後像正常執行一樣繼續。
報告
python3 .github/retro/retro.py --report
寫出 .coograph/retro/report.md。它以一段白話開頭,然後是表格:每條規則對照閾值並附「升級到」一欄,路徑聚集,建置重試,每次工作階段的 token(以最近一次規則改動為界做前後拆分),工作流程遵循度(同時跑了審查或驗證技能的編輯工作階段),指令檔案相對 token 預算的大小,以及封存統計。確定性的 Python,只用標準函式庫,不涉及模型。
提案
/coograph-retro 讀取報告,寫出 openspec/changes/<date>-retro-<n>/,包含提案、規格、任務和現成的修補。五種改動類型:
- 改寫規則:當證據顯示歧義時改寫既有規則
- 新增規則:沒有任何規則涵蓋的反覆出現的模式
- 新增指令檔案:一份
.github/instructions/<name>.instructions.md,用applyTo限定到違規聚集的目錄 - 新增 hook:仍在被違反的文字規則變成警告 hook;仍在被違反的警告 hook 變成攔截 hook
- 刪除規則:十次工作階段裡沒人碰過的非硬性文字規則
每一處改動都以三句白話開頭:發生了什麼、為什麼重要、改什麼;後面是一個證據區塊,數字只來自報告。你核可後,用 /coograph-apply 像其他變更一樣套用。
迴圈的規則
- 仍在被違反的文字規則會被升級為 hook。永遠不會被改寫得更大聲;資料說那沒用。
- 啟發式訊號永遠不是唯一證據。
- 超出指令 token 預算後,每新增一條規則必須配對刪掉一條。
- Retro 可以對它自己的技能、偵測器和 hook 提出修改,但不能改閾值或停用偵測器,除非提案裡有明確的
Loosen:任務。 - 每個生成的 hook 都帶來源註解。每份 retro 提案都以回滾任務結尾。
Episode
閾值計的是 episode,不是工作階段。一個 episode 就是「某個工作階段在某個自然日」。
單靠工作階段 id 撐不起閾值。習慣開長工作階段的人,好幾天才產生一個 id,於是「三次工作階段」這種門檻無論規則被破壞多少次都搆不著;而頻繁重啟的人,靠雜訊就能越過它。按「工作階段 × 日」計數讓兩種工作方式可比,也代表在一個長工作階段裡、分三天各破壞一次的規則真的會達到閾值——而這正是過去按工作階段計數會掩蓋掉的情形。報告裡兩個數字都給出來,所以三次短工作階段看起來依然和一次長工作階段不一樣。
閾值
全部在 .github/retro/rules.json 裡,可按專案調整。名字裡帶 session 的那幾項,計的是 episode。
| 閾值 | 預設值 | 含義 |
|---|---|---|
deterministic_events / deterministic_sessions | 3 / 2 | 確定性規則在這麼多個 episode 裡出現這麼多次事件即超過閾值 |
heuristic_events / heuristic_sessions | 5 / 3 | 啟發式規則成為僅供佐證的證據 |
prune_sessions | 10 | 非硬性文字規則在這麼多個 episode 裡零事件即為刪除候選 |
defect_lookback_days | 14 | 缺陷偵測器往回找「修復所落在的那次改動」的天數 |
instruction_token_budget | 8000 | 超過後,每新增一條規則必須配對一次刪除 |
retro_prompt_min_sessions | 3 | 上次 retro 之後累積多少個 episode,工作流程才會提議再做一次 |
bootstrap_min_archives | 10 | 從沒啟用過 Retro 的專案引導啟動所需的封存變更數 |
它不是什麼
Retro 不是模型意義上的遞迴自我改進。沒有權重會改變,沒有模型自己訓練自己。它是指令層的版本:系統觀察自身的失敗並對自身的鷹架提出修改,由人守在閘門前。規則能否在多輪之後持續改進還沒有被證明;第一次 retro 通常找到大部分價值。完整的偵測器參考、登錄檔格式和測試在儲存庫的 .github/retro/README.md。