# `MobDev.OtpAudit`
[🔗](https://github.com/genericjam/mob_dev/blob/main/lib/mob_dev/otp_audit.ex#L1)

Reachability analysis for the bundled OTP runtime tree of a Mob app.

Walks every `.beam` file under an OTP root, extracts the `imports` chunk
to learn who calls whom, and computes the transitive closure starting
from the app's entry-point modules. Anything not reachable is a strip
candidate — modules, whole libs, or duplicate library versions.

Used by `mix mob.audit_otp` (read-only report) and the planned
`mix mob.release --slim` flag (auto-strip based on the report).

## Entry points

Reachability seeding is deliberately generous:

  * App's `start/2` callback module (read from each `.app` file's `mod` key)
  * All exported functions of `kernel` and `stdlib` (BEAM startup needs them)
  * `elixir`, `logger`, `eex` (runtime support that gets called via macros)

Anything reachable from those is kept; the rest is candidate-for-strip.

## Output

Returns a map with:

  * `:libs` — every lib found, with reachable/total module counts and KB
  * `:duplicates` — libs that appear multiple times (only newest is kept)
  * `:foreign_apps` — non-OTP, non-app code in the lib dir (other projects)
  * `:strippable_libs` — libs with zero reachable modules
  * `:total_kb` / `:reachable_kb` / `:strippable_kb` — size summary

All sizes are post-strip — i.e. what `mix mob.release` actually ships,
not raw OTP source.

# `lib_name`

```elixir
@type lib_name() :: String.t()
```

# `lib_report`

```elixir
@type lib_report() :: %{
  name: lib_name(),
  version: String.t() | nil,
  path: String.t(),
  modules_total: non_neg_integer(),
  modules_reachable: non_neg_integer(),
  kb_total: non_neg_integer(),
  kb_reachable: non_neg_integer(),
  unreachable_modules: [module_atom()],
  is_app_under_test?: boolean()
}
```

# `module_atom`

```elixir
@type module_atom() :: atom()
```

# `report`

```elixir
@type report() :: %{
  otp_root: String.t(),
  app_name: String.t() | nil,
  libs: [lib_report()],
  duplicates: %{required(lib_name()) =&gt; [String.t()]},
  foreign_apps: [String.t()],
  strippable_libs: [lib_name()],
  total_kb: non_neg_integer(),
  reachable_kb: non_neg_integer(),
  strippable_kb: non_neg_integer()
}
```

# `audit`

```elixir
@spec audit(
  String.t(),
  keyword()
) :: report()
```

Run the audit. `otp_root` is the directory containing `lib/` and `erts-*/`
(typically the runtime tree extracted into the app bundle, or the cache
the release packaging copies from).

`:app_name` (option) — the application's atom name (e.g. `:air_cart_max`).
Used to seed reachability from the app's modules. If omitted, every lib
with no `mod` callback is treated as a potential entry point (broader,
finds less to strip).

---

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