:lv_hook — removes a Phoenix.LiveView lifecycle-hook management call, collapsing it to
the socket it would have returned:
attach_hook(socket, :log, :handle_event, &log/3) -> socket
detach_hook(socket, :log, :handle_event) -> socket
socket |> attach_hook(:log, :handle_event, &log/3) -> Elixir.Function.identity()Both directions of the hook lifecycle are one family with two variant labels: attach
("no test depends on this hook running" — the hook never fires) and detach ("no test
depends on this hook stopping" — the hook keeps running). Suppress one kind with
# mutare:ignore[lv_hook:attach] / [lv_hook:detach], or the whole family with
# mutare:ignore[lv_hook].
Only the attachment is this family's seam. The :cont/:halt decision inside a hook
function (and in on_mount/4) belongs to Mutare's built-in :convention family, which
swaps those atoms wherever they appear — enabling both families mutates the two axes
independently.
Only the real arities fire — attach_hook/4 and detach_hook/3 — so a name-matched call
of any other arity (reachable only by an explicit qualifier) is left alone, keeping every
metamutant compiling. Piped calls are removed with the Elixir.Function.identity() no-op
stage (the :lv_event trick — the :Elixir-led alias is never rewritten by alias
resolution, so the no-op always names the real Function.identity/1).
The hook name and stage arguments are structural identifiers, not computed values —
perturbing :handle_event raises inside attach_hook (an uninformative crash-kill), and a
renamed hook still runs (a near-unkillable equivalent). This family therefore routes both
positions :raw (Mutare.CallRouting), so no core family — AtomLiteral, StringLiteral,
ConventionAtom, … — mutates them or anything inside them.
Matches direct (Phoenix.LiveView.attach_hook(...)), aliased, and bare-imported
(use-injected) calls.