package db import ( "fmt" "strings" ) // FailureAtlasEntry is one tier's aggregated failure rate for a fingerprint bucket. type FailureAtlasEntry struct { Tier string Total int Failures int FailPct float64 } // FailureAtlasSummary returns a compact prosecutor-ready summary from tier_outcomes. func (d *Database) FailureAtlasSummary(fingerprintKey, goos string) (string, error) { entries, err := d.failureAtlasEntries(fingerprintKey) if err != nil { return "", err } if len(entries) == 0 && goos != "" { entries, err = d.failureAtlasEntriesLike(goos + "|%") if err != nil { return "", err } } if len(entries) == 0 { return "no fleet failure atlas samples for this fingerprint", nil } parts := make([]string, 0, len(entries)) for _, e := range entries { if e.Failures == 0 { continue } parts = append(parts, fmt.Sprintf("%s %d/%d failed (%.0f%%)", e.Tier, e.Failures, e.Total, e.FailPct)) } if len(parts) == 0 { return "fleet atlas shows no tier failures for this fingerprint", nil } return strings.Join(parts, "; "), nil } func (d *Database) failureAtlasEntries(fingerprintKey string) ([]FailureAtlasEntry, error) { return d.queryFailureAtlas(`fingerprint_key = ?`, fingerprintKey) } func (d *Database) failureAtlasEntriesLike(pattern string) ([]FailureAtlasEntry, error) { return d.queryFailureAtlas(`fingerprint_key LIKE ?`, pattern) } func (d *Database) queryFailureAtlas(whereClause string, arg interface{}) ([]FailureAtlasEntry, error) { if d == nil { return nil, nil } query := fmt.Sprintf(` SELECT tier, COUNT(*) AS total, SUM(CASE WHEN ok = 0 THEN 1 ELSE 0 END) AS failures FROM tier_outcomes WHERE %s GROUP BY tier HAVING failures > 0 ORDER BY failures DESC, total DESC LIMIT 12`, whereClause) rows, err := d.Query(query, arg) if err != nil { return nil, err } defer rows.Close() var out []FailureAtlasEntry for rows.Next() { var e FailureAtlasEntry if err := rows.Scan(&e.Tier, &e.Total, &e.Failures); err != nil { return nil, err } if e.Total > 0 { e.FailPct = 100 * float64(e.Failures) / float64(e.Total) } out = append(out, e) } return out, rows.Err() }