mix adr.check (Statifier v2.0.0)

Copy Markdown View Source

Reports lines the branch adds that look like violations of ADR-0002 (Appendix D naming), ADR-0003 (pure core with effects), ADR-0004 (predicator as the datamodel), ADR-0008 (generated identifier formats), or ADR-0018 (process artifacts are not code comments) - plus, since ADR-0058, three checks that are not about the diff at all: docs/adr/ numbers colliding, docs/adr/README.md's table falling out of bijection with the directory, and a branch-added ADR number that already exists on the base ref under a different filename.

It runs as the ADR guard custom stage of mix quality, so drift from an accepted decision is a named gate failure rather than something review has to catch. Every finding names the ADR it is about. The ADR-0002/0003/0004/0008 findings clear with an inline comment on or above the line citing that ADR or the word "deviation" - the same justification CLAUDE.md already asks for. The ADR-0018 bead-ID finding does not: an ADR citation is exactly what it is checking against, so it clears only with its own marker, ADR-0018-exempt, placed on or above the line.

The ADR-0058 numbering findings (adr-0058-duplicate-number, adr-0058-readme-index, adr-0058-base-number) clear on neither escape hatch. They carry no line number - they are invariants over the working tree's filenames and README table, or a comparison against the base ref's docs/adr/ listing, not patterns over an added line - and there is no comment that makes a duplicate ADR number or a missing table row correct. The fix is a renumber and a README row move.

adr-0058-base-number is the odd one out: a finding from it is always real, but a pass promises nothing when origin/main is stale. ADR-0058 decision 2 is explicit that this half "exists for early warning only" and that "no document, skill, or report may cite a bare-gate ADR guard pass as evidence that no collision exists on the remote" - the actual guarantee is adr-0058-duplicate-number and adr-0058-readme-index, which run tree-locally and therefore bite once wurk:mr's post-fetch rebase has materialized a colliding file into the working tree.

Usage

mix adr.check
mix adr.check --base upstream/trunk
mix adr.check --format json

Options

  • --base - ref to diff against, tried before origin/main and main
  • --format json - print the ExQuality finding contract instead of prose

Exit status is 0 when nothing looks off, 1 when something does, and 2 when no base ref resolves - but 2 no longer means nothing ran. The docs/adr/ numbering invariant needs no base ref, so it runs regardless; 2 means only that the diff-based and base-ref checks had no base ref to run against, and that the numbering invariant ran and was clean. Exit 1 is possible with no base ref at all, when the numbering invariant itself finds a collision - a duplicate number or a README bijection failure sitting in the tree fails the stage whether or not origin/main resolves. The skip_exit_code: 2 stage config turns a real exit-2 skip into a reasoned skip rather than a pass; a diff that simply touches no lib/ file is a pass, not a skip, since there was something to check and nothing was wrong with it.

Summary

Functions

Runs the guard and reports the outcome instead of halting.

Functions

execute(argv, opts \\ [])

@spec execute(argv :: [String.t()], opts :: keyword()) ::
  {:ok, iodata()} | {:skip, String.t()} | {:error, iodata()}

Runs the guard and reports the outcome instead of halting.

opts are passed through to Mix.Statifier.AdrGuard.collect/1, so opts[:runner] drives this without a real git history.