Skip to content

Commit 00ac27b

Browse files
committed
feat(report): make data provenance explicit — window in banner, named USES header, per-row samples
Real-cluster feedback: the table showed a USES number with no indication of what it was (p95? peak? avg?) or how much data backed it. Now it's self-documenting: - banner: '... data: 0 (kubetidy operator) · observed over 48m' (the time span) - header: 'USES (cpu p95 · mem peak)' names exactly what the proposal is sized to - CONF column: '▒ low 31% · 31 smp' — the sample count behind each recommendation Legend trimmed accordingly; full avg/p95/p99/peak still under --explain.
1 parent 9d6ce26 commit 00ac27b

2 files changed

Lines changed: 26 additions & 5 deletions

File tree

internal/report/report.go

Lines changed: 25 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,9 @@ func writeBanner(b *strings.Builder, result model.ScanResult, opts Options) {
102102
ctx = "(no context)"
103103
}
104104
banner := fmt.Sprintf("kubetidy · %s · data: %s", ctx, result.Tier.String())
105+
if w := observedWindow(result); w > 0 {
106+
banner += fmt.Sprintf(" · observed over %s", formatWindow(w))
107+
}
105108
if opts.Color {
106109
fmt.Fprintf(b, "%s%s%s\n", ansiBold+ansiCyan, banner, ansiReset)
107110
} else {
@@ -162,7 +165,7 @@ func writeRecommendations(b *strings.Builder, recs []model.Recommendation, opts
162165
var buf strings.Builder
163166
tw := tabwriter.NewWriter(&buf, 0, 0, 2, ' ', 0)
164167
// Flushing to an in-memory strings.Builder cannot fail.
165-
_, _ = fmt.Fprintf(tw, " WORKLOAD\tREQUESTED\tUSES\tPROPOSED\tSAVE\tCONF\n")
168+
_, _ = fmt.Fprintf(tw, " WORKLOAD\tREQUESTED\tUSES (cpu p95 · mem peak)\tPROPOSED\tSAVE\tCONF (samples)\n")
166169
for _, rec := range recs {
167170
_, _ = fmt.Fprintf(tw, " %s\t%s\t%s\t%s\t%s\t%s\n",
168171
workloadLabel(rec),
@@ -195,8 +198,8 @@ func writeRecommendations(b *strings.Builder, recs []model.Recommendation, opts
195198
// writeLegend emits the short column/confidence legend footer.
196199
func writeLegend(b *strings.Builder, opts Options) {
197200
lines := []string{
198-
"columns are cpu/mem · USES = p95 cpu / peak mem (what PROPOSED is sized to) · SAVE = $/mo (↑ grow = reliability)",
199-
"CONF = confidence: ▒ low · ▓ med · █ high + score% — grows with data history (tier, window, samples).",
201+
"values are cpu/mem · USES (p95 cpu / peak mem) is what PROPOSED is sized to · SAVE = $/mo (↑ grow = reliability)",
202+
"CONF = confidence ▒ low · ▓ med · █ high (score% · samples) — grows with data history.",
200203
"→ run kubetidy scan --explain <workload> to see the full usage distribution (avg/p95/p99/peak).",
201204
}
202205
for _, l := range lines {
@@ -258,7 +261,25 @@ func confidenceCell(rec model.Recommendation) string {
258261
default:
259262
glyph, label = "▒", "low"
260263
}
261-
return fmt.Sprintf("%s %s %d%%", glyph, label, rec.Confidence.Percent())
264+
cell := fmt.Sprintf("%s %s %d%%", glyph, label, rec.Confidence.Percent())
265+
// Append the sample count backing this recommendation — the data provenance the confidence
266+
// is built on, so a "low" reads as "low, because only N samples so far".
267+
if n := rec.Usage.Samples; n > 0 {
268+
cell += fmt.Sprintf(" · %s smp", formatCount(n))
269+
}
270+
return cell
271+
}
272+
273+
// observedWindow returns the longest observation window across the recommendations — the
274+
// data's time span, shown in the banner. Zero (snapshot/static) means "no window".
275+
func observedWindow(result model.ScanResult) time.Duration {
276+
var longest time.Duration
277+
for _, r := range result.Recommendations {
278+
if r.Usage.Window > longest {
279+
longest = r.Usage.Window
280+
}
281+
}
282+
return longest
262283
}
263284

264285
// JSON renders a stable machine-readable schema of the scan result.

internal/report/report_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -267,7 +267,7 @@ func TestTableLegendShown(t *testing.T) {
267267
t.Fatalf("Table: %v", err)
268268
}
269269
out := buf.String()
270-
if !strings.Contains(out, "CONF = confidence:") {
270+
if !strings.Contains(out, "CONF = confidence") {
271271
t.Errorf("missing legend\n--- got ---\n%s", out)
272272
}
273273
}

0 commit comments

Comments
 (0)