# `Lei.RiskProfile`
[🔗](https://github.com/kitplummer/lowendinsight/blob/main/lib/lei/risk_profile.ex#L1)

The distribution a report's verdict was collapsed from (#247).

`data.risk` is the worst of a repository's metrics. That makes two very
different repositories read identically:

    one functional contributor, active this month   -> "critical"
    one functional contributor, silent two years    -> "critical"

The first is a bus-factor bet on a project someone is working on. The second
is an abandoned dependency. `max/1` is the operation that loses the
difference, and it loses it after every threshold has already been applied,
so no amount of retuning reaches it.

Nor does a level above `critical`: the scale saturates exactly where
compounding matters most, and a fifth level would break every consumer of the
enum -- `scripts/canary.sh`, `scripts/library-isolation.sh`, the SARIF
mapping, and any customer gate.

So the verdict is left alone and the distribution reported beside it. No
weights, no 0-100 score, no normalisation: a weighted score needs weights,
and deciding whether bus factor outranks currency is a judgement there is no
evidence for. Counts need none, and they are enough to order 200 `critical`
dependencies by how much is wrong with each.

# `of`

```elixir
@spec of(map()) :: map()
```

Builds the profile from a results map.

Works on atom-keyed results as the analyzer holds them and string-keyed ones
as they come back from JSON, because it matches on the *values* -- the risk
words themselves -- rather than on which keys are supposed to carry them.
That also means a field whose value is not a risk level is skipped rather
than counted: `agentic_classification` reads "human", "mixed" or "agent",
and is not a verdict about risk.

Metric names in `elevated` are returned as strings and sorted, so the field
is stable between two reports of the same repository.

# `rank`

```elixir
@spec rank(map()) :: non_neg_integer()
```

A sort key for "what should I look at first", higher being worse.

A customer whose tree comes back half `critical` cannot act on the verdict:
measured across a 99-repository npm sample, 54% were `critical` and not one
was `low`. The word stops discriminating at that density, which is what this
orders through -- a repository with three critical metrics outranks one with
a single critical metric, though both read `critical`.

Lexicographic on the counts: criticals first, then highs, then mediums. The
base is 100 rather than 10 because a report can carry more than nine metrics
and a carry would silently make three highs outrank one critical.

Comparable within a report, not across them: it says which of these is worse,
not how bad any of them is. A report with fewer metrics computed scores lower
for that reason alone, so it is a ranking and never a score.

---

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