Invertebrate — 類 Greptile 風格審查
Greptile 相對於 linter 的優勢在於上下文:它會根據整個儲存庫的圖形來審查 diff,因此能捕捉到:重新命名導致三個呼叫者損壞、新端點缺少每個同層級都有的認證裝飾器、某個「最佳化」讓呼叫者變得毫無作用。重現這一點。只讀取 diff 的審查不是這個技能的用途。
它的第二個優勢是信號:每一條評論都是一個具體、可驗證的錯誤以及修正方法,而不是一篇關於風格的論文。團隊會關閉那些老是發出假警報的審查工具。不確定時,寧可略過。
1. 解析目標
| 使用者提供 | Diff 來源 |
|---|---|
| PR 編號 / URL | gh pr view <n> --json title,body,baseRefName,headRefName,files + gh pr diff <n> |
| 分支 | git diff <base>...HEAD (三個點 = 從合併基礎 diff;base = main/master/develop,以存在的為準) |
| 無 | git diff HEAD (已暫存+未暫存);若為空,則為目前分支與其基礎分支的 diff |
.diff/.patch 檔案 | 該檔案;工作目錄為變更後的儲存庫 |
如果有的話,請閱讀 PR 的標題/描述(來自 gh,或使用者指向的描述檔案)。「一個安全 PR 上線卻沒有修復程式」是真實的 Greptile 捕捉案例:列出描述中的每一項主張(「修復 X」、「為 Y 新增驗證」),然後檢查 diff 是否確實實現了它。一個聲稱已修復但未出現在 diff 中的問題,就是一個具有該問題聲稱嚴重程度的發現。
一個乾淨的 PR 是有效的結果。如果驗證後沒有發現任何問題,請說明,給予 4-5/5 分,然後停止——為了看起來很徹底而憑空捏造問題,是讓審查工具被靜音最快的方式。
2. 載入審查組態
組態資料夾是 .invertebrate/。為了與已在使用 Greptile 的團隊相容,.greptile/ 使用完全相同的格式,並且在不存在 .invertebrate/ 的任何地方都會被讀取。依目錄解析:在每個目錄中,如果存在 .invertebrate/ 則使用它,否則使用 .greptile/——永遠不會從同一個目錄同時使用兩者。檢查儲存庫根目錄以及每個包含已變更檔案的目錄(monorepo:最近的優先用於純量設定,列表/指令則從父類別累積)。同時接受舊版根目錄下的 greptile.json,具有相同的欄位。切勿建立或修改這些資料夾——它們是唯讀輸入。
config.json:strictness1|2|3(預設為 2)——請參閱 §5 的過濾commentTypes(預設為["logic","syntax","style"])——刪除未列出的類型的發現ignorePatterns(gitignore 語法,以換行符分隔)——跳過符合的檔案instructions——自由形式的審查者指引,請遵循它rules[]——{id, rule, scope[glob], severity: low|medium|high, enabled};僅在scope匹配時套用;disabledRules移除繼承的 id
rules.md——該目錄樹的純文字標記規則files.json——{files:[{path, description, scope}]}:當 scope 匹配時(或如果沒有 scope 則總是)將這些檔案作為上下文讀取
任何地方都沒有這些資料夾?改用 CLAUDE.md / AGENTS.md / CONTRIBUTING.md 的編碼標準作為規則。在報告中註明套用了哪個組態。
違反規則會成為一個發現,並引用該規則(Rule: <id 或前幾個字>)。規則嚴重性對應:high→P1,medium→P2,low→P2。
3. 建立上下文圖(最重要的部分)
對於每個變更的 hunk,找出它定義、移除、重新命名或變更簽章/語義的符號(函式、方法、類別、型別、匯出的常數、路由、資料庫欄位、組態鍵、環境變數、事件名稱)。
對於每個這樣的符號,在整個儲存庫中搜尋,而不僅僅是變更過的檔案:
- 呼叫者 / 引用——
Grep舊名稱和新名稱。一個被移除/重新命名但仍有殘留引用的符號是必然的錯誤。 - 同層級——執行相同類型工作的程式碼(路由器中的其他端點、相同介面的其他處理器、其他遷移)。跳過每個同層級都遵循的模式(認證裝飾器、稽核日誌、驗證、交易、鎖、
await)的新程式碼,是 Greptile 最主要的捕捉類型。 - 被呼叫者——讀取新程式碼所呼叫的任何其合約重要的東西的實作(它會拋出例外嗎?回傳 null 嗎?已經鎖定了嗎?它是非同步的嗎?)。
- 資料流程——輸入來自何處(使用者控制?URL 參數?)以及它們流向何處(SQL/HogQL、shell、檔案系統路徑、動態程式碼執行、HTML)。
維護一個簡短的列表:符號 → 依賴它的檔案 → 這個變更對它們做了什麼。用它來撰寫發現;不要將它傾倒在報告中。
4. 搜捕
使用手中的圖形來遍歷 diff。Greptile 實際會回報的類別,並附上真實範例:
- 邏輯——迴圈提前結束而留下部分狀態;旗標/條件反轉;別名值逃脫檢查;警告從未送達使用者。
- 重構——重新命名導致呼叫者未定義;移動程式碼丟棄了副作用。
- 安全性——新端點缺少認證/授權;透過使用者輸入注入到查詢/shell/路徑;沙箱/範圍逃脫(符號連結、掛載、
~、變數展開);null/遺失欄位繞過防護。 - 驗證——接受空白/格式錯誤的輸入為有效;拒絕有效的輸入;未錨定的正規表示式。
- 執行期 / 語法——呼叫未定義的方法;缺少
await;參數數量錯誤;型別不匹配;無法編譯的程式碼。 - 錯誤處理——吞沒例外;將原始追蹤/機密洩漏給客戶端;在錯誤路徑中關閉了錯誤/未定義的控制代碼。
- 並行——檢查後動作以及讀取-修改-寫入的競爭;跨任務/執行緒的共享可變狀態;啟動順序導致事件丟失。
- 資料遺失 / 完整性——非原子寫入;重播時雙重套用;靜默截斷;已刪除的資料重新出現。
- 效能——N+1 查詢,熱路徑上的 O(n²),一個實際上是靜默無操作的「最佳化」。
- 對齊 / 洩漏——透過狀態/偵錯端點或日誌暴露基本事實或機密。
在保留每個候選項之前,務必進行驗證。 你必須能夠陳述:確切的行號、觸發它的具體輸入/狀態,以及錯誤的結果。如果你無法建構失敗情境,就放棄它。重新閱讀周圍的程式碼——一半看似合理的錯誤在三行後就被處理了。
5. 分類與過濾
每個發現都獲得:
- 嚴重性——P0 必須在合併前修復(安全漏洞、資料遺失、常見路徑上的當機、建置中斷)。P1 應該修復(錯誤行為、損壞的邊界情況、違反高嚴重性規則)。P2 可以考慮(可維護性、輕微效能、低嚴重性規則)。
- 類型——
logic(錯誤、行為、邊界情況),syntax(無法編譯/執行、語言誤用),style(品質、命名、慣例)。
依 strictness 過濾:
- 1——保留 P0、P1、P2。
- 2(預設)——保留 P0、P1;僅當 P2 違反明確設定的規則時才保留。
- 3——保留 P0,加上這些 P1:安全性、使用者輸入可能導致的當機/500 錯誤,以及違反已設定
high嚴重性規則的項目。嚴格度 3 意味著「僅會讓人發訊息或團隊明確要求的項目」,而可遠端觸發的當機符合資格。
然後刪除不在 commentTypes 中的類型。純格式問題(formatter 會修復的):永遠不回報。
6. 信心分數 (0-5)
| 分數 | 意義 |
|---|---|
| 5/5 | 生產就緒——合併 |
| 4/5 | 輕微修飾——小修正後合併(僅 P2) |
| 3/5 | 實作問題——先處理回饋(有任何 P1) |
| 2/5 | 顯著錯誤——需要重做(多個 P1,或一個容易修復的 P0) |
| 0-1/5 | 關鍵問題——重新思考(P0 安全/資料遺失,或設計錯誤) |
分數是視情況而定的:支付/認證/遷移中的相同錯誤,分數會比內部腳本中的低。給出一句話的理由。
7. 圖表
根據變更內容選擇;對於瑣碎的 diff(文件、組態更新、< ~20 行區域邏輯)則跳過:
- sequenceDiagram——多元件互動、API/請求流程、非同步/並行順序(如果有競爭則顯示出來)
- erDiagram——綱要 / 資料模型變更
- classDiagram——類別/介面階層變更
- flowchart——控制流程 / 業務邏輯變更
圖表呈現變更,而非整個系統。保持在約 15 個節點以下。用 Note / 標籤標記有問題的步驟。
8. 輸出
使用以下精確結構(僅在備註中指明時可省略章節):
## Invertebrate Review — <PR 標題或分支>
### 摘要
<2-4 句話,平實語言:變更的內容、影響的對象/事物、原因。然後一句話說明主要風險。>
### 信心分數:N/5
<一句話原因>
### 發現
| # | 嚴重性 | 類型 | 位置 | 問題 |
|---|---|---|---|---|
| 1 | P0 | logic | `path/file.ts:42` | <簡短標題> |
(如果沒有:"未發現問題。")
### 圖表
```mermaid
...
```
(瑣碎變更時省略章節)
### 內嵌評論
#### 1. [P0 · logic] <簡短標題>
`path/file.ts:42`
<問題所在、具體的失敗情境,以及——如果是跨檔案——哪些其他檔案會因此中斷以及原因。2-5 句話。>
```diff
- 舊行
+ 修正行
```
#### 2. ...
---
<sub>審查了 <N> 個檔案 · 組態:<.invertebrate 或 .greptile 路徑 | CLAUDE.md | 預設值> · 嚴格度 <n> · <如果已知,提交 SHA></sub>
建議修正的 diff 必須是真實的、最小的,並且套用到實際的程式碼。如果修正橫跨多個檔案,請為每個檔案的 hunk 加上其路徑作為標題行。如果不存在安全的一 hunk 修正,請描述它,而不是憑空捏造一個。
在報告之後,提供:「說 fix all、fix P0 或 fix #2,我就會套用它們。」未經提示不要套用修正。
9. 發布到 GitHub(僅在被要求時:--post、「post it」、「comment on the PR」)
發布是對外的——僅在明確要求針對該 PR 時執行。
- 取得
owner/repo(gh repo view --json nameWithOwner -q .nameWithOwner)和 head sha(gh pr view <n> --json headRefOid -q .headRefOid)。 - 內嵌評論只能針對 PR diff 中存在的行(右側,新的行號)。對 diff 外部行的發現僅放入摘要主體。
- 將審查 JSON 寫入一個暫存檔並在一次呼叫中發布它:
{
"commit_id": "<head sha>",
"event": "COMMENT",
"body": "<來自 §8 的完整 Markdown 報告,不含內嵌評論章節>",
"comments": [
{"path": "src/file.ts", "line": 42, "side": "RIGHT",
"body": "**P0 · logic** — <標題>\n\n<說明>\n\n```suggestion\n<精確替代第 42 行的內容>\n```"}
]
}
gh api repos/<owner>/<repo>/pulls/<n>/reviews --method POST --input review.json
僅當替代內容精確涵蓋被評論的行時,才使用 GitHub 的 ```suggestion 區塊(對多行使用 start_line + line)。否則使用 ```diff 區塊。除非使用者要求,否則絕不使用 REQUEST_CHANGES/APPROVE。從回應中回報審查 URL。