GET /stats reports the per-ecosystem breakdown
Part 4 of six splitting #381 ("analytics dashboard") into reviewable pieces, as the review there asked for. #381 stays open as the umbrella and reference thread until the last of these lands; it is not meant to be merged.
Series: #388 route coverage · #389 ecosystem stats · #390 analytics page · #391 /stats breakdown · #392 Grafana dashboard · #393 source attribution
Needs #389 and #390, and branches off #390, so until those merge the Files changed tab here shows all three. The last commit is this part.
/stats answered from two cheap counters. It now also carries downloaded_bytes, downloads and an ecosystems array, served from the same 60-second snapshot the gauges and the analytics page read, so a polling client does not re-run the aggregation per request.
stats_unavailable
Raised in the review of #381: when the aggregation fails with no retained snapshot, zeros and an empty array are indistinguishable from an idle proxy for anything consuming the JSON. The response now carries stats_unavailable: true in that case.
It is a new field with omitempty rather than pointer totals, so the field set stays stable for existing consumers and a healthy response is byte-identical to one without the flag. A retained snapshot is served on without the flag -- those figures are stale, not absent, and staleness is surfaced to a human on the page rather than to a machine here.
The comment above the call in #381 claimed the breakdown is omitted when there is no snapshot. That held for the array but not for the totals; it now says what the code does.
The endpoint keeps answering with the artifact count and cache size either way: a failing breakdown does not take down an endpoint that worked before it existed.
Tests cover all three states. There were none for the /stats shape before.