Runtime, additive-only verify-and-repair for the PhoenixKit migration chain (spec §6, D6). Two public entry points:
verify/1— read-only. Never executes a create, never writes the version comment, never callsOban.Migration.up/1.repair/1—dry_run: false(the default) actually applies missing objects and comment-policy writes;dry_run: trueruns the identical pipeline but only plans (seePhoenixKit.Migrations.Repair.Executor.create_action/2).
Both return {:ok, PhoenixKit.Migrations.Repair.Report.t()} on a completed
run, or {:error, reason} for a condition the pipeline refuses to proceed
past (manifest not generated, below-floor, comment ahead of code, a
pooled connection without --unsafe-pooled, or a detected concurrent
migration). reason is always either :not_generated or a {tag, ...}
tuple; error_message/1 renders any of them to the same text
mix phoenix_kit.repair/mix phoenix_kit.doctor print.
Options
:prefix— schema prefix, already resolved (default"public"). Callers resolving it from config/--prefix(PhoenixKit.Install.PrefixConfig.resolve_prefix/1) do so before calling here — this module does not read application config itself, the same wayPhoenixKit.Migrations.Postgres.up/1does not.:repo— defaults toPhoenixKit.RepoHelper.repo/0.:adopt— R4 (§6.4). Only consulted when the raw comment isnil.:heal_comment— R2's stale-low heal. Only consulted for an in-range comment whose marker cross-check found{:stale_low, _}.:unsafe_pooled— skip the advisory lock and FKVALIDATE(§6.3). Required (else{:error, {:pooled_connection, message}}) wheneverPhoenixKit.Migrations.Repair.Environment.pooled?/2says yes AND this run would actually write (repair/1withdry_run: false, the default). Never consulted for a read-only pass —verify/1, orrepair/1withdry_run: true— which takes neither the lock nor the FKVALIDATEpath regardless of the pooled verdict, so a pooled connection can never block one. This is what letsmix phoenix_kit.doctor's manifest-repair check callverify/1plainly, with no flag, and still get real information on a PgBouncer-fronted deployment (a real, documented topology for this codebase's own runtime).:dry_run—repair/1only;verify/1always runs as if this weretrueregardless of what is passed.
Repair vs. a concurrently running migration
Postgres.up/1 and .down/1 take the same advisory lock key this module
takes (PhoenixKit.Migrations.Repair.Environment.with_lock/2) — spec §6.1 —
but the exclusion that buys is one-directional: a chain run started while
a repair holds the lock waits for it, whereas a repair started while a chain
run is mid-DDL is not blocked, because the generated wrappers disable their
DDL transaction and the migration side's transaction-scoped lock is therefore
released after its own statement (see Postgres.acquire_chain_lock!/0 for the
full reasoning). For that direction — and on a pooled connection, where
advisory locking cannot be trusted at all — the before/after raw-comment
re-read (PhoenixKit.Migrations.Repair.CommentPolicy.concurrent_migration?/2,
S18) remains the mechanism, and it detects rather than prevents.
Summary
Types
Every {:error, _} shape this module returns; see moduledoc.
Functions
Renders any error_reason() this module returns to operator-facing text — the wording mix phoenix_kit.repair/mix phoenix_kit.doctor both print verbatim.
Applies missing objects and comment-policy writes unless dry_run: true. See moduledoc.
Read-only. See moduledoc.
Types
@type error_reason() :: :not_generated | {:not_installed, prefix :: String.t()} | {:below_floor, comment :: pos_integer(), floor :: pos_integer()} | {:comment_unreadable, term()} | {:above_current, comment :: pos_integer(), current :: pos_integer()} | {:pooled_connection, String.t()} | {:concurrent_migration, before :: term(), after_ :: term()}
Every {:error, _} shape this module returns; see moduledoc.
Functions
@spec error_message(error_reason()) :: String.t()
Renders any error_reason() this module returns to operator-facing text — the wording mix phoenix_kit.repair/mix phoenix_kit.doctor both print verbatim.
@spec repair(keyword()) :: {:ok, PhoenixKit.Migrations.Repair.Report.t()} | {:error, error_reason()}
Applies missing objects and comment-policy writes unless dry_run: true. See moduledoc.
@spec verify(keyword()) :: {:ok, PhoenixKit.Migrations.Repair.Report.t()} | {:error, error_reason()}
Read-only. See moduledoc.