Gunnar

mikronova/modgunn

.nornir/rollup-design.md

# rollup — design

> **Class:** `design` · **Function axis:** `f0` the folds · `f1` per-repo health · `f2` fleet
> rollup. Strict: only the shape we run. The system view is in [`design.md`](design.md).

## f0 — the folds

`rollup` (module `rollup.rs`, **always** compiled — no feature) is pure higher-level folds over
scan + advisory data, so a consumer can see modgunn at altitude. No warehouse, no network; the
`warehouse` layer maps its `FleetFinding`s in.

- [`SeverityHistogram::of(severities)`](../src/rollup.rs) (`rollup.rs:34`) — counts
  info/low/medium/high/critical + total; [`worst()`](../src/rollup.rs) (`rollup.rs:51`) is the
  top severity, [`health_score()`](../src/rollup.rs) (`rollup.rs:70`) a 0–1 health number.
- [`tally(items)`](../src/rollup.rs) (`rollup.rs:83`) — a generic frequency fold `→ Vec<(K,
  count)>`.

**Reuse:** `rollup.uses = [core, warehouse]` — but only to *map in* `FleetFinding`s; the folds
themselves are pure and take plain input, so `rollup` works with or without the warehouse.

## f1 — per-repo health

[`FindingRow`](../src/rollup.rs) (`rollup.rs:102`) is the pure input `{ repo, component,
severity }`; [`repo_health(findings)`](../src/rollup.rs) (`rollup.rs:128`) folds rows into
[`RepoHealth`](../src/rollup.rs) (`rollup.rs:112`) `{ repo, vulnerable_components, findings,
critical, high, worst, score }` per repo.

## f2 — fleet rollup

[`fleet_rollup(findings)`](../src/rollup.rs) (`rollup.rs:170`) produces
[`FleetRollup`](../src/rollup.rs) (`rollup.rs:159`) `{ severity, repos, top_components,
affected_repos }` — the whole-fleet posture. The bridge from the warehouse layer is
[`FindingRow::from_fleet`](../src/rollup.rs) (`rollup.rs:184`) and
[`rollup_fleet_findings(&[FleetFinding])`](../src/rollup.rs) (`rollup.rs:191`), which maps
`warehouse::FleetFinding`s straight into a `FleetRollup`.