# `MobDev.Plugin.Report`
[🔗](https://github.com/genericjam/mob_dev/blob/master/lib/mob_dev/plugin/report.ex#L1)

Pure transforms behind `mix mob.plugins`: turn discovered deps + the
activation list into report rows, and render them as a table.

Kept separate from the Mix task (which does the filesystem/config I/O) so
the classification and formatting are unit-testable without a project on disk.

# `dep`

```elixir
@type dep() :: {atom(), map() | nil | {:unverified, term()}}
```

An app and its manifest: the loaded map, `nil` for no manifest (tier 0), or
`{:unverified, reason}` for a manifest that was not loaded because its
signature check failed (`MobDev.Plugin.Verify.load_verified/2`).

# `row`

```elixir
@type row() :: %{
  name: atom(),
  tier: 0..4 | nil,
  hot_pushable: true | false | :partial | nil,
  status: :activated | :installed,
  manifest?: boolean(),
  unverified: term() | nil,
  description: String.t() | nil,
  vetting: vetting() | nil
}
```

One row of `mix mob.plugins` output. `unverified` is the verification
failure for a manifest that was refused; tier and hot-push are then unknown
(`nil`), since the manifest is never evaluated.

# `vetting`

```elixir
@type vetting() :: %{
  audit: %{
    high: non_neg_integer(),
    medium: non_neg_integer(),
    low: non_neg_integer()
  },
  capability_errors: non_neg_integer()
}
```

Vetting summary attached to a row by `with_vetting/2`.

# `render`

```elixir
@spec render([row()]) :: String.t()
```

Renders rows as a human-readable table (or a friendly empty message).

# `render_audit`

```elixir
@spec render_audit([map()]) :: String.t()
```

Renders the output of `MobDev.Plugin.Audit.audit_plugin/2` (one or more)
for `mix mob.audit_plugins`. Pure.

`reports` is a list of `MobDev.Plugin.Audit.report()` maps. Output is
ordered by plugin name; findings within a plugin are already sorted by
severity → file → line by `audit_plugin/2`. A trailing summary line tallies
every severity across all reports.

# `rows`

```elixir
@spec rows([dep()], [atom()]) :: [row()]
```

Builds sorted report rows from all deps and the activated-plugin list.

A dep is a plugin row if it ships a manifest (loaded or refused) *or* is
named in `config :mob, :plugins`. A tier-0 plugin (no manifest) therefore
only appears once activated — otherwise it's indistinguishable from an
ordinary library dependency. Status is `:activated` when in the list, else
`:installed` (present in deps, not yet activated).

# `with_vetting`

```elixir
@spec with_vetting([row()], %{required(atom()) =&gt; Path.t()}) :: [row()]
```

Adds a `:vetting` summary to each activated row by running the static-analysis
audit (`MobDev.Plugin.Audit.audit_plugin/2`) and the capability checks
(`MobDev.Plugin.Validator.activated_capability_errors/1`) over the plugin's
source tree.

`dep_dirs` is a map of `plugin_name => plugin_dir` (absolute path). Rows
whose plugin is not in `dep_dirs` (or which are `:installed` only — not
activated) get `vetting: nil` and render as a dash. This function performs
IO; pure rendering stays in `render/1`.

---

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