Firebreak.RuntimeOverlay (Firebreak v0.1.0)

Copy Markdown View Source

Join the static model IR against a live node's observed reality.

The model IR (Firebreak.Model) is a declared projection — it says "Worker calls synchronously into Cache, and that crosses a supervision boundary." --observe reads what is actually running. This backend pairs the two: for every synchronous cross-tree crossing in the IR, it annotates the target with its live state — is it alive, how deep is its mailbox, how many instances are running — answering the question neither view answers alone:

of my static cross-tree crossings, which targets are *hot right now*?

This is a projection, not a new judgement. The judgements about runtime reality already live in findings: runtime_fanout (a supervisor running more children than static modelled) and runtime_mailbox_backlog (a deep mailbox on a synchronously-called process). The overlay is the structured ground-truth layer those findings are derived from, exposed in IR shape so the same consumers that read --format model can read the live annotation too.

Everything here carries :exact confidence — it is observed, not inferred. build/1 returns :no_runtime for an analysis produced without --observe (there is nothing to overlay), so callers can present a clear "pass --observe" notice rather than an empty artifact.

analysis = Firebreak.analyze("my_app", observe: "my_app@host")
Firebreak.RuntimeOverlay.build(analysis)
#=> %{node: :my_app@host, crossings: [%{from: Worker, to: Cache,
#=>   target_alive: true, target_mailbox: 1400, target_instances: 1}, ...], ...}

Summary

Functions

Build the runtime overlay for analysis, or :no_runtime if it was produced without --observe (no live reading to join against).

The overlay as a JSON string (key order preserved). For an analysis with no live reading, emits a small object explaining that --observe is required, rather than failing — so --format overlay is always well-formed.

The overlay schema version (bumped on a breaking shape change).

Types

crossing_overlay()

@type crossing_overlay() :: %{
  from: module(),
  to: module(),
  in_init: boolean(),
  target_alive: boolean(),
  target_mailbox: non_neg_integer() | nil,
  target_instances: pos_integer() | nil
}

t()

@type t() :: %{
  schema_version: pos_integer(),
  node: node(),
  live_processes: non_neg_integer(),
  supervisors: [map()],
  crossings: [crossing_overlay()],
  live_names: %{optional(atom()) => module()}
}

Functions

build(a)

@spec build(Firebreak.Analysis.t()) :: t() | :no_runtime

Build the runtime overlay for analysis, or :no_runtime if it was produced without --observe (no live reading to join against).

json(a)

@spec json(Firebreak.Analysis.t()) :: String.t()

The overlay as a JSON string (key order preserved). For an analysis with no live reading, emits a small object explaining that --observe is required, rather than failing — so --format overlay is always well-formed.

schema_version()

@spec schema_version() :: pos_integer()

The overlay schema version (bumped on a breaking shape change).