跳到內文
coograph 17

Retro

指令檔案靠堆積生長。每一條規則都是某人看著代理做錯事之後加上去的,而沒人衡量這條規則之後有沒有用。Retro 把這個迴圈閉合。它記錄護欄何時被打破,衡量每次工作階段花了多少 token,然後以 OpenSpec 的形式提出對指令、hook 和技能的具體修改,由你逐行核可或駁回。

什麼都不會離開這台機器。什麼都不會自動套用。

它記錄什麼

一個 Claude Code hook 解析 Claude Code 本來就保存在磁碟上的對話紀錄,並把中繼資料附加到 .coograph/signals.jsonl(被 git 忽略)。它在 SessionEnd 時執行,每次工作階段壓縮時也執行。第二個觸發點才讓長工作階段變得可讀:跑一整天的工作階段只會抵達一次結束,而且很晚,被強制結束就根本沒有;壓縮是它唯一會反覆觸發的事件。重複擷取是安全的,因為一次工作階段的紀錄是整體替換而不是附加。warn-scope.pyblock-generated.py 這兩個 hook 每次觸發時也附加一筆記錄。

每筆違規都帶著它發生的那一刻,取自引發它的那則訊息,而不是擷取執行的時刻。

每筆記錄包含工具名、計數、規則 id、儲存庫相對檔案路徑、shell 命令的雜湊和程式名、時間戳記和 token 用量。它永遠不包含提示文字、助理文字、工具輸出、檔案內容或完整命令。允許清單寫在程式碼裡強制執行,還有一個哨兵測試:在一份對話紀錄的每個部分埋下標記字串,只要標記進了訊號檔案就失敗。

偵測器

偵測器規則信心觸發條件
graph-firstgraph-first確定性在本次工作階段第一次存取程式碼圖之前(mcp__code-graph__* 工具,或同時包含 sqlite3.code-graph 的 shell 命令)出現了 GrepGlob 呼叫,且 .code-graph/graph.db 存在。會記錄之後是否碰到了程式碼圖(mcp-latersqlite-later)還是從未碰到(total-bypass)。子代理的呼叫不計。
openspec-gateopenspec-gate啟發式編輯了兩個以上原始檔,工作階段中沒有任何操作觸碰 openspec/changes,且不存在進行中的變更目錄
scope-warningscope確定性warn-scope.py 印出了它的警告
generated-file-blockgenerated-files確定性block-generated.py 攔截了一次編輯
build-retry確定性同一條 shell 命令跑了三次以上,其中至少兩次失敗
user-correctionuser-correction啟發式一則使用者訊息在工具呼叫之後符合某個糾正模式(只記錄模式 id)。圍繞同一主題反覆糾正,是「某條指令缺失或含糊」最清楚的證據,所以它們歸到自己的規則底下聚類
defectdefect確定性工作階段時間窗內的一次修復提交,落在了過去 14 天內被非修復提交改過的檔案上。每對「修復提交 + 來源提交」出一筆訊號,只帶檔案清單和兩個短雜湊,其他什麼都不帶。專案根是儲存庫就掃根目錄,否則掃它下面一層的各個儲存庫,因此根目錄下放著 app/admin/ 的專案也涵蓋得到
new-dependencyno-new-deps確定性npm install <pkg>pip install <pkg>uv addcargo addgo get 之類,或對依賴清單的編輯。安裝清單裡已有的東西不算:pip install -r requirements.txtpip 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-archivecoograph-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>/,包含提案、規格、任務和現成的修補。五種改動類型:

每一處改動都以三句白話開頭:發生了什麼、為什麼重要、改什麼;後面是一個證據區塊,數字只來自報告。你核可後,用 /coograph-apply 像其他變更一樣套用。

迴圈的規則

Episode

閾值計的是 episode,不是工作階段。一個 episode 就是「某個工作階段在某個自然日」。

單靠工作階段 id 撐不起閾值。習慣開長工作階段的人,好幾天才產生一個 id,於是「三次工作階段」這種門檻無論規則被破壞多少次都搆不著;而頻繁重啟的人,靠雜訊就能越過它。按「工作階段 × 日」計數讓兩種工作方式可比,也代表在一個長工作階段裡、分三天各破壞一次的規則真的會達到閾值——而這正是過去按工作階段計數會掩蓋掉的情形。報告裡兩個數字都給出來,所以三次短工作階段看起來依然和一次長工作階段不一樣。

閾值

全部在 .github/retro/rules.json 裡,可按專案調整。名字裡帶 session 的那幾項,計的是 episode。

閾值預設值含義
deterministic_events / deterministic_sessions3 / 2確定性規則在這麼多個 episode 裡出現這麼多次事件即超過閾值
heuristic_events / heuristic_sessions5 / 3啟發式規則成為僅供佐證的證據
prune_sessions10非硬性文字規則在這麼多個 episode 裡零事件即為刪除候選
defect_lookback_days14缺陷偵測器往回找「修復所落在的那次改動」的天數
instruction_token_budget8000超過後,每新增一條規則必須配對一次刪除
retro_prompt_min_sessions3上次 retro 之後累積多少個 episode,工作流程才會提議再做一次
bootstrap_min_archives10從沒啟用過 Retro 的專案引導啟動所需的封存變更數

它不是什麼

Retro 不是模型意義上的遞迴自我改進。沒有權重會改變,沒有模型自己訓練自己。它是指令層的版本:系統觀察自身的失敗並對自身的鷹架提出修改,由人守在閘門前。規則能否在多輪之後持續改進還沒有被證明;第一次 retro 通常找到大部分價值。完整的偵測器參考、登錄檔格式和測試在儲存庫的 .github/retro/README.md