All notable changes to this project are documented in this file.
The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.
Unreleased
0.1.0 - 2026-09-07
Initial release.
Added
- Two mutator families over the phoenix_swoosh template-rendering surface,
each reported under its own name and independently enable-able:
Mutare.Phoenix.Swoosh.RenderBody(:render_body) — the "which body parts render" seam. Removes arender_body/2,3call, collapsing to the email (remove— the email ships with no rendered body); narrows a literal atom template to each of its string forms (html_only/text_only— only the.html/ only the.textbody renders, the "one body part broke and nobody noticed" gap); and drops one entry per mutant from a literalput_new_formats/2map (format— that extension stops rendering, the custom-formats form of the same gap). Both wrapper arities fire, theuse-injected default-assigns/2form included; a string or computed template gets the removal mutant alone, and a single-entry format map produces no drop (that is theremovemutant's diff).Mutare.Phoenix.Swoosh.Layout(:mail_layout) — the "which layout wraps the body" seam. Removes a layout setter, collapsing to the email:put_layout/2(put— the previous layout stays) andput_new_layout/2(put_new— the layout stays unset and the body renders bare); and suppresses the layout at the render site by writinglayout: falseintorender_body's assigns (off), which is where ause-configured layout can be reached at runtime at all.offfires only where a layout is demonstrably in effect — the mailer'suseline configured one, or the call's own assigns carry a truthylayout:— so it never mints an equivalent mutant. Literal map and keyword assigns gain the entry in place, an existinglayout:entry is turned off rather than duplicated, a computed assigns argument is normalised the way phoenix_swoosh normalises it (Enum.into/2thenMap.put/3), and the default-assigns/2form gains its assigns map without requalifying the bare call (which would skip theusewrapper'sput_new_view).
- Structural pins on phoenix_swoosh's identifier positions, via call-routing
registry
:rawroutes covering the whole argument subtree: the template name, the layout value (tuple interior included), andput_new_formats/2's extension→field map — a perturbed value there is a missing-template crash at render, so core's value families never mint mutants in those positions. The format map's owner then mints the one safe mutation there itself. - A
Mutare.UseExpansionextension (Mutare.Phoenix.Swooshunder:extensions) that takes overuse Phoenix.Swooshand surfaces a wholeimport Phoenix.Swooshin place of the real injectedexcept: [render_body: 3]import + local wrapper pair — the piece that makes barerender_bodycalls resolvable (and their witness safe) at all. The standalonetemplate_root:style additionally surfaces theimport Phoenix.Viewits nestedusewould inject. The same expansion reports whether theuseline configured a layout, as theMutare.Phoenix.Swoosh.LayoutConfiguredmarker — the one channel auseexpansion has into a mutator's context, and how a compile-time option gates a runtime mutant. - Every family matches its call written qualified, aliased, bare-imported, or
piped (a piped removal becomes the
Function.identity()no-op stage). - Ignore-variant labels throughout, so
# mutare:ignore[family:label]can suppress one kind of mutant (e.g.[render_body:text_only],[render_body:format],[mail_layout:put],[mail_layout:off]). Mutare.Phoenix.Swoosh.all/0for splicing both families into a:mutatorslist, composing with the baseMutare.Swoosh.all/1preset frommutare_swoosh(a dependency of this package).- Documented scope: the
use Phoenix.Swooshline's own options are compile-time configuration Mutare prunes whole, so only the runtime calls that configuration flows through are mutable; layout-name narrowing and theput_new_layout↔put_layoutswap are written down as deferrals with their reasons; and the README carries a verified recipe for pinning core's whole-assigns-map collapse per project.