brindle / Guides

Rule packs and checks for coding agents

A rule in a prompt is a request. A rule brindle checks against the branch's diff is a gate. Rule packs let you write both in one markdown file and attach it to any agent profile.

Screen recording of brindle workers finishing in their own branches, each one reviewed and checked before it merges

Most teams have a few standing rules for code written by agents: no shelling out with user input, no new dependency without a conversation, every change in the source tree comes with a test. Pasting them into every prompt works until someone forgets, and a model that is told a rule can still break it. brindle splits the problem in two. The prose of a rule reaches the agent in its prompt, for every provider. The parts that can be matched mechanically are checked against what the branch actually changed, in the same step that runs your repository's checks.

Start from a profile with extends

An agent profile is a markdown file with frontmatter that says which provider, model and prompt a worker or reviewer uses. A profile can start from another one instead of copying it. Put it in .brindle/agents/ in the repository, or in ~/.brindle/agents/ for yourself:

---
name: backend-auditor
description: Reviews a branch as a backend security audit
extends: reviewer
rules: security/backend
---
Audit the change the way a security reviewer would...

extends names one parent, looked up in the repository's profiles, then yours, then the built-ins. The child's fields override the parent's one by one, and a field the child leaves out is the parent's. The child's prompt is added to the parent's, so the child adds to the role rather than restating it. Chains work, and a cycle is an error that names the loop. brindle profile show backend-auditor prints the profile as brindle resolves it, which is the quickest way to see what you inherited.

Write a rule pack

The rules field names rule packs. A pack is a markdown file in .brindle/rules/ (the repository's), ~/.brindle/rules/ (yours) or one of brindle's built-ins, in that order of precedence. A pack named security/backend is the file security/backend.md. A child's packs are added after its parent's.

---
name: security/backend
description: Backend code handles untrusted input, secrets and the shell carefully
deny_deps: [pickle]
require_tests_for: [src/**/*.py]
deny_patterns:
  - shell=True
  - \beval\(
---
Treat every input that crosses a trust boundary as hostile until validated...

The text below the frontmatter is the standing instruction. It joins the agent's prompt on Claude Code, Codex, Antigravity and local models alike, with the mechanical rules spelled out so the agent knows what will be checked. A pack with only prose is a prompt and nothing more.

brindle ships three packs you can use as they are. security/backend covers shell-outs from input, eval, unsafe deserializing and secrets in code. tests/only asks for changes to tests rather than the code under test, so every diff must touch a test file. style/minimal-diff asks for the smallest diff that does the job. Two built-in profiles use them: backend-auditor is reviewer plus security/backend, and qa is developer plus tests/only.

What the diff checks do

Three frontmatter keys are checked against the branch's diff, which is what the branch commits on top of its base:

These run in the same step as your checks. The reviewer's check summary lists each pack as pass or fail with the offending file:line, and merge_workspace refuses the branch while a pack fails, handing back the violations to send to the worker, exactly like a failing test. Nothing about this needs a model to notice: a worker that adds shell=True is stopped by a pattern match on the added line.

Be clear about what these are. They are pattern checks on the diff's text. They catch the honest mistake and the obvious shortcut and they name the line. A dependency loaded through a name built at run time, or a test that tests nothing, is still for the reviewer to catch, which is why the pack's prose and the reviewer matter too. The diff is read in a way a repository cannot tamper with, because git's attributes, drivers and prefixes are overridden, and a worker whose profile no longer loads fails the gate rather than passing with no rules. A pack that a profile names but that does not exist stops the launch, so a rule never silently stops applying.

Create and lint profiles

You can write the files by hand, or let brindle write the skeleton:

brindle profile new api-dev --extends developer --rules security/backend
brindle profile lint

brindle profile lint checks every profile it can see for a missing parent, a cycle, an unknown pack, a pattern that does not compile and an unknown provider, and exits 1 if any fails. That makes it a good line in your own CI or a pre-commit hook, so a typo in a pack is caught when it is written and not when a worker is launched.

Share rules with a team

Commit .brindle/rules/ and .brindle/agents/ so everyone on the repository gets the same rules. With brindle Pro, reviewers that keep asking for the same change can suggest a rule for it: brindle rules suggest lists the candidates and brindle rules accept adds one to .brindle/rules/learned.md, an ordinary pack that every profile in the repository picks up. Nothing changes until you accept it. With brindle Team, an admin can publish profiles and packs to the whole organization and pin them, so a repository cannot loosen what the organization requires.

Where to go next

Start with one pack and one profile that matters, for example qa for test-writing tasks, and watch what the review summary reports on the first few branches. Tighten from there. The configuration docs describe profile fields and the lookup order, and the guide to cross-model review shows how the reviewer and the checks fit together at merge time. If you want to see a gated merge before pointing brindle at your own code, brindle demo runs one on a practice repository.

Try brindle. Free to use, source-available (Brindle License 1.0), on macOS or Linux.
curl -fsSL pawdelta.com/brindle/install | sh