diff --git a/frontend/packages/agent/src/stats.html b/frontend/packages/agent/src/stats.html index 271f8650..0709130f 100644 --- a/frontend/packages/agent/src/stats.html +++ b/frontend/packages/agent/src/stats.html @@ -23,6 +23,7 @@ +
diff --git a/frontend/packages/dashboard/src/stats.html b/frontend/packages/dashboard/src/stats.html index 88182a1c..97ac3954 100644 --- a/frontend/packages/dashboard/src/stats.html +++ b/frontend/packages/dashboard/src/stats.html @@ -34,6 +34,7 @@ + diff --git a/hive-ag3nt/src/stats.rs b/hive-ag3nt/src/stats.rs index d7c3515d..80298c04 100644 --- a/hive-ag3nt/src/stats.rs +++ b/hive-ag3nt/src/stats.rs @@ -26,6 +26,10 @@ pub enum Window { ThreeDay, Week, Month, + /// All available data: the range starts at the earliest recorded turn + /// (`MIN(started_at)`) rather than a fixed lookback, with an adaptive + /// bucket width so the trend series stays bounded at any span. + All, } impl Window { @@ -37,6 +41,7 @@ impl Window { "3d" => Self::ThreeDay, "7d" => Self::Week, "30d" => Self::Month, + "all" => Self::All, _ => Self::Day, } } @@ -49,6 +54,7 @@ impl Window { Self::ThreeDay => "3d", Self::Week => "7d", Self::Month => "30d", + Self::All => "all", } } @@ -61,21 +67,42 @@ impl Window { Self::ThreeDay => 3 * 24 * 3600, Self::Week => 7 * 24 * 3600, Self::Month => 30 * 24 * 3600, + // `All` has no fixed lookback — its range is computed from + // `MIN(started_at)` in `snapshot()`. 0 is only a safe fallback + // (→ `from == now` → empty range) if ever reached generically. + Self::All => 0, } } fn bucket_secs(self) -> i64 { match self { // 5-min buckets for 1h (12 buckets), 15-min for 4h (16 buckets), - // hourly for 24h + 3d, daily for 7d + 30d. + // hourly for 24h + 3d, daily for 7d + 30d. `All` shares the daily + // fallback, but its real bucket width is chosen adaptively in + // `snapshot()` via `adaptive_bucket_secs` — this arm is only + // reached defensively. Self::Hour => 300, Self::FourHour => 900, Self::Day | Self::ThreeDay => 3600, - Self::Week | Self::Month => 24 * 3600, + Self::Week | Self::Month | Self::All => 24 * 3600, } } } +/// Bucket width for the unbounded `all` window, laddered by the actual +/// span so the trend series stays bounded (≈ ≤100 buckets) at any range: +/// hourly ≤ 2d, daily ≤ 90d, weekly ≤ 2y, ~monthly (30d) beyond. +fn adaptive_bucket_secs(span_secs: i64) -> i64 { + const HOUR: i64 = 3600; + const DAY: i64 = 24 * HOUR; + match span_secs { + s if s <= 2 * DAY => HOUR, + s if s <= 90 * DAY => DAY, + s if s <= 730 * DAY => 7 * DAY, + _ => 30 * DAY, + } +} + #[derive(Debug, Serialize)] pub struct Snapshot { pub window: &'static str, @@ -208,8 +235,20 @@ fn snapshot(path: &Path, window: Window) -> Result