# `Mutare.Phoenix.LiveView.Hook`
[🔗](https://github.com/foxbenjaminfox/mutare_phoenix_live_view/blob/v0.2.1/lib/mutare/phoenix/live_view/hook.ex#L1)

`: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)  ->  socket

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]`.

This family mutates attachment and detachment calls. Mutare's built-in `:convention`
family swaps the `:cont`/`:halt` return tags *inside* a hook function (and in `on_mount/4`)
wherever those atoms appear. Enabling both families tests these behaviours independently.

Only calls to `attach_hook/4` and `detach_hook/3` are mutated, so a name-matched call
of any other arity (reachable only by an explicit qualifier) is left alone, keeping every
metamutant compiling. A piped stage collapses to the socket piped into it, with everything
upstream kept.

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.

---

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