Skip to main content

CHANGELOG.md

# Changelog

All notable changes to Firebreak are documented here. The format is based on
[Keep a Changelog](https://keepachangelog.com/), and the project follows
semantic versioning.

## [0.1.0]

Initial public release.

Firebreak builds two graphs from an Elixir/OTP project — the declared supervision
tree and the actual process-to-process coupling — and reports where coupling
crosses a supervision boundary the tree treats as "contained": a synchronous call
from one branch into another, which a restart turns into `:noproc`/`:timeout` for
the caller. No app boot, no LLM, CI-friendly.

### Analysis

- **Two-graph model.** The supervision *forest* is read the way OTP reads it — by
  calling each supervisor's `init/1` (child specs are runtime data; `init/1`
  returns them without starting anything) — with static AST parsing as the fallback
  for code it can't load. The *coupling graph* is always static: it resolves
  `GenServer.call`/`cast`, `:gen_server`/`:gen_statem`, registered names,
  `Registry`, `:global`, `Process.whereis`, `:ets`, `Phoenix.PubSub`, and `:pg` to
  the owning module, following wrapper functions transitively.
- **Synchronous vs async weighting.** Only a synchronous crossing makes a caller
  block on `:noproc`, so severities are gated on it — async-only coupling rates lower.
- **Confidence.** Findings are tagged `exact` (read via `init/1`) or `best-effort`
  (static), so you know which is which.

### Checks

- **Coupling / correctness:** `cross_tree_coupling`, `crash_cascade` (failure
  simulation over the restart closure), `cyclic_coupling`, `boot_order_cycle` (an
  in-`init/1` synchronous cycle means an unbootable tree), `missing_trap_exit`,
  `boot_order_dependency`, `start_link_in_callback`.
- **Blast radius / structural:** `one_for_all_blast_radius`,
  `supervisor_subtree_blast`, `dynamic_supervisor_restart_blast`,
  `orphaned_stateful_process`, `dynamic_supervisor_registry_race`,
  `lookup_or_create_race`, `unhandled_port_exit`,
  `shutdown_exceeds_intensity_window`, `default_restart_intensity`.

### Runtime observation (`--observe`)

Attach to a live node over distributed Erlang and fold its real shape into the
analysis: live `DynamicSupervisor` children join the forest, registered names are
recovered, `runtime_fanout` reports supervisors running more children than the
source models, and `runtime_mailbox_backlog` flags a deep mailbox on a
synchronously-called process. Reads use standard-library `:rpc` only — the target
needs nothing installed.

### Output formats (`mix firebreak --format`)

`text` (default; leads with primary findings, collapses advisories), `json`,
`dot`, `mermaid`, `github` (PR annotations), `html`, `model` (the supervision-model
IR), `score` (a structural risk score + per-supervisor ranking), `failure` (a
Mermaid diagram of the cross-tree failure modes), and `overlay` (static crossings
annotated with a live node's observed state; needs `--observe`).

### The model IR and its backends

`mix firebreak --format model` emits a **versioned, documented contract**
([`notes/model-ir-contract.md`](notes/model-ir-contract.md)) — every other artifact
is a pure function of it:

- **`mix firebreak.spec`** — a TLA+ lifecycle spec per supervisor (restart-intensity
  budget + escalation, and the cross-tree caller's permanent `:noproc`), runnable
  with TLC. `--lang quint` emits the same model as Quint.
- **`mix firebreak.lockstep`** — a lockstep regression-test scaffold per
  synchronous crossing.
- **`Firebreak.WhatIf`** — simulate a refactor (`move/3`, `set_strategy/3`) and diff
  the crossings before touching code.
- **`Firebreak.RiskScore`**, **`Firebreak.FailureViz`**, **`Firebreak.RuntimeOverlay`**
  — a risk score, a failure-mode diagram, and a live overlay, all from the IR.

### CI integration

- `--fail-on <severity>` gate; `--min-severity` filter.
- `--write-baseline` / `--baseline` to gate only on *new* findings; a
  `.firebreak.exs` allowlist for accepted findings.
- `--write-expected` / `--expect` topology conformance: snapshot the intended tree
  and report `topology_drift` (strategy flips, dropped children, intensity changes).
- A composite GitHub Action (`action.yml`).

### Implementation

Pure Elixir, zero runtime dependencies (hand-rolled JSON encoder; `ex_doc` is
dev-only).