diff --git a/internal/briefing/daily.go b/internal/briefing/daily.go index fcdf21e..784f5ac 100644 --- a/internal/briefing/daily.go +++ b/internal/briefing/daily.go @@ -176,9 +176,6 @@ func readinessNotes(daypart forecast.DaypartSummary) []string { if daypart.PeakWindGust != nil && daypart.PeakWindGust.Value >= 30 { notes = append(notes, fmt.Sprintf("Morning gusts may reach %.0f mph.", daypart.PeakWindGust.Value)) } - if daypart.Indicators.Thunder { - notes = append(notes, "Morning thunder could affect departure timing.") - } if daypart.Indicators.Snow || daypart.Indicators.Ice { notes = append(notes, "Morning wintry weather could affect surfaces and travel.") } @@ -203,9 +200,6 @@ func concernNotes(daypart forecast.DaypartSummary) []string { if daypart.PeakWindGust != nil && daypart.PeakWindGust.Value >= 30 { notes = append(notes, fmt.Sprintf("%s gusts may reach %.0f mph.", prefix, daypart.PeakWindGust.Value)) } - if daypart.Indicators.Thunder { - notes = append(notes, prefix+" thunder may disrupt outdoor plans.") - } if daypart.Indicators.Snow || daypart.Indicators.Ice { notes = append(notes, prefix+" wintry weather may affect travel.") } @@ -229,9 +223,6 @@ func overnightWatchNotes(daypart forecast.DaypartSummary) []string { if daypart.PeakWindGust != nil && daypart.PeakWindGust.Value >= 30 { notes = append(notes, fmt.Sprintf("Overnight gusts may reach %.0f mph before morning plans begin.", daypart.PeakWindGust.Value)) } - if daypart.Indicators.Thunder { - notes = append(notes, "Overnight storms could change morning impacts.") - } if daypart.Indicators.Snow || daypart.Indicators.Ice { notes = append(notes, "Overnight wintry weather could leave morning travel impacts.") } @@ -293,10 +284,6 @@ func scoreOutdoorWindow(daypart forecast.DaypartSummary) OutdoorWindow { score += float64(len(daypart.AlertOverlaps)) * 100 reasons = append(reasons, "alert overlap") } - if daypart.Indicators.Thunder { - score += 75 - reasons = append(reasons, "thunder risk") - } if daypart.Indicators.Heat || daypart.Indicators.Cold { score += 25 if daypart.Indicators.Heat { @@ -334,9 +321,6 @@ func bottomLineText(conditions []string, hazards []string) string { func hazardsForIndicators(indicators forecast.Indicators) []string { var hazards []string - if indicators.Thunder { - hazards = append(hazards, "thunder") - } if indicators.Snow { hazards = append(hazards, "snow") } diff --git a/internal/briefing/daily_test.go b/internal/briefing/daily_test.go index 406e81b..5d25ae5 100644 --- a/internal/briefing/daily_test.go +++ b/internal/briefing/daily_test.go @@ -163,7 +163,7 @@ func TestTomorrowBriefingIncludesPlanningInputs(t *testing.T) { Value: wind, Time: mustParse("2026-05-30T09:00:00-05:00"), }, - Indicators: forecast.Indicators{Thunder: true}, + Indicators: forecast.Indicators{Snow: true}, }, }, } diff --git a/internal/briefing/storm.go b/internal/briefing/storm.go index c02b967..7485ea7 100644 --- a/internal/briefing/storm.go +++ b/internal/briefing/storm.go @@ -139,9 +139,6 @@ func reasonableWorstCase(alerts []forecast.AlertOverlap, summary forecast.Daypar items = appendUnique(items, "Alert scenario to consider: "+label+".") } } - if summary.Indicators.Thunder { - items = appendUnique(items, "Thunderstorm timing or intensity could be more disruptive than the baseline forecast.") - } if summary.Indicators.Wind { items = appendUnique(items, "Wind impacts could be higher where stronger gusts occur.") } diff --git a/internal/briefing/three_day_test.go b/internal/briefing/three_day_test.go index 9a75e9b..3d24eb4 100644 --- a/internal/briefing/three_day_test.go +++ b/internal/briefing/three_day_test.go @@ -39,7 +39,7 @@ func TestThreeDayBriefingBuildsOutlookDays(t *testing.T) { Value: gust, Time: mustParse("2026-05-29T10:00:00-05:00"), }, - Indicators: forecast.Indicators{Thunder: true, Wind: true}, + Indicators: forecast.Indicators{Wind: true}, }, }, AlertOverlaps: []forecast.AlertOverlap{{Event: "Flood Watch"}}, @@ -74,7 +74,7 @@ func TestThreeDayBriefingBuildsOutlookDays(t *testing.T) { t.Fatalf("Days length = %d, want 2", len(pkg.ThreeDay.Days)) } first := pkg.ThreeDay.Days[0] - if !strings.Contains(first.OverallCharacter, "Showers") || !strings.Contains(strings.Join(first.Risks, ","), "thunder") { + if !strings.Contains(first.OverallCharacter, "Showers") || !strings.Contains(strings.Join(first.Risks, ","), "wind") { t.Fatalf("first day = %#v, want conditions and risks", first) } if len(pkg.ThreeDay.RelevantAlerts) != 1 { diff --git a/internal/briefing/weekend.go b/internal/briefing/weekend.go index 5004548..8a41463 100644 --- a/internal/briefing/weekend.go +++ b/internal/briefing/weekend.go @@ -96,9 +96,6 @@ func weekendRainStormNotes(date string, daypart forecast.DaypartSummary) []strin if daypart.MaxPrecipitationProbability != nil && daypart.MaxPrecipitationProbability.Value >= 30 { notes = append(notes, fmt.Sprintf("%s precipitation chance peaks near %.0f%%.", label, daypart.MaxPrecipitationProbability.Value)) } - if daypart.Indicators.Thunder { - notes = append(notes, label+" thunder risk is present.") - } return notes } diff --git a/internal/briefing/weekend_test.go b/internal/briefing/weekend_test.go index 7bd1f76..4b2b6ee 100644 --- a/internal/briefing/weekend_test.go +++ b/internal/briefing/weekend_test.go @@ -43,7 +43,7 @@ func TestWeekendBriefingBuildsPlanningInputs(t *testing.T) { Value: gust, Time: mustParse("2026-05-30T16:00:00-05:00"), }, - Indicators: forecast.Indicators{Thunder: true, Wind: true}, + Indicators: forecast.Indicators{Wind: true}, HourlyPeriods: []forecast.ForecastPeriod{ { StartTime: mustParse("2026-05-30T15:00:00-05:00"), @@ -79,8 +79,8 @@ func TestWeekendBriefingBuildsPlanningInputs(t *testing.T) { if len(pkg.Weekend.Planning.WorstWeatherWindows) == 0 { t.Fatalf("WorstWeatherWindows = %#v, want weather window", pkg.Weekend.Planning.WorstWeatherWindows) } - if !strings.Contains(strings.Join(pkg.Weekend.Planning.RainStormTiming, " "), "thunder") { - t.Fatalf("RainStormTiming = %#v, want thunder timing", pkg.Weekend.Planning.RainStormTiming) + if !strings.Contains(strings.Join(pkg.Weekend.Planning.RainStormTiming, " "), "precipitation") { + t.Fatalf("RainStormTiming = %#v, want precipitation timing", pkg.Weekend.Planning.RainStormTiming) } if len(pkg.Weekend.Planning.UncertaintyInputs) == 0 { t.Fatal("UncertaintyInputs length = 0, want discussion context") diff --git a/internal/changes/daily.go b/internal/changes/daily.go index a1732da..7ba3843 100644 --- a/internal/changes/daily.go +++ b/internal/changes/daily.go @@ -126,7 +126,6 @@ func compareIndicators(previous forecast.Indicators, current forecast.Indicators previous bool current bool }{ - {name: "thunder", previous: previous.Thunder, current: current.Thunder}, {name: "snow", previous: previous.Snow, current: current.Snow}, {name: "ice", previous: previous.Ice, current: current.Ice}, } { @@ -146,7 +145,6 @@ func compareIndicators(previous forecast.Indicators, current forecast.Indicators func aggregateIndicators(dayparts []forecast.DaypartSummary) forecast.Indicators { out := forecast.Indicators{} for _, daypart := range dayparts { - out.Thunder = out.Thunder || daypart.Indicators.Thunder out.Snow = out.Snow || daypart.Indicators.Snow out.Ice = out.Ice || daypart.Indicators.Ice } diff --git a/internal/changes/daily_test.go b/internal/changes/daily_test.go index 416c91a..89585ad 100644 --- a/internal/changes/daily_test.go +++ b/internal/changes/daily_test.go @@ -62,14 +62,14 @@ func TestCompareDailyAlertAddedAndRemoved(t *testing.T) { func TestCompareDailyIndicatorChange(t *testing.T) { previous := dailyBriefing(60, 70, 10, at("2026-05-29T08:00:00Z"), nil, forecast.Indicators{}) - current := dailyBriefing(60, 70, 10, at("2026-05-29T08:00:00Z"), nil, forecast.Indicators{Thunder: true}) + current := dailyBriefing(60, 70, 10, at("2026-05-29T08:00:00Z"), nil, forecast.Indicators{Snow: true}) changes, err := CompareDaily(previous, current, testThresholds()) if err != nil { t.Fatalf("CompareDaily() error = %v", err) } - if countType(changes, "thunder_risk_change") != 1 { - t.Fatalf("changes = %#v, want thunder risk change", changes) + if countType(changes, "snow_risk_change") != 1 { + t.Fatalf("changes = %#v, want snow risk change", changes) } } diff --git a/internal/changes/three_day_test.go b/internal/changes/three_day_test.go index 9931c88..88eacef 100644 --- a/internal/changes/three_day_test.go +++ b/internal/changes/three_day_test.go @@ -34,7 +34,7 @@ func TestCompareThreeDayDetectsDayChanges(t *testing.T) { Value: currentPrecip, Time: time.Date(2026, 5, 29, 12, 0, 0, 0, time.UTC), }, - Dayparts: []forecast.DaypartSummary{{Indicators: forecast.Indicators{Thunder: true}}}, + Dayparts: []forecast.DaypartSummary{{Indicators: forecast.Indicators{Snow: true}}}, }}}, } @@ -51,16 +51,16 @@ func TestCompareThreeDayDetectsDayChanges(t *testing.T) { t.Fatal("changes length = 0, want detected 3-day changes") } var foundPrecip bool - var foundThunder bool + var foundSnow bool for _, change := range changes { if change.Type == "outlook_precip_probability_change" { foundPrecip = true } - if change.Type == "outlook_thunder_risk_change" { - foundThunder = true + if change.Type == "outlook_snow_risk_change" { + foundSnow = true } } - if !foundPrecip || !foundThunder { - t.Fatalf("changes = %#v, want precipitation and thunder changes", changes) + if !foundPrecip || !foundSnow { + t.Fatalf("changes = %#v, want precipitation and snow changes", changes) } } diff --git a/internal/changes/weekend_test.go b/internal/changes/weekend_test.go index 93bda81..3a2977f 100644 --- a/internal/changes/weekend_test.go +++ b/internal/changes/weekend_test.go @@ -23,7 +23,7 @@ func TestCompareWeekendDetectsOutlookChanges(t *testing.T) { Weekend: &briefing.Weekend{Days: []briefing.OutlookDay{{ Date: "2026-05-30", Temperature: forecast.Range{Max: ¤tTemp}, - Dayparts: []forecast.DaypartSummary{{Indicators: forecast.Indicators{Thunder: true}}}, + Dayparts: []forecast.DaypartSummary{{Indicators: forecast.Indicators{Snow: true}}}, }}}, } @@ -35,9 +35,9 @@ func TestCompareWeekendDetectsOutlookChanges(t *testing.T) { t.Fatal("changes length = 0, want weekend changes") } for _, change := range changes { - if change.Type == "weekend_outlook_thunder_risk_change" { + if change.Type == "weekend_outlook_snow_risk_change" { return } } - t.Fatalf("changes = %#v, want thunder risk change", changes) + t.Fatalf("changes = %#v, want snow risk change", changes) } diff --git a/internal/forecast/derive.go b/internal/forecast/derive.go index 9a48d82..a37ebda 100644 --- a/internal/forecast/derive.go +++ b/internal/forecast/derive.go @@ -47,13 +47,12 @@ type TimedValue struct { } type Indicators struct { - Thunder bool `json:"thunder,omitempty"` - Snow bool `json:"snow,omitempty"` - Ice bool `json:"ice,omitempty"` - Fog bool `json:"fog,omitempty"` - Heat bool `json:"heat,omitempty"` - Cold bool `json:"cold,omitempty"` - Wind bool `json:"wind,omitempty"` + Snow bool `json:"snow,omitempty"` + Ice bool `json:"ice,omitempty"` + Fog bool `json:"fog,omitempty"` + Heat bool `json:"heat,omitempty"` + Cold bool `json:"cold,omitempty"` + Wind bool `json:"wind,omitempty"` } type AlertOverlap struct { @@ -291,11 +290,10 @@ func sortedKeys(values map[string]struct{}) []string { func indicatorsForText(text string) Indicators { lower := strings.ToLower(text) return Indicators{ - Thunder: strings.Contains(lower, "thunder") || strings.Contains(lower, "storm"), - Snow: strings.Contains(lower, "snow"), - Ice: strings.Contains(lower, "ice") || strings.Contains(lower, "freezing") || strings.Contains(lower, "sleet"), - Fog: strings.Contains(lower, "fog"), - Wind: strings.Contains(lower, "wind") || strings.Contains(lower, "gust"), + Snow: strings.Contains(lower, "snow"), + Ice: strings.Contains(lower, "ice") || strings.Contains(lower, "freezing") || strings.Contains(lower, "sleet"), + Fog: strings.Contains(lower, "fog"), + Wind: strings.Contains(lower, "wind") || strings.Contains(lower, "gust"), } } @@ -318,13 +316,12 @@ func numericIndicators(period ForecastPeriod) Indicators { func mergeIndicators(left Indicators, right Indicators) Indicators { return Indicators{ - Thunder: left.Thunder || right.Thunder, - Snow: left.Snow || right.Snow, - Ice: left.Ice || right.Ice, - Fog: left.Fog || right.Fog, - Heat: left.Heat || right.Heat, - Cold: left.Cold || right.Cold, - Wind: left.Wind || right.Wind, + Snow: left.Snow || right.Snow, + Ice: left.Ice || right.Ice, + Fog: left.Fog || right.Fog, + Heat: left.Heat || right.Heat, + Cold: left.Cold || right.Cold, + Wind: left.Wind || right.Wind, } } diff --git a/internal/forecast/derive_test.go b/internal/forecast/derive_test.go index ed490a0..ab13904 100644 --- a/internal/forecast/derive_test.go +++ b/internal/forecast/derive_test.go @@ -41,10 +41,10 @@ func TestBuildDailySummaryGroupsDaypartsAndComputesMetrics(t *testing.T) { t.Fatalf("morning peak gust = %#v, want 40", morning.PeakWindGust) } if morning.DominantCondition != "Thunderstorms and gusty wind" { - t.Fatalf("morning dominant = %q, want thunderstorm condition", morning.DominantCondition) + t.Fatalf("morning dominant = %q, want raw forecast condition", morning.DominantCondition) } - if !morning.Indicators.Thunder || !morning.Indicators.Wind { - t.Fatalf("morning indicators = %#v, want thunder and wind", morning.Indicators) + if !morning.Indicators.Wind { + t.Fatalf("morning indicators = %#v, want wind", morning.Indicators) } afternoon := summary.Dayparts[2] @@ -87,8 +87,8 @@ func TestBuildDailySummaryFromFixtureBundle(t *testing.T) { if len(summary.Dayparts) != 2 { t.Fatalf("Dayparts length = %d, want 2", len(summary.Dayparts)) } - if !summary.Dayparts[0].Indicators.Thunder { - t.Fatalf("morning indicators = %#v, want thunder", summary.Dayparts[0].Indicators) + if summary.Dayparts[0].DominantCondition != "Showers and thunderstorms" { + t.Fatalf("morning dominant = %q, want raw forecast condition", summary.Dayparts[0].DominantCondition) } if len(summary.AlertOverlaps) != 1 { t.Fatalf("AlertOverlaps length = %d, want 1", len(summary.AlertOverlaps))