The result shape PhoenixKit.Migrations.Repair.verify/1 and .repair/1 return —
versions observed, one finding per object/invariant that was not silently
clean, and the derived exit code for mix phoenix_kit.repair (spec §6.1's
final pipeline step, §6.2's severity mapping, and the 0/1/2 contract on
the mix task's moduledoc).
Fields
:prefix— the schema this report is about.:dry_run— whether the run that produced this report was read-only (PhoenixKit.Migrations.Repair.verify/1, or.repair/1withdry_run: true). Purely descriptive — everyfinding.kindalready disambiguates:would_repair(dry-run) from:repaired(applied).:versions—%{comment: comment(), floor: pos_integer(), current: pos_integer()}.commentis the raw value (seePhoenixKit.Migrations.Repair.Probe.raw_comment/2) —:absent(thephoenix_kittable does not exist),nil(table exists, no comment — the half-installed/adopt case), or the numeric value.:comment_action— what, if anything, happened to the version comment this run::none,{:healed, version}(stale-low, R2/--heal-comment),{:adopted, version}(R4/--adopt), or{:would_heal, version}/{:would_adopt, version}for a dry run that found the condition but did not write.:findings— seefinding/0.
Severity → exit code
mix phoenix_kit.repair's contract is exit 0/1/2 (moduledoc); this
module computes that from the highest severity present, never from
counting kinds one by one, so a run that both fixed something repairable
AND surfaced an error-severity divergence reports the divergence's exit
code (2) — the operator still needs to see it even though other things
got fixed:
- any
severity: :errorfinding present →2("report-only divergences present" — spec §6.2's error-severity bucket: wrong type/length/ default, unexpected NOT NULL, divergent index/constraint definition,:create_failed, a failing data invariant) - else any
severity: :repairablefinding present →1("repairs applied-or-pending" — covers both an actual:repairedinrepair/1and a:would_repairinverify/1/dry-run, deliberately collapsed to the same code per the mix task moduledoc's own wording) - else →
0(clean; only:info-severity findings, if any)
Summary
Types
Raw comment as read by Probe.raw_comment/2 — never the legacy no-comment→1 mapping.
One reportable event. since/object_id are nil for findings that are not
about a single manifest object (comment-policy findings, data invariants use
object_id: nil + a since from the invariant).
See the moduledoc's severity → exit code table.
Functions
Appends one finding (prepended internally; findings/1 returns them in append order).
The highest-severity-wins exit code — see the moduledoc's "Severity → exit
code" section. 0/1/2 only; never raises.
Findings in the order they were added.
Builds an empty report for the given prefix/dry_run/versions triple.
Sets comment_action.
Counts findings by severity and by kind, plus the total — the numbers
mix phoenix_kit.repair's human-readable summary line and --json output
both render from.
Plain-data rendering for --json — every value is already a JSON scalar,
list, or map except :versions.comment (:absent needs a string form) and
:comment_action (a tagged tuple). Both are flattened here; everything else
(finding.kind/finding.severity atoms included) is left for the caller's
JSON encoder to stringify, matching how mix phoenix_kit.doctor/
release_check already emit atoms as plain output.
Types
@type comment() :: :absent | nil | non_neg_integer()
Raw comment as read by Probe.raw_comment/2 — never the legacy no-comment→1 mapping.
@type comment_action() :: :none | {:healed, pos_integer()} | {:adopted, pos_integer()} | {:would_heal, pos_integer()} | {:would_adopt, pos_integer()}
@type finding() :: %{ kind: atom(), severity: severity(), object_id: String.t() | nil, since: pos_integer() | nil, message: String.t() }
One reportable event. since/object_id are nil for findings that are not
about a single manifest object (comment-policy findings, data invariants use
object_id: nil + a since from the invariant).
@type severity() :: :info | :repairable | :error
See the moduledoc's severity → exit code table.
@type t() :: %PhoenixKit.Migrations.Repair.Report{ comment_action: comment_action(), dry_run: boolean(), findings: [finding()], prefix: String.t(), versions: %{comment: comment(), floor: pos_integer(), current: pos_integer()} }
Functions
Appends one finding (prepended internally; findings/1 returns them in append order).
@spec exit_code(t()) :: 0 | 1 | 2
The highest-severity-wins exit code — see the moduledoc's "Severity → exit
code" section. 0/1/2 only; never raises.
Findings in the order they were added.
@spec new(String.t(), boolean(), %{ comment: comment(), floor: pos_integer(), current: pos_integer() }) :: t()
Builds an empty report for the given prefix/dry_run/versions triple.
@spec put_comment_action(t(), comment_action()) :: t()
Sets comment_action.
@spec summary(t()) :: %{total: non_neg_integer(), by_severity: map(), by_kind: map()}
Counts findings by severity and by kind, plus the total — the numbers
mix phoenix_kit.repair's human-readable summary line and --json output
both render from.
Plain-data rendering for --json — every value is already a JSON scalar,
list, or map except :versions.comment (:absent needs a string form) and
:comment_action (a tagged tuple). Both are flattened here; everything else
(finding.kind/finding.severity atoms included) is left for the caller's
JSON encoder to stringify, matching how mix phoenix_kit.doctor/
release_check already emit atoms as plain output.