Gunnar

mikronova/modgunn

.nornir/rollup-guide.md

# rollup — guide

> **Class:** `guide` · audience: someone summarizing findings at fleet altitude. The *what* is
> in [`rollup-design.md`](rollup-design.md).

## f0 — roll up a fleet scan

The common path — straight from a warehouse fleet scan to a fleet posture:

```rust
let findings = wh.scan_fleet()?;                             // Vec<FleetFinding>
let rollup = modgunn::rollup::rollup_fleet_findings(&findings);
println!("{} repos affected, worst = {:?}", rollup.affected_repos, rollup.severity.worst());
for (component, n) in &rollup.top_components { println!("{n:>3}  {component}"); }
```

## f1 — from your own rows (no warehouse)

```rust
use modgunn::rollup::{repo_health, fleet_rollup, FindingRow};
use modgunn::core::Severity;

let rows = vec![
    FindingRow { repo: "app-a".into(), component: "some-crate 1.2.3".into(), severity: Severity::High },
    // …
];
let per_repo = repo_health(&rows);      // Vec<RepoHealth>
let fleet    = fleet_rollup(&rows);     // FleetRollup
```

## f0 — a severity histogram / health score

```rust
use modgunn::rollup::SeverityHistogram;
let hist = SeverityHistogram::of(rows.iter().map(|r| r.severity));
println!("worst {:?}, health {:.2}", hist.worst(), hist.health_score());
```

## Gotchas

- `rollup` is pure and always compiled — it needs no feature and does no I/O. Feed it rows;
  where they come from (warehouse, a live scan, a test) is your call.
- `rollup_fleet_findings` is just `fleet_rollup` after `FindingRow::from_fleet` — use it when
  you already have `warehouse::FleetFinding`s.