From cc89107965bfc4586d2e9d6a3e16d70034a91640 Mon Sep 17 00:00:00 2001 From: Peli de Halleux Date: Wed, 29 Jul 2026 10:17:06 +0000 Subject: [PATCH] Improve forecast command output - Remove Engines and Triggers columns from the summary table - Only show detailed per-run samples table when specific workflows are requested - Compute TOTAL row even when NA values are present (skip NaN/Inf, sum Runs) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: f275d7e4-4d62-4ea3-8fb3-2bc485d0e2bf --- pkg/cli/forecast_render.go | 38 ++++++++++++++------------------------ pkg/cli/forecast_types.go | 2 -- 2 files changed, 14 insertions(+), 26 deletions(-) diff --git a/pkg/cli/forecast_render.go b/pkg/cli/forecast_render.go index 6965b998377..fe15f40f81e 100644 --- a/pkg/cli/forecast_render.go +++ b/pkg/cli/forecast_render.go @@ -5,7 +5,6 @@ import ( "fmt" "math" "os" - "strings" "github.com/github/gh-aw/pkg/console" "github.com/github/gh-aw/pkg/constants" @@ -37,6 +36,7 @@ func renderForecastTable(output ForecastResult, config ForecastConfig) error { anyUnreliable := false var totalWeeklyP50, totalMonthlyP50 float64 + var totalRuns int rows := make([]forecastTableRow, 0, len(output.Workflows)+1) for _, wf := range output.Workflows { unreliableMark := "" @@ -53,19 +53,23 @@ func renderForecastTable(output ForecastResult, config ForecastConfig) error { if mc := wf.MonthlyMonteCarlo; mc != nil { monthlyP50 = mc.P50ProjectedAIC } - totalWeeklyP50 += weeklyP50 - totalMonthlyP50 += monthlyP50 + // Skip NaN/Inf so a single unreliable projection can't poison the totals. + if !math.IsNaN(weeklyP50) && !math.IsInf(weeklyP50, 0) { + totalWeeklyP50 += weeklyP50 + } + if !math.IsNaN(monthlyP50) && !math.IsInf(monthlyP50, 0) { + totalMonthlyP50 += monthlyP50 + } + totalRuns += wf.SampledRuns row := forecastTableRow{ Workflow: wf.WorkflowID + unreliableMark, - Engines: formatEngineList(wf.Engines), Runs: wf.SampledRuns, P50PerRun: formatForecastAIC(wf.P50AIC), P95PerRun: formatForecastAIC(wf.P95AIC), WeeklyP50: formatForecastAIC(weeklyP50), MonthlyP50: formatForecastAIC(monthlyP50), SuccessRate: formatForecastPercent(wf.SuccessRate, wf.SampledRuns > 0), - Triggers: formatTriggerList(wf.ActiveTriggers), } rows = append(rows, row) } @@ -76,6 +80,7 @@ func renderForecastTable(output ForecastResult, config ForecastConfig) error { if len(output.Workflows) > 1 { rows = append(rows, forecastTableRow{ Workflow: "TOTAL", + Runs: totalRuns, WeeklyP50: formatForecastAIC(totalWeeklyP50), MonthlyP50: formatForecastAIC(totalMonthlyP50), }) @@ -84,8 +89,10 @@ func renderForecastTable(output ForecastResult, config ForecastConfig) error { fmt.Fprint(os.Stderr, console.RenderStruct(rows)) fmt.Fprintln(os.Stderr, "") - // Show detailed per-run samples section. - printRunSamplesSection(output.Workflows) + // Show detailed per-run samples section only when specific workflows were requested. + if len(config.WorkflowIDs) > 0 { + printRunSamplesSection(output.Workflows) + } // Show experiment variant details when present. for _, wf := range output.Workflows { @@ -245,13 +252,6 @@ func formatForecastAIC(value float64) string { return fmt.Sprintf("%.2fM", value/1_000_000) } -func formatEngineList(engines []string) string { - if len(engines) == 0 { - return "-" - } - return strings.Join(engines, ", ") -} - // formatForecastSignedAIC formats a signed AIC value, preserving // the sign so callers can display positive/negative deltas (e.g., error abs). func formatForecastSignedAIC(value float64) string { @@ -270,13 +270,3 @@ func formatForecastSignedAIC(value float64) string { func roundForecastAIC(value float64) float64 { return math.Round(value*1000) / 1000 } - -func formatTriggerList(triggers []string) string { - if len(triggers) == 0 { - return "-" - } - if len(triggers) <= 3 { - return strings.Join(triggers, ", ") - } - return strings.Join(triggers[:3], ", ") + fmt.Sprintf(" +%d", len(triggers)-3) -} diff --git a/pkg/cli/forecast_types.go b/pkg/cli/forecast_types.go index 83e839853a5..ec5ba96800f 100644 --- a/pkg/cli/forecast_types.go +++ b/pkg/cli/forecast_types.go @@ -144,12 +144,10 @@ type workflowMeta struct { // forecastTableRow is a flattened struct used for console table rendering. type forecastTableRow struct { Workflow string `json:"workflow" console:"header:Workflow"` - Engines string `json:"engines" console:"header:Engines"` Runs int `json:"runs" console:"header:Runs"` P50PerRun string `json:"p50_per_run" console:"header:P50 AIC/Run"` P95PerRun string `json:"p95_per_run" console:"header:P95 AIC/Run"` WeeklyP50 string `json:"weekly_p50" console:"header:Weekly AIC (P50)"` MonthlyP50 string `json:"monthly_p50" console:"header:Monthly AIC (P50)"` SuccessRate string `json:"success_rate" console:"header:Success Rate"` - Triggers string `json:"triggers" console:"header:Triggers"` }