~/codigoagente
AI agents

Open Code Review custom rules: how to write your rule.json, with real examples

Rules are what turn Open Code Review from a generic reviewer into one that knows your project. They tell the model what to look for in each file: "amounts are in cents", "every route validates its input", "no test only covers the happy path". In this third part of the guide we write a real rule.json for the sample API from part 2, debug it with ocr rules check and show you the ordering mistake that caught us out.

How ocr picks the rule for each file

For every file that passes the filter, ocr looks for a rule in four layers, in this order. The first match wins and the other layers are never consulted:

PriorityLayerWhere it livesTypical use
1`--rule`The file you pass on the CLIA special review (security only, an audit)
2Project`<repo>/.opencodereview/rule.json`Team standards. Committed to the repo.
3Global`~/.opencodereview/rule.json`Your personal preferences across repos
4SystemEmbedded in the binaryPer-language rules: Java, Go, TS/JS, Python, PHP, SQL, YAML…

A missing layer file is silently skipped. The system layer always exists, so there is always some rule.

The chosen rule is injected into the sub-agent prompt as {{system_rule}}. That is why a good rule makes such a difference: it is the checklist the model reviews that file against.

The rule.json format

It has three fields, all optional:

.opencodereview/rule.jsonjson
{
  "include": ["src/**/*.test.js"],
  "exclude": ["scripts/**"],
  "rules": [
    { "path": "src/routes/**/*.js", "rule": "Rule text..." }
  ]
}
  • include: patterns that bypass the default exclusions (tests, unsupported extensions, dist/…). Careful: it is not an allowlist; files that don't match are still reviewed normally.
  • exclude: patterns that are never reviewed. The highest-priority user filter. Merged with whatever you pass to --exclude.
  • rules: a list of {path, rule} evaluated in order. The first entry whose path matches decides the file's rule.

Each rules entry accepts a third field, merge_system_rule. With false (default) your rule replaces the system rule for that file. With true they are combined: the language rule first, then yours.

Patterns use doublestar glob syntax: * (does not cross /), ** (any depth), {ts,tsx} (alternatives), ? and [abc]. Matching is case-insensitive.

A real rule.json, step by step

Our feat/cupones branch adds src/lib/cupones.js (applies a coupon and splits the total into installments), changes an Express route and adds a test. We want three things:

  1. Code in src/lib/ reviewed with the JavaScript rules plus our money rule.
  2. Routes reviewed with our validation rule (without the generic React rules, which don't apply).
  3. Tests, which ocr excludes by default, reviewed too, with their own rule.
.opencodereview/rule.json with include for tests, exclude for scripts and three rules for tests, lib and routes
The final rule.json of the sample project (rule text in Spanish) · codigoagente
.opencodereview/rule.jsonjson
{
  "include": ["src/**/*.test.js"],
  "exclude": ["scripts/**"],
  "rules": [
    {
      "path": "**/*.test.js",
      "rule": "Check that every test covers edge cases (0, 1, negative values), not just the happy path."
    },
    {
      "path": "src/lib/**/*.js",
      "rule": "Money: amounts are stored in cents (integers). Flag any decimal arithmetic, any percentage not divided by 100 and any split whose parts don't add up to the total.",
      "merge_system_rule": true
    },
    {
      "path": "src/routes/**/*.js",
      "rule": "Every Express route must validate req.params and req.body before use, await every async call and return 404 when the resource does not exist."
    }
  ]
}

(Our test used the same rules written in Spanish, as in the screenshot; the rule text goes into the prompt verbatim, so any language works.)

Once committed, ocr review --preview lists src/lib/cupones.test.js under "Will review": the include overrode the default test exclusion. .opencodereview/rule.json shows up too, reviewed with the system JSON rule.

Check which rule wins: ocr rules check

ocr rules check <path> tells you which layer and pattern win for a file, and prints the full rule text. It is debugging tool number one:

ocr rules check output for the route, the test and the library, showing the Project layer and the matching pattern
Each file with its layer, pattern and rule; for `cupones.js` the user part is appended to the system part · codigoagente
terminalsh
ocr rules check src/routes/pedidos.js
ocr rules check src/lib/cupones.test.js
ocr rules check src/lib/cupones.js

For cupones.js, thanks to merge_system_rule: true, the output has two blocks: ## System-Specific Rules (Mandatory) with the JS/TS checklist (typos, dead code, ===, null checks, async handling, XSS, eval, secrets…) and ## User-Specific Rules (Mandatory) with our money rule.

To see what a one-off rule would do without touching the project, pass it with --rule:

terminalsh
echo '{"rules":[{"path":"**/*","rule":"Security only: SQL injection, secrets in code and missing authorization. Ignore style."}]}' > security-only.json
ocr rules check --rule security-only.json src/lib/cupones.js
# Source: Custom (--rule)
# Pattern: **/*
ocr review --from main --to feat/cupones --rule security-only.json

The mistake we made: rule order

Our first version had the test rule last, after src/lib/**/*.js. Going from general to specific felt natural. ocr rules check set us straight:

ocr rules check on the test file returning the src/lib/**/*.js pattern instead of the test rule
The test `src/lib/cupones.test.js` also matches `src/lib/**/*.js`, which came first, so it never reached the test rule · codigoagente

src/lib/cupones.test.js matches src/lib/**/*.js, and since that entry came first, it won. The test rule never applied, with no warning at all. The fix is the rule.json above: most specific first, most general last. And if two of your rules must apply to the same file, write them together as one: merge_system_rule only merges with the system layer, not your rules with each other.

Useful recipes

Global security on top of each language's rules

In ~/.opencodereview/rule.json, for all your repos:

~/.opencodereview/rule.jsonjson
{
  "rules": [
    {
      "path": "**/*",
      "rule": "Security review: flag hardcoded secrets, unvalidated redirects, and missing authz checks.",
      "merge_system_rule": true
    }
  ]
}

Without merge_system_rule, that **/* would replace the Java, Python, Go… rules in every file. With true, each file gets its language rule plus yours. Remember the global layer has the lowest priority: if the project has a matching rule, the global one is skipped.

Ignore generated code and focus on src

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

Backend standards for Go or SQL

.opencodereview/rule.jsonjson
{
  "rules": [
    {
      "path": "internal/api/**/*.go",
      "rule": "Any handler that opens a transaction must defer tx.Rollback() right after. Wrap errors with fmt.Errorf(\"...: %w\", err)."
    },
    {
      "path": "**/*mapper*.xml",
      "rule": "Check for SQL injection (${} instead of #{}), unbound parameters and unclosed XML tags."
    }
  ]
}

Docs-in-sync rule

The Open Code Review repo itself uses this technique in its .opencodereview/rule.json: when someone touches internal/llm/providers.go, the rule requires updating the provider table in all four documentation languages and adding a test per new provider. It is a great model for "if you change A, change B too" rules that no linter catches.

Rules and context: --background

Rules say how to review; --background says what the change was meant to do. They complement each other:

terminalsh
ocr review --from main --to feat/cupones \
  -b "Discount coupons (percentage 0-100) and installment payments. Amounts in cents."
# or from a requirements file
ocr review --from main --to feat/cupones -B docs/coupons.md

With context, the model can say "this does not meet the requirement", not just "this has bad style".

Common errors

My rule isn't applied. Run ocr rules check <file>. It is almost always one of these: an earlier entry matches first (the case above), the pattern doesn't cover the depth (src/*.js doesn't enter subfolders; use src/**/*.js), or a --rule overrides the whole project.

A file isn't reviewed even though it has a rule. Rules don't decide whether a file is reviewed; the filter does. Check the reason with ocr review --preview and, if it is default_path or unsupported_ext, add the pattern to include.

I added include and now "it only reviews that". It shouldn't: include is not an allowlist. If you really want to narrow it, use exclude.

include doesn't bring back vendor/. If the folder is at the repo root, it is filtered while reading the diff (provider_directory) and no include recovers it. A nested vendor/ (api/vendor/) can be included.

.env is never reviewed. By design: secret paths are excluded before your rules and cannot be forced.

FAQ

Can I write rules in another language? Yes. The rule text goes into the prompt as is; we wrote ours in Spanish and the model applied them. To get comments in a given language, set ocr config set language <Language>.

Where can I see the system rules? Run ocr rules check on a file of that language (with no matching project rule), or browse internal/config/rules/rule_docs/ in the repo (one Markdown file per language).

Do rules apply in delegation mode too? Yes. ocr delegate rule <files> resolves the same layers and groups files that share a rule. See part 4.

Should I commit .opencodereview/rule.json? Yes, that is the point: the whole team and CI review with the same standards.

Conclusion

A short, well-ordered rule.json is what gets the most out of Open Code Review: specific rules first, merge_system_rule: true when you want to add rather than replace, and include to review what is ignored by default. Before trusting a rule, verify it with ocr rules check.

In part 4 we use these same rules from Claude Code, with no API key, through delegation mode.

// source code

Repository in this article
The code this article covers is public: browse it, star it or clone it from here.

// implementation

Need this automation running in your business?
Don't spend weeks wiring servers, APIs and agents. Safe Build Labs, the agency that sponsors codigoagente, builds it for you, production-ready.
We tested mcp-builder, Anthropic's official skill for building MCP servers: one for a small shop in 99 seconds for 0.25 USD, plus two version bugs to avoid.
  • Skills for your agents
  • Claude Code
read article
We tested webapp-testing, Anthropic's official skill that tests your local web app with Playwright: setup, with_server.py, two real pitfalls and what it costs.
  • Skills for your agents
  • Claude Code
read article
video-shotcraft is an Apache-2.0 skill with 150 shots that lets Claude Code build your product's promo video in Remotion. How it works and what it costs.
  • Skills for your agents
  • Claude Code
read article