feat(#76): anchor scenario windows at evidence endpoint T

Headline and scenario rates now describe exact evidence-supported
monitored spans anchored at the latest published usage-evidence
endpoint T, not clock_now. Reader refresh alone never moves T or
dilutes rates.

- Add horizon_reasons field to ScenarioRange for specific unavailability facts
- Modify _compute_horizon_rate to use exact trailing 7/28/90×86400-second starts from T
- Show specific reasons for affected horizons (e.g., "starts before earliest data")
- Update TUI and CLI to display horizon-specific reasons
- Add 6 new tests for evidence-anchored projection rates

Co-authored-by: CommandCodeBot <noreply@commandcode.ai>
This commit is contained in:
xavierk
2026-09-14 11:11:50 +05:30
co-authored by CommandCodeBot
parent 5d916ee97f
commit 4f2f30abac
4 changed files with 176 additions and 18 deletions
+7 -4
View File
@@ -386,14 +386,17 @@ def _format_projection(proj, freshness: str, service: Dict[str, Any],
lines.append("%s evidence" % state_label)
lines.append("")
# --- Scenario range (§6.5) ---
if proj.scenario_range and proj.scenario_range.rates:
# --- Scenario range (§6.5) with horizon reasons ---
if proj.scenario_range:
parts = []
for horizon in sorted(proj.scenario_range.rates.keys()):
rate_gb_day = proj.scenario_range.rates[horizon] * 86400 / 1e9
parts.append("%dd: %.2f GB/day" % (horizon, rate_gb_day))
lines.append("scenario range: %s" % " · ".join(parts))
lines.append("")
for horizon, reason in sorted(proj.scenario_range.horizon_reasons.items()):
parts.append("%dd: %s" % (horizon, reason))
if parts:
lines.append("scenario range: %s" % " · ".join(parts))
lines.append("")
# --- PU context line (§6.1) ---
lines.append(proj.pu_context_line)