# Data flow

How a network check becomes a displayed result.

## Models

```
ProbeResult        Raw ping data for one host (RTTs, loss, jitter)
Issue              A single diagnosed problem from one check (kind, severity, description)
CheckResult        Output of one check cycle: timestamp + ProbeResults + Issues
IssueRecord        Aggregation of Issues across multiple CheckResults, grouped by kind
```

## Pipeline

```
 ┌─────────────────────────────────────────────────────────────┐
 │  Probes (checks/)                                          │
 │                                                            │
 │  gateway.run()  ──→  ProbeResult (1 host)                  │
 │  internet.run() ──→  list[ProbeResult] (multiple hosts)    │
 └──────────────────────────┬──────────────────────────────────┘
                            │
                            ▼
 ┌─────────────────────────────────────────────────────────────┐
 │  Diagnosis (diagnosis.py)                                  │
 │                                                            │
 │  analyze(gateway, internet) ──→  list[Issue]               │
 │                                                            │
 │  Compares probe results against thresholds (see metrics.md)│
 │  to produce issues like "gateway-loss", "isp-latency", etc.│
 └──────────────────────────┬──────────────────────────────────┘
                            │
                            ▼
 ┌─────────────────────────────────────────────────────────────┐
 │  Runner (runner.py)                                        │
 │                                                            │
 │  run_once() ──→  CheckResult                               │
 │                                                            │
 │  Bundles timestamp + ProbeResults + Issues into one result.│
 └──────────────────────────┬──────────────────────────────────┘
                            │
                            ▼
 ┌─────────────────────────────────────────────────────────────┐
 │  Aggregation (models.py)                                   │
 │                                                            │
 │  IssueRecord.from_results(list[CheckResult])               │
 │      ──→  list[IssueRecord]                                │
 │                                                            │
 │  Groups Issues by kind across all check results.           │
 │  Tracks count, last_seen, whether currently active,        │
 │  and last observed value.                                  │
 └──────────────────────────┬──────────────────────────────────┘
                            │
                            ▼
 ┌─────────────────────────────────────────────────────────────┐
 │  UI (ui/)                                                  │
 │                                                            │
 │  WatchState holds a rolling buffer of CheckResults.        │
 │  On each cycle:                                            │
 │    - Runs a check (run_once)                               │
 │    - Appends the CheckResult                               │
 │    - Renders dashboard:                                    │
 │        Status indicator (stable / unstable / problem)      │
 │        Issue history (from IssueRecords)                   │
 │        Probe summary (gateway + internet one-liners)       │
 │    - Sends desktop notification on healthy→problem change  │
 └─────────────────────────────────────────────────────────────┘
```
