評審規則

告訴 OCR 每個檔案要「關注什麼」——四層優先級鏈與五重門過濾

四層優先級鏈

對每個檔案路徑,依序嘗試各層;第一個匹配的模式生效。

優先級來源路徑說明
1(最高)--rule 參數使用者指定CLI 覆蓋;只要提供就生效
2專案配置<repoDir>/.opencodereview/rule.json專案級——可安全提交
3全域配置~/.opencodereview/rule.json使用者級偏好
4(最低)系統內建內嵌 system_rules.json永遠存在,涵蓋常見語言

規則檔格式

{
  "include": ["src/**/*.{ts,tsx}", "src/**/*.go"],
  "exclude": ["**/*.test.ts", "**/generated/**"],
  "rules": [
    { "path": "src/api/**/*.go", "rule": "All exported handlers must validate request bodies before use." },
    { "path": "**/*mapper*.xml", "rule": "Check SQL for injection risks, parameter errors, and missing closing tags." }
  ]
}

glob 能力(bmatcuk/doublestar/v4)

語法含義
*匹配除 / 外任意字元
**跨目錄邊界(src/**/*.go 覆蓋任意深度)
{a,b,c}花括號展開(*.{ts,tsx})
? / [abc]單字元 / 字元類

模式匹配不區分大小寫(路徑先小寫化)。不確定時用 ocr rules check <path> 確認。

五重門檔案過濾

對每個 diff,OCR 依序問:

  1. binary——二進位?排除。
  2. user_exclude——命中你的 exclude?排除。
  3. user_include——有 include 且命中?立即保留(繞過下面兩門)。
  4. unsupported_ext——擴展名在白名單?不在則排除。
  5. default_path——命中內建測試檔排除模式(**/*_test.go…)?排除。

全部通過才發給 LLM。用 ocr review --preview 可不花 token 印出過濾結果。

內建排除(節選)

**/*_test.go            **/*.test.{js,jsx,ts,tsx}
**/src/test/**/*.java   **/__tests__/**
**/*_test.py            **/*_spec.rb
**/*Test.java           **/*_test.rs

每檔規則解析

過濾決定某檔會審後,OCR 依序試 --rule → 專案 rule.json → 全域 rule.json → 系統內建。解析出的規則正文成為 plan 與 main task prompt 的 {{system_rule}}。

查看哪條規則生效:ocr rules check

$ ocr rules check src/main/java/com/example/UserService.java
File: src/main/java/com/example/UserService.java
Source: System built-in
Pattern: **/*.java
Rule:
────────────────────────
…java.md 內容…
────────────────────────

配方

專案級:強制編碼規範

{
  "rules": [
    { "path": "src/api/**/*.go", "rule": "Every public handler must `defer tx.Rollback()` immediately after starting a transaction." },
    { "path": "**/*mapper*.xml", "rule": "Check SQL for injection risks, missing parameter binding, and unclosed XML tags." }
  ]
}

跳過生成碼,聚焦 src

{ "include": ["src/**/*.{ts,tsx,js,jsx}"], "exclude": ["**/*.gen.ts", "**/generated/**"] }

按 PR 覆蓋

ocr review --rule ./.review-rules-only-for-this-pr.json
📖 教學解說:評審規則深入

四層優先級鏈的「為什麼」

四層不是任意順序——它們有明確的覆蓋邏輯:

  • --rule(CLI)→ 臨時覆蓋,不改配置
  • 專案 .opencodereview/rule.json → 團隊共識,可提交
  • 全域 ~/.opencodereview/rule.json → 個人偏好
  • 系統內建 → 永遠兜底

「首條匹配生效」意味著更具體的規則要放前面。

include 是「繞過機制」不是白名單

這是最多人誤解的:include 不是「只審這些檔」——它是「這些檔繞過後面兩門排除」。五重門的第三門 user_include 命中後立即保留,跳過 unsupported_ext 和 default_path。這就是為什麼你可以用 include 強制保留測試檔。

用 ocr rules check 除錯

「規則沒觸發?」→ 跑 ocr rules check <path>。它顯示:① 匹配的層(CLI/專案/全域/內建)→ ② glob 模式 → ③ 規則正文。如果層不對(應該匹配專案但匹配了內建),多半是宣告順序問題——把更具體的規則前移。

🔍 Worked Example:自訂規則 + 除錯

Step 1 — 建立專案級規則檔

cat > .opencodereview/rule.json << 'EOF'
{
  "include": ["src/**/*.go"],
  "exclude": ["**/*_test.go"],
  "rules": [
    { "path": "src/api/**/*.go", "rule": "Check for missing error handling after db.Begin()." }
  ]
}
EOF

Step 2 — 確認規則是否匹配

ocr rules check src/api/handler.go

Step 3 — 跑 preview 看過濾結果

ocr review --preview

Step 4 — 真正跑評審

ocr review

預期產出

File: src/api/handler.go
Source: Project .opencodereview/rule.json
Pattern: src/api/**/*.go
Rule:
────────────────────────
Check for missing error handling after db.Begin().
────────────────────────

常見錯誤與診斷

錯誤訊息診斷修復
規則沒觸發glob 模式不匹配或宣告順序不對跑 ocr rules check 確認
我的檔案沒被評審被五重門過濾跑 ocr review --preview 看排除原因

練習 / 驗收清單

  • 能說出四層優先級鏈的順序
  • 能解釋 include 是繞過機制而非白名單
  • 能描述五重門各擋什麼
  • 能用 ocr rules check 除錯規則沒觸發
看完這頁你應該能說出:四層優先級鏈的順序、include 是繞過機制而非白名單、五重門各擋什麼、以及如何用 ocr rules check 除錯「規則沒觸發」。

延伸閱讀:CLI 參考 · 架構 · FAQ

① 進階真實情境 Worked Example:多語言專案的規則分層與「特定優先」

情境

你的 monorepo 同時有 Go(API)、TypeScript(web)、SQL mapper XML(DAO)。你要一套分層規則:全站共用一條「禁止 secrets 硬編碼」,各語言再各自有專門規則,且「特定語言規則」必須壓過「共用規則」。

Step 1 — 建立分層規則檔

# .opencodereview/rule.json
{
  "include": ["src/**/*.{go,ts,tsx}", "src/**/*.xml"],
  "exclude": ["**/*.test.*", "**/generated/**"],
  "rules": [
    { "path": "**/*", "rule": "Check for hardcoded secrets, tokens, or credentials. Flag any literal that looks like an API key." },
    { "path": "src/api/**/*.go", "rule": "Go-specific: every db.Begin() must defer tx.Rollback(); check goroutine leaks and missing error wrapping." },
    { "path": "src/web/**/*.tsx", "rule": "React-specific: check for missing keys in lists, stale closures, and un-memoized expensive computations." },
    { "path": "**/*mapper*.xml", "rule": "Check SQL for injection risks, parameter binding errors, and unclosed tags." }
  ]
}

Step 2 — 驗證「特定優先」真的生效

ocr rules check src/api/handler.go
ocr rules check src/web/App.tsx
ocr rules check src/dao/user-mapper.xml

Step 3 — 確認 **/* 這條「共用規則」不會吃掉特定規則

關鍵在宣告順序:**/*(共用)必須放在最後。因為「首條匹配生效」——如果 **/* 在前,它會先匹配所有檔,後面的 Go/React/XML 規則永遠輪不到。把共用規則放最後,讓特定規則先匹配。

Step 4 — 跑評審驗證

ocr review --format json --audience agent | jq '.comments[].path'

為什麼選這條審查路徑

多語言 monorepo 的規則設計核心是「先特後通」——特定規則(Go/React/XML)優先於共用規則(secrets)。用 ocr rules check 逐檔驗證是唯一能「證明匹配層正確」的手段;沒有它,你會以為規則生效了,其實被 **/* 吃掉。宣告順序是這裡唯一的坑,而 rules check 讓它可測。

預期產出

handler.go    → Source: Project rule.json / Pattern: src/api/**/*.go   ✓ Go 特定
App.tsx       → Source: Project rule.json / Pattern: src/web/**/*.tsx  ✓ React 特定
user-mapper.xml → Pattern: **/*mapper*.xml                            ✓ XML 特定
(共用 secrets 規則作為 fallback,三者都看不到時才輪到)

② 深入原理擴充:glob 匹配的「順序敏感」與「大小寫」陷阱

首條匹配生效 = 規則檔是「if-else 鏈」

規則檔的 rules 陣列不是「全部套用」,而是依序求值、首個匹配生效。這讓它可以表達「fallback 模式」:最具體的放前面,最籠統的(**/*)放最後當兜底。這也意味著宣告順序本身就是程式邏輯——改順序就改行為。

大小寫陷阱

glob 匹配不區分大小寫(路徑先小寫化)。src/API/handler.go 與 src/api/handler.go 會匹配同一個 pattern——這在多數情況是好事,但如果你依賴大小寫區分「不同環境的目錄」(Dev/ vs dev/),規則會誤觸。用 ocr rules check 確認實際匹配。

「看起來規則生效但其實沒觸發」的案例

# 你把規則寫成:
{ "path": "src/**/*.go", "rule": "..." }
# 但實際檔案在:  src/gen/api.go   ← 被 include 繞過? 不,被 default_path 或 exclude 擋了
# 你跑 ocr rules check 說「匹配了」——那只是說「規則文本會套用」
# 但 ocr review --preview 才顯示:  src/gen/api.go  (excluded: default_path)

ocr rules check 只回答「規則層」(哪條規則會套用),ocr review --preview 才回答「這個檔會不會被審」。兩者是獨立的——規則匹配了,但檔案可能先被五重門過濾掉。防禦:懷疑「規則沒生效」時,兩個命令都跑:rules check 確認規則、--preview 確認檔案存活。

③ 診斷式疑難排解

症狀可能原因解決方案
特定規則永遠不觸發共用規則(**/*)宣告在特定規則前面把共用規則移到陣列最後(首條匹配生效)
rules check 匹配了但檔沒被審檔案先被五重門過濾(exclude / default_path)跑 ocr review --preview 看排除原因
規則意外匹配到別語言glob 太寬或大小寫不區分誤觸用更精確的 pattern;rules check 逐檔確認
include 看起來「沒強制保留」include 繞過後面的門,但繞不過 exclude(優先級更高)確認檔沒在 exclude;include 只在「排除後」處理
改規則後行為沒變OCR 讀的是 ~/.opencodereview/rule.json 全域檔,不是你的 repo 檔確認規則在 <repoDir>/.opencodereview/rule.json;或用 --rule 指定

④ 進階挑戰題

  1. 挑戰一:把「共用 secrets 規則」放第一個會發生什麼?用「首條匹配生效」的邏輯推演各檔會被套用哪條規則。
  2. 挑戰二:寫一個規則檔,讓「internal/** 的 Go 檔」與「cmd/** 的 Go 檔」分別套用不同規則(提示:兩條 pattern 都含 *.go,靠順序與路徑區分)。
  3. 挑戰三:為什麼「include 繞過 default_path」卻「繞不過 exclude」?這兩個「繞過」在五重門裡的順序差別是什麼?