# `mix verify.release_parity`
[🔗](https://github.com/szTheory/scrypath/blob/v0.3.5/lib/mix/tasks/verify.release_parity.ex#L1)

Verifies that the file list inside the published Hex tarball for `X.Y.Z`
matches the file list inside `lib/ + guides/ + docs/` at git tag
`scrypath-vX.Y.Z`.

## Usage

    mix verify.release_parity 0.3.0
    mix verify.release_parity 0.3.0 --json

## Exit codes

  * `0` — parity (no drift)
  * `2` — drift detected (POSIX "intentional failure")
  * `1` — runtime error (network failure, missing tag, tarball fetch failure)

## Retry behavior

Inherits `SCRYPATH_RELEASE_VERIFY_ATTEMPTS` (default 10) and
`SCRYPATH_RELEASE_VERIFY_SLEEP_MS` (default 15_000) from
`verify.release_publish` so daily cron runs do not false-fail during
CDN propagation of a freshly-cut release.

## Implementation notes

Path-set equality only. Content-digest comparison is deliberately
deferred — `git archive` at a tag and `mix hex.build` from the same
commit produce byte-equal contents because they read the same locked
git tree, so content drift would require post-tag file mutation
(orthogonal to the v1.2 incident class).

The module is split into a pure `compute/2` + a thin `run/1` so the
drift-exit-code semantics (via `System.halt(2)`) can be unit-tested
without killing the ExUnit runner (Pitfall 11).

# `compute`

```elixir
@spec compute(MapSet.t(String.t()), MapSet.t(String.t())) ::
  :parity | {:drift, [String.t()], [String.t()]}
```

Pure path-set comparison. Returns `:parity` when the two MapSets are equal,
otherwise a `{:drift, only_in_git_sorted, only_in_hex_sorted}` tuple.

Exposed publicly for Pitfall 11 unit testability.

# `parse_version!`

```elixir
@spec parse_version!([String.t()]) :: String.t() | no_return()
```

Validates the one-and-only user-supplied argument against a canonical
semver regex before it reaches any subprocess (Security V5 / Pitfall 7).

# `render_json`

```elixir
@spec render_json(String.t(), :parity | :drift, [String.t()], [String.t()]) ::
  String.t()
```

Renders the `--json` output shape documented in `docs/releasing.md`.

`:parity` status renders as `"ok"` in the JSON envelope.

# `retry_until!`

```elixir
@spec retry_until!(String.t(), pos_integer(), non_neg_integer(), (-&gt; :ok
                                                               | {:error,
                                                                  term()})) ::
  :ok | no_return()
```

Retry loop copied from `verify.release_publish.ex:58-75` — same env-var
names, same semantics — so maintainers learn one retry model.

Exposed publicly for Pitfall 1 unit testability (stub the fun with a
counter-backed Agent).

---

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