belay 是做什麼的?
你透過許多小步驟達成目標。一個檢查會為每一步評分,過程中無需人為介入。scripts/belay.py 負責記錄分數:你編輯,它執行檢查,若成功則提交,若失敗則復原,並寫入日誌。你永遠不需要自己記錄分數,也永遠不需要決定某一步是否「基本上」通過了。
每個目標最終都會轉化為同一種東西:一個輸出數字的指令。速度更快是時間或計數。稽核是失敗測試的數量,每個發現對應一個。移植是相符輸出的數量。UI 是評審的分數。
工作方式:
- 使用者啟動一次攀登後便離開。 不要停下來提供更新。他們會閱讀三件事:卡片、只有他們能回答的問題,以及報告。如果工具有提問工具,請使用它來提問。
- 狀態存在於磁碟上,而非對話中。 卡片、日誌、你的筆記以及最後一次的檢查輸出,都存放在
.belay/<name>/下。在壓縮或崩潰後,只需一次status呼叫就能恢復所有資訊。 - 絕對不要使用 sleep 迴圈等待。 在背景執行長時間的指令,並在它們執行期間規劃下一步。
- 下方的
BELAY是此檔案旁scripts/belay.py的絕對路徑。 將完整路徑寫入每個指令。從目標儲存庫內部執行它。你傳遞的路徑和指令是相對於儲存庫根目錄的。 - 攀登會擁有自己的分支
climb/<name>。 復原的步驟會重置樹狀狀態,並刪除 git 忽略規則未涵蓋的未追蹤檔案。如果其他人在此工作目錄中工作,請在單獨的工作樹(worktree)中進行攀登。如果你已經在為此工作建立的分支上,請傳遞--here。
1. 開始前先恢復
執行 python3 "$BELAY" list。如果存在針對此目標的攀登,切換到它的分支,執行 status <name>,然後繼續執行步驟 4。在崩潰或新會話後「繼續」就是指這個意思。不要重新開始。
2. 建立檢查
攀登的品質取決於其檢查。一個衡量錯誤事物的檢查,會讓整個運行都在改善錯誤的事物。先讓一個粗略的檢查能運作,之後再用 relock 來改進它。
捫心自問:我會執行什麼指令來確認目標已達成?讓那個指令輸出一個數字。使用最合適、最嚴謹的形式:精確比對或測試計數,然後是工作量計數(查詢次數、往返次數、位元組數),然後是時鐘計時,最後,只有在沒有其他方式能判斷品味時,才使用模型評審。
| 目標 | 分數 |
|---|---|
| 錯誤或功能 | 根據規格(而非你的修正)所撰寫的失敗測試 |
| 稽核、穩定化、「讓 X 能運作」 | 失敗測試,每個發現對應一個 |
| 速度或大小 | 時間背後的工作量計數,或時間本身 |
| 移植或複製 | 與真實工具相符的輸出 |
| 同時達成多個目標 | 朝最差標準的進度 |
| 外觀、感受、文案 | 帶有評分標準和良好範例的 judge.py |
每種目標的具體做法都放在 references/checks.md 中。在撰寫檢查之前,請閱讀符合你目標的部分。
使用儲存庫現有的資源。--score '<regex>' 從任何測試執行器的輸出中讀取分數,例如 --score '(\d+) failed'。它會取最後一行符合的內容,並累加其捕獲的數字。一個通過且無符合項目的執行,其分數為 0。如果沒有 --score,檢查標準輸出的最後一行必須是純粹的數字。重複使用 --check 可同時執行多個指令;它們的分數會相加。對攀登會編輯的每個其他工作目錄,使用 --repo <path>。這樣,步驟會在所有這些儲存庫中同時被保留或復原。
在信任檢查之前,先證明它:
- 它能失敗。 故意破壞程式碼,觀察分數變差。使用
git checkout -- <file>還原。 - 它能看見成功。 一個微小的真實改進必須能改變分數。
- 它很穩定。 執行它 3 次。如果分數在沒有程式碼變更的情況下飄移,請用計數取代時鐘計時,或使用
--runs 3、高於飄移量的--min-gain(當分數會大幅縮小時,使用百分比,如3%),以及--paired。 - 它無法被欺騙。 每次執行時改變輸入,並與參考資料比較。手動嘗試一種作弊方式,例如寫死答案,並確認檢查或某個守護檢查能抓到它。
- 它很快。 每次步驟都需要花費檢查的時間。設法讓一個步驟在 2 分鐘內完成:對樣本或每次步驟的計數進行評分,並將完整的測試套件或計時運行保留作為守護檢查或保留資料集。
- 標準是可達到的。 找出當前設計能做到的最佳結果。如果標準超出此範圍,請在卡片上說明,並提出一個你可達到的標準。
保護所有分數未衡量的項目:
- 守護檢查(
--guard,透過結束代碼 0 通過)。列出你將要修改的程式碼所影響的範圍:呼叫者、其他平台、儲存的資料、公開 API。每個都需要一個守護檢查:今日能通過的測試、型別檢查、lint、建置。當分數是失敗計數時,將那些通過的測試放在它們自己的檔案中作為守護檢查,這樣崩潰就不會偽裝成「更少的失敗」。 - 鎖定(
--lock)檢查、測試、固定裝置和保留資料集。當鎖定檔案發生差異時,try會拒絕評分。在--lock路徑下的新檔案會成為攀登分支上的第一個提交。任何其他未提交的變更都會阻止init。 - 保留資料集(
--holdout),當檢查使用樣本、基準測試或評審時:攀登從未訓練過的案例,保存在.belay/<name>/holdout/下。不要再打開它們。如果分數上升但保留資料集沒有,表示攀登過度擬合。停止並說明原因。
稽核和開放式目標。 將目標涵蓋的範圍作為檢查清單,列在 .belay/<name>/notes.md 中。逐區尋找錯誤。根據規格或錯誤報告,為每一個真正的發現撰寫一個失敗測試。當後續步驟發現一個新的錯誤時,加入其測試並執行 relock --why "found <bug>"。分數會因正當理由而上升,且日誌會顯示原因。需要使用者決定的發現應寫入筆記和報告,而不是分數中。
3. 卡片,然後 init
僅傳送一條包含卡片的訊息來決定,不要有其他內容:
攀登卡片:
build-speed目標: 冷建置快 2 倍,輸出相同
分數 中位數建置秒數,越低越好:現在 47.0,標準 23.5 檢查 ./scripts/bench-build.sh,3 次運行,配對比較,最小增益 3%守護檢查 npm test、npm run typecheck保留資料集 12 個範例應用中的 3 個 鎖定 scripts/bench-build.sh、test/、samples/限制 無(每次步驟約 2 分鐘) 需要你的回答: 無
回覆 go,或更改其中一行。
填寫每一行,使用你最佳的選擇。如果使用者給了一個沒有標準的目標,提出一個有野心且可檢查的標準,例如「快 2 倍」或「每個範例都相符」。等待「go」,這表示批准了卡片、分支及其提交。當使用者已經要求直接執行而不詢問,或者目標很小且標準無庸置疑時,則跳過等待,並將卡片放在報告的頂部。在那之後,除了你能自行查詢或決定的事情之外,不要詢問任何事。
python3 "$BELAY" init build-speed --goal "cold build 2x faster" \
--check ./scripts/bench-build.sh --direction min --target 2x \
--runs 3 --paired --min-gain 3% --guard "npm test" --guard "npm run typecheck" \
--holdout ./scripts/bench-holdout.sh --lock scripts --lock test \
--lock .belay/build-speed/holdout
一個涵蓋後端和前端稽核的範例:
python3 "$BELAY" init billing-audit --goal "every billing finding fixed" \
--check "pytest tests/billing -q" --check "cd ../web && npx vitest run billing" \
--score '(\d+) failed' --direction min --target 0 --repo ../web \
--guard "pytest tests/legacy -q" --guard "cd ../web && npx tsc --noEmit" \
--lock tests/billing --lock ../web/src/billing/__tests__
init 會建立分支、測量基準值,並在任何守護檢查已失敗時拒絕開始。在第一步之前,修正它所警告的內容。僅在使用者指定限制時,才設定 --max-hours 或 --max-steps。若無限制,攀登會持續進行,直到滿足標準或卡關為止。
4. 攀登
每一步就是一個想法、一個變更、一次 try:
- 選擇想法。除非你能說明這次有何不同,否則不要重試日誌中已復原的想法。如果使用了評審,請閱讀
.belay/judge-notes.md。 - 為一個原因做一個變更,並猜測其影響。將猜測寫入想法中:例如
"cache the parser (expect -10%)"。當猜測與實際結果相差甚遠時,表示你對程式碼的理解有誤。在下一步之前進行效能分析。 python3 "$BELAY" try "<idea>"。它會提交成功或重設樹狀狀態。無論如何,繼續前進。該步驟的完整檢查輸出在.belay/<name>/last.txt,最佳步驟的輸出在best.txt。請閱讀這些檔案,而不要再次執行檢查。- 當檢查執行時,不要編輯樹狀狀態。同時只有一個會改變攀登狀態的
belay.py指令在執行。如果它顯示另一個指令仍在執行,請等待它。 - 每 5 個步驟,用幾行文字更新
.belay/<name>/notes.md:說明哪些有效、哪些無效、下一步該嘗試什麼。下一次會話將從此處繼續。
如果你相信一個被檢查復原兩次的想法,表示這個檢查無法看到它的價值。讓檢查更精細並執行 relock。永遠不要為了讓步驟通過而編輯鎖定的檔案。
永遠不要欺騙檢查
以下每一項都會提高分數,但會使工作成果更糟。即使 try 保留了它們,每一項都是一次失敗的攀登:
- 針對檢查使用的輸入編寫特殊案例,或寫死/快取預期輸出。
- 跳過、削弱或刪除測試,或捕捉並隱藏錯誤。
- 讓基準測試做更少的工作,而不是讓程式碼更快。
- 使用
relock來移除或放寬困難的案例,以便達到標準。Relock 只應讓檢查更正確。如果標準無法達成,請報告你所測量到的極限。 - 讀取保留資料集。
一個真正的成功,是讓同樣的程式碼在檢查從未見過的輸入上表現得更好。如果前進的唯一方法是上述之一,那麼檢查有問題。請說明情況。
持續重新評估
分數只是使用者真實需求的代理。請持續檢查它是否仍然代表這個需求:
- 每 10 個保留的步驟,以及達到標準時: 親自檢查真實的結果。打開應用程式、讀取輸出、檢視螢幕截圖。執行完整的測試套件和
holdout。如果分數和真實結果之間出現了偏差,修正檢查並執行relock --why "..."。 - 某個東西壞了,但沒有任何守護檢查抓到: 使用
git bisect run <failing command>找出是哪個步驟造成的,然後用try --fix "<what broke>"來修復它,這會保留一個能維持住分數的變更。如果唯一的修復方式必須放棄該步驟的成果,則使用git revert復原該步驟,並執行relock --why "reverted step N: broke X"。加入缺失的守護檢查。 - 在提出報告之前: 將
git diff <start>..HEAD視為一個整體變更來審查,用全新的眼光。尋找被移除的測試、被吞沒的錯誤、被關閉的功能、被改變的預設值,以及資料或 API 的變更。使用try --fix修復,然後再執行一次檢查和holdout --why final,以便報告引用測量到的數字。
5. 當它卡關時
在連續 5 個步驟沒有進展後,try 和 status 會顯示 PLATEAU。停止嘗試想法,找出阻礙。在猜測之前先進行測量:
- 時間或錯誤計數實際上從哪裡來?進行效能分析。
- 檢查是否不穩定、太粗糙以至於無法看到小的成功,或者對問題視而不見?
- 是否存在一個更大的想法,是小的步驟無法達成的?
- 什麼工具、存取權限、資料或運算資源可以改變接下來的十個步驟?
使用 python3 "$BELAY" review "<what you found>" 記錄下來,這會讓 PLATEAU 狀態靜默 5 個步驟。修復你能修復的部分:更快的檢查、更細緻的指標、不同的方法。只有在所有其他想法都嘗試過之後,才向使用者請求只有他們能提供的東西,例如存取權限或資金。
6. 停止並報告
當以下任一情況發生時停止:
try顯示 BAR REACHED。它會自動為你執行保留資料集。一個可見的成功但保留資料集沒有變化的情況,並非真正的成功。status顯示 STUCK:兩次阻礙審查之間沒有任何進展。try拒絕並顯示「Budget used」。- 保留資料集與分數不一致。
- 下一步需要只有使用者能提供的東西。
然後執行上述的最終審查,刪除 .belay/<name>/data/,並傳送報告。讓報告能在十秒內讀完:
攀登
build-speed:已達標準47.1 秒 → 18.7 秒,快 2.5 倍(標準 23.5 秒,在第 41 步達成)
保留資料集 51.0 秒 → 20.2 秒,與分數同步變化 步驟 52 步中保留了 19 步 安全性 每個保留的步驟都通過了守護檢查。完整套件和最終審查乾淨 分支 climb/build-speed,19 個提交有效的嘗試
- 增量型別快取:−12 秒
嘗試過但已復原
- swc 交換:破壞了 3 個守護檢查
你的下一步
- 審查:
git log --oneline 1a2b3c4..climb/build-speed
標題是以下之一:已達標準、卡關、預算用盡、保留資料集不一致、需要你。引用腳本測量到的數字,絕不使用估計值。如果你在中途更改了檢查或標準,請在標題中說明。最大的成果優先。每個區塊最多 3 個項目符號。跳過空白區塊。
如果使用者之後想要更多,使用 raise 2x --why "..." 為同一個聲明設定更嚴格的標準,並繼續攀登。永遠不要切換到一個新的目標。
子代理
如果工具能啟動子代理,可以在設定階段用它們進行並行閱讀,並在阻礙審查和最終審查時提供新的觀點。要求結論,而不是檔案傾印。只有你可以在攀登樹狀狀態中寫入,因為 try 會重設它。一個輔助代理負責閱讀,或在自己的工作樹中起草一個修補程式,再由你套用並執行 try。如果沒有子代理,則內聯處理。
指令
| 指令 | 功能 |
|---|---|
init <name> --goal "..." --check "..." --direction min|max | 基準值、分支、鎖定。選項:--check(可重複)、--score '<regex>'、--target(數字或 2x)、--guard(可重複)、--lock(可重複)、--holdout、--repo(可重複)、--runs、--paired、--min-gain、--timeout、--max-hours、--max-steps、--here |
try "<idea>" [--fix] | 為編輯評分,然後保留或復原它。--fix 會保留一個能維持分數的修復 |
status [name] | 分數、最近步驟、筆記、PLATEAU 或 STUCK |
relock --why "..." | 接受已更改的檢查並重新測量 |
holdout --why "..." | 立即執行保留資料集 |
review "<found>" | 記錄一次阻礙審查 |
raise <bar> --why "..." | 在達成當前標準後設定一個更嚴格的標準 |
budget [--hours N] [--steps N] | 為攀登設定或檢視預算。若無引數則顯示當前預算 |
list | 列出此工作目錄中的所有攀登 |
log [name] | 以可讀格式顯示攀登日誌 |
export [name] | 匯出攀登結果為 JSON |
環境
BELAY_PATH:scripts/belay.py的路徑。若未設定,則使用scripts/belay.py相對於此 SKILL.md 檔案的路徑。BELAY_BACKEND:要使用的 LLM 後端(例如claude、openai、gemini)。預設為系統預設值。BELAY_DEBUG:設定為1以啟用除錯輸出。HOME、USER:用於識別使用者和個人配置目錄。SHELL:用於執行指令的 shell。GIT_*:所有標準的 git 環境變數都受尊重。PATH:應包含所有必要的工具(git、python3、node、npm、pytest、npx等)。