Regression diff checks

View Source

mix kogen.diff_check compares the base and head versions of changed Elixir functions. Credo's ordinary checks inspect a single source version; these rules identify changes relative to the base.

mix kogen.diff_check origin/main...HEAD
mix kogen.diff_check HEAD~1..HEAD --format json
mix kogen.diff_check origin/main...HEAD --format github

Two dots compare the named revisions directly. Three dots use the merge base. Git failures, invalid options and source that cannot be parsed produce an error; invalid source is not silently treated as a successful analysis.

RuleDetects
query-lost-limitA query loses its SQL bound
query-lost-scopeA query loses a configured owner/tenant predicate
new-repo-in-loopA new database call is introduced inside a loop
new-silent-fallbackA newly added error/fallback branch silently returns a default
changeset-weakenedA changeset loses validation/constraints or widens sensitive cast fields
lost-transactionA transaction is removed while multiple writes remain
widened-apiA private function becomes public or the public surface grows

These rules ship as advice. New implementation tests do not establish the old prototype's historical precision measurements for the revised code. Configure error severity only after reviewing findings and measuring the desired policy.

The task supports text, JSON and escaped GitHub annotations. Its default --fail-on error does not fail merely because an advisory finding exists; --fail-on advisory does. --fail-on none still does not turn parser, Git or usage errors into success. --error-rules selects rule ids to elevate, and --rules selects the rules to run. Read Mix.Tasks.Kogen.DiffCheck and KogenCredo.Diff.CLI for exact options, including sensitive-field configuration.

Dynamic query builders, macro expansion, runtime authorization and indirect behavior outside analysed local functions remain static-analysis limits. These checks supplement tests and review; they do not prove equivalent behavior.

Project roots and umbrella paths

Paths are relative to the directory passed through --repo, including its default lib/. Output filenames remain relative to the Git repository root. A path that matches no file in either the base or head tree is a usage/path error (exit 2), rather than a successful empty scan. An unchanged range over existing paths is clean.

For an umbrella, point at the app or pass its path explicitly:

mix kogen.diff_check base..HEAD --repo apps/billing
mix kogen.diff_check base..HEAD --repo . --path apps/billing/lib/

Passing --repo lib now means lib/lib/ by default; use the project root instead. The task needs Git 2.28 or newer for consistent root-relative diff output.