Firebreak.Orphans (Firebreak v0.1.0)

Copy Markdown View Source

Finds stateful processes that no supervisor starts.

A GenServer / :gen_statem / GenStage / Agent is meant to be a supervised process. If its module appears in no supervisor's subtree — not a static child, not a DynamicSupervisor child we could recover, not a Broadway producer — then either it is started unsupervised (no restart on crash) or it is never started at all. Both are bugs the supervision tree alone won't show.

The signal is sharpest when something else depends on the orphan: a resolved coupling edge into it means a caller will hit :noproc the moment it reaches for a process nothing keeps alive. That case is reported at :medium; an orphan with no inbound callers is reported at :info (it may be dead code, or started by a path static analysis can't see). Always best-effort: a child spec assembled at runtime can hide a supervision we genuinely cannot resolve.

Two pieces of evidence (from Firebreak.ModuleInfo) refine that verdict instead of leaving every unsupervised process under the same hedge:

  • Supervised via a builder — if something builds a Mod.child_spec(...) for the module (often inside a __using__ macro or a per-entity wrapper), it is supervised, just through an indirection we can't attribute to a named supervisor. Reported at :info as context, relabelled so it doesn't read as a defect.
  • Hand-started — if the module is started by a direct Mod.start_link/1 outside any supervisor, it is genuinely unsupervised, and we can name the exact call site. Reported at :medium (something depends on it) or :low, with the file:line of the start rather than "we couldn't see it".

Summary

Functions

analyze(modules, forest, edges)