Getting started · 9 of 9

Rule ids and categories

What a rule id like BV002 or SL013 means: the family prefixes (BV, BP, BS, BC, EX, SL, MY) and the two-letter category codes (LK, RW, CN, …) every rule carries.

Every finding carries a rule id such as BV002, BP006 or SL013. The two letters are the family — which corpus the rule belongs to, where it runs and what it reads. The three digits are stable for the life of the rule; a retired rule leaves a gap and a number is never reused. Separately, every rule is tagged with a two-letter category such as LK or CN that says what goes wrong. Families group rules by mechanism, categories by harm: one category spans several families, and one family covers many categories.

Families — the id prefix

PrefixEngineCorpusRulesReadsDefault posture
BV###PostgresCore safety50The parse tree of one migration; refined against the live schema with --dbBlocks CI on critical findings
BP###PostgresPerformance11The parse tree, plus index and table statistics with --dbAdvisory notes; never fails CI unless team policy raises it
BS###PostgresSet ordering2Every file in the migration set, in apply orderBlocks CI; silent on a single file
BC###Postgres and any text fileCredentials6String literals in SQL; with bolvrk secrets, any fileBlocks CI; the value is always masked
EX###PostgresPlan rulesEXPLAIN (FORMAT JSON) output from bolvrk explainWarnings and notes; not part of check
SL###SQLite (alpha)Static corpus35The SQLite parse tree, behind --engine=sqliteLocal only; no --db or --remote yet
MY###MySQLStatic corpusThe MySQL parse treePlanned; not run anywhere yet

The engine a rule targets follows from the prefix alone: SL is SQLite, MY is MySQL, every other family is Postgres. Reports and the rule reference list families in a fixed order — BV, BP, BS, BC, then SL — and numerically within a family. The free CLI bundles 18 of the 68 Postgres SQL rules; the rest run in the hosted service on Startup and above.

Categories — the two-letter tag

Categories are shared across every engine, so one filter reads the same for a Postgres rule and a SQLite rule. A rule has exactly one. They are the category filter on the rule reference and on the Policy page, and the code chip on every rule row.

CodeNameWhat it covers
LKLocks & blockingStatements that queue or block production traffic while they run.
RWTable rewritesChanges that copy or rewrite every row of a table.
CNConstraints & keysConstraints, primary keys, and foreign keys added the slow or unsafe way.
DSDestructive changesStatements that remove data, objects, or enforcement.
DWDeploy windowChanges that break code still running the previous release.
TXTransactionsStatements that misbehave inside a multi-statement transaction.
RPReplication & durabilityReplication, WAL, vacuum, and crash-safety settings.
TYType choicesColumn types with well-known traps.
OPOperational safetyServer-level or batch operations that do not belong in a migration.
PCPrivileges & credentialsSecurity posture: privileges, row-level security, secrets in SQL.
IXIndex hygieneIndexes that cost writes without paying back in reads.
QSQuery shapeIndex design judged against the migration's own queries.
TSTable settingsPer-table storage and planner settings that undo good defaults.
SOSet orderingPromises one migration makes that a later one must keep.

Roughly: the first ten categories are where the BV, BC and SL rules land, IX, QS and TS belong to the performance corpus, and SO is the set-ordering corpus. That is a tendency, not a rule — BV017 (foreign key without a covering index) is CN.

Reading an id

BV002 — Postgres core safety, rule 2: ADD COLUMN NOT NULL without a default. Category CN. BP006 — Postgres performance, rule 6, category QS: an index judged against the queries in the same migration. SL013 — SQLite, rule 13, category DS: a destructive change, tagged with the same code a Postgres DROP would carry.

One command to start

Check a migration now, sign in when the team wants the rest.

Sign in with GitHub