# `Mix.Statifier.GateGuard`
[🔗](https://github.com/riddler/statifier-ex/blob/v2.0.0/lib/mix/statifier/gate_guard.ex#L1)

Finds changes to the quality gate's own configuration that no one has
justified in writing.

ADR-0011 says the gate's config is not agent-editable: a red gate is never
turned green by lowering a threshold, skipping a check, tagging a test
`@tag :skip`, or shrinking the regression ratchet. This module is the
mechanical half of that policy. It reads a diff of the current branch against
its base and reports a finding for every guarded change the diff does not also
record in `docs/quality-gate-changes.md`.

The check deliberately cannot tell a weakening from a strengthening, and does
not try. Any guarded change needs a ledger entry; the entry is where a human
says which it was.

`analyze/1` is pure - it takes a diff and two registry snapshots and returns
findings. `collect/1` is the part that talks to git, and takes an
`opts[:runner]` so tests never need a fixture repository.

# `finding`

```elixir
@type finding() :: %{
  file: String.t(),
  line: pos_integer() | nil,
  severity: String.t(),
  check: String.t(),
  message: String.t()
}
```

# `source`

```elixir
@type source() :: %{
  diff: String.t(),
  base_registry: String.t() | nil,
  head_registry: String.t() | nil
}
```

# `analyze`

```elixir
@spec analyze(source :: source()) :: [finding()]
```

Turns a diff and two registry snapshots into unjustified-change findings.

Findings cleared by a ledger entry added in the same diff are dropped, so a
ledger entry written on some other branch clears nothing here.

# `collect`

```elixir
@spec collect(opts :: keyword()) ::
  {:ok, source()} | {:error, String.t()} | :no_base_ref
```

Reads the diff and registry snapshots the guard needs out of git.

Base ref resolution is `opts[:base]`, then `origin/main`, then `main`. When
none of them resolves there is nothing to diff against, so this returns
`:no_base_ref` rather than guessing a base; the task turns that into a skipped
stage.

`opts[:runner]` replaces the `git` shell-out with a function of an argument
list returning `{output, status}`, mirroring `Mix.Tasks.Test.Regression`.

# `guarded_paths`

```elixir
@spec guarded_paths() :: [String.t()]
```

The gate-config files any edit to which needs a ledger entry.

# `ledger_path`

```elixir
@spec ledger_path() :: String.t()
```

Path of the justification ledger, relative to the project root.

---

*Consult [api-reference.md](api-reference.md) for complete listing*
