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
+6 -2
View File
@@ -1205,13 +1205,17 @@ class FenrisTuiApp(App):
)
def _format_scenario(self, proj: ProjectionResult) -> str:
"""Format scenario range (spec §6.5)."""
if not proj.scenario_range or not proj.scenario_range.rates:
"""Format scenario range with horizon reasons (spec §6.5)."""
if not proj.scenario_range:
return ""
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))
for horizon, reason in sorted(proj.scenario_range.horizon_reasons.items()):
parts.append("%dd: %s" % (horizon, reason))
if not parts:
return ""
return "[bold]Scenario range[/bold] · %s" % " · ".join(parts)
# --- Actions ---