對每個檔案路徑,依序嘗試各層;第一個匹配的模式生效。
| 優先級 | 來源 | 路徑 | 說明 |
|---|---|---|---|
| 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." }
]
}
include——glob 模式,繞過內建預設排除(測試檔排除)。它不是白名單。exclude——OCR 不審的檔。過濾中優先級最高。rules——{path, rule} 條目,按宣告順序求值。第一個匹配決定發給模型的 prompt。| 語法 | 含義 |
|---|---|
* | 匹配除 / 外任意字元 |
** | 跨目錄邊界(src/**/*.go 覆蓋任意深度) |
{a,b,c} | 花括號展開(*.{ts,tsx}) |
? / [abc] | 單字元 / 字元類 |
模式匹配不區分大小寫(路徑先小寫化)。不確定時用 ocr rules check <path> 確認。
對每個 diff,OCR 依序問:
**/*_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 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." }
]
}
{ "include": ["src/**/*.{ts,tsx,js,jsx}"], "exclude": ["**/*.gen.ts", "**/generated/**"] }
ocr review --rule ./.review-rules-only-for-this-pr.json
四層不是任意順序——它們有明確的覆蓋邏輯:
--rule(CLI)→ 臨時覆蓋,不改配置.opencodereview/rule.json → 團隊共識,可提交~/.opencodereview/rule.json → 個人偏好「首條匹配生效」意味著更具體的規則要放前面。
這是最多人誤解的:include 不是「只審這些檔」——它是「這些檔繞過後面兩門排除」。五重門的第三門 user_include 命中後立即保留,跳過 unsupported_ext 和 default_path。這就是為什麼你可以用 include 強制保留測試檔。
「規則沒觸發?」→ 跑 ocr rules check <path>。它顯示:① 匹配的層(CLI/專案/全域/內建)→ ② glob 模式 → ③ 規則正文。如果層不對(應該匹配專案但匹配了內建),多半是宣告順序問題——把更具體的規則前移。
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 除錯「規則沒觸發」。你的 monorepo 同時有 Go(API)、TypeScript(web)、SQL mapper XML(DAO)。你要一套分層規則:全站共用一條「禁止 secrets 硬編碼」,各語言再各自有專門規則,且「特定語言規則」必須壓過「共用規則」。
# .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." }
]
}
ocr rules check src/api/handler.go
ocr rules check src/web/App.tsx
ocr rules check src/dao/user-mapper.xml
**/* 這條「共用規則」不會吃掉特定規則關鍵在宣告順序:**/*(共用)必須放在最後。因為「首條匹配生效」——如果 **/* 在前,它會先匹配所有檔,後面的 Go/React/XML 規則永遠輪不到。把共用規則放最後,讓特定規則先匹配。
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,三者都看不到時才輪到)
規則檔的 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 指定 |
internal/** 的 Go 檔」與「cmd/** 的 Go 檔」分別套用不同規則(提示:兩條 pattern 都含 *.go,靠順序與路徑區分)。