Changelog

The corpus, release by release

Currently 69 rules at corpus v0.9.0. Every entry below shipped with fixtures both directions — see the rule reference for the live list.

v0.9.0

The credential corpus — no secret reaches the repo

  • A new rule family, BC, for credentials written into a migration: a role created or altered with a plaintext password (BC001; an md5 verifier is a warning, a SCRAM verifier stays silent), a foreign server, user mapping or subscription carrying an embedded credential (BC002), a connection string with the password inline in any string literal (BC003), a vendor-format API key, JWT or PEM private key in any literal (BC004), and a secret-named column given a literal value (BC005).
  • Published formats only, never entropy: sk_live_, AKIA, ghp_, xoxb-, -----BEGIN PRIVATE KEY----- and friends fire; a uuid, a bcrypt hash, a placeholder or an empty string stays silent.
  • Every finding masks the value. When a BC rule fires on a statement, the engine redacts the secret in every finding on that statement — the report, the PR comment and the run log never repeat it.
  • New command: bolvrk secrets runs the same credential rules over any file — .env, YAML, JSON, source, shell — line by line, with a text-only rule for secret-named settings assigned a literal (BC006). Same findings contract, same policy overrides, same SARIF output. Always local: the scanner has no remote mode by design.
  • Suppression works the same way: bolvrk-ignore BC006: reason on the line or the line above, in any comment syntax, reason required.
v0.8.0

The performance corpus — indexing and query health

  • A new rule family, BP, for migrations that work and then quietly slow you down. Advisory by default: notes never block CI unless your policy says so.
  • Eleven rules to start: redundant indexes covered by a wider prefix (BP001), indexes led by a boolean or near-constant column (BP002), B-tree on jsonb, array, or tsvector columns (BP003), DROP INDEX removing the only index covering a foreign key (BP004), one more index on a hot table already carrying many (BP005), composite indexes led by a range column ahead of an equality column (BP006), partial or expression indexes the migration's own queries cannot use (BP007), backfills filtering a large table on columns no index leads with (BP008), column statistics disabled with SET STATISTICS 0 (BP009), autovacuum scale factors raised past the point of usefulness (BP010), and DROP INDEX on an index the planner is demonstrably using (BP011).
  • Connection health: stored connections are reached on a schedule and on every use; the Connections page shows the last result, and a check page warns when the connection its live context came from is unreachable, removed, or has not been reached in a day.
  • SARIF export: bolvrk check --format=sarif and the GitHub Action's sarif-file input write a SARIF 2.1.0 log with every rule's description and fix, so findings appear in GitHub code scanning next to the PR comment.
  • Set-ordering rules: a constraint added NOT VALID with no later VALIDATE (BS001), and a migration naming a column an earlier one dropped or renamed (BS002). They read the whole set — locally, through --remote, and on pull requests — and stay silent when only one file is visible.
  • Multi-file awareness: when several files are checked together, each one sees the tables, column types, indexes and foreign keys the earlier files declared — so an index on a boolean column created two migrations ago is judged correctly. Structure only; size and traffic still come from a connected database.
  • CLI workflow parity: check many files or globs at once (or stdin), keep defaults in bolvrk.json, silence one finding with -- bolvrk-ignore RULE: reason directly above the statement (reason required, every attempt recorded in the report), and run bolvrk policy pull so local runs block on exactly what CI blocks on — the same policy function now lives in the engine.
  • New command: bolvrk explain runs the file's own queries through EXPLAIN — read-only, never executed — against your shadow database and reports sequential scans of large tables, nested loops over sequential scans, and large sorts, in the same findings format as check.
  • Three of those read the migration's own UPDATE, DELETE, and INSERT ... SELECT clauses as evidence — never guessing at application queries. No query in the file, no finding.
  • The live-schema snapshot now carries index metadata (access method, partial and expression flags, validity, size, scan counts) and column statistics where the read role may see them — still structure only, never rows.
v0.7.0

The corpus reaches 50 — operations out of schema history

  • Five new rules: unbounded INSERT ... SELECT backfills (BV046), timeout guards disabled with SET ... = 0 (BV047), ALTER SYSTEM in a migration (BV048), row-level security switched off on a live table (BV049), and write privileges granted to PUBLIC (BV050).
  • A zero-valued lock_timeout no longer counts as a guard — zero means "wait forever", the opposite of protection.
  • Fewer false alarms: DETACH PARTITION CONCURRENTLY, standalone VALIDATE CONSTRAINT, and DROP INDEX CONCURRENTLY hold only weak locks and no longer trip the lock_timeout advisory.
v0.6.1

Every rule tells you what to do instead

  • All 45 rules now ship remediation guidance — a "do this instead" description and a corrected example, validated against the checker itself.
  • Rule documentation pages redesigned around the fix: what fires, why it is dangerous, and the safe pattern side by side.
  • A 34-case near-miss battery pins down safe lookalikes: statements that resemble dangerous ones and must stay silent.
v0.6.0

Hot-table awareness and database-wide REINDEX

  • Tier-2 checks now read table activity (pg_stat_user_tables): lock-heavy operations escalate to critical on tables under heavy sustained write traffic, not just large ones.
  • BV045 — REINDEX SYSTEM / REINDEX DATABASE: a database-wide rebuild is a scope failure in a migration; system catalog indexes can never rebuild concurrently.
  • Non-concurrent index creation (BV003) no longer goes silent on small tables with heavy write traffic.
v0.5.0

The corpus reaches 44 rules

  • Twenty-one new rules across rewrite traps, destructive operations, lock hygiene, replication configuration, and column-type advisories.
  • New "note" severity: this will work — and then likely regress performance. Advisories for humans — and for any AI loop iterating against the checker.
  • Every rule now carries a minimal firing example; the docs and this site derive their rule lists straight from the engine.
v0.3.0

Schema-aware refinements

  • Foreign keys without a covering index (BV017) — escalates using the live index list.
  • Tables created without a primary key (BV018).
  • Severity taxonomy ratified: critical / warning / note.
v0.1.0

First public corpus

  • Sixteen rules covering the classics: non-concurrent indexes, NOT NULL without default, inline constraint validation, type-change rewrites, unbounded writes.
  • Two-tier design: static parse-tree analysis everywhere, live-schema refinement when a shadow database is connected.
  • Fixtures in both directions for every rule, validated against Postgres 13–17 in CI.