Implemented the AFD Short Term / Long Term coverage

This commit is contained in:
2026-05-29 19:42:07 -05:00
parent 3e93a97d10
commit 42defcf4b9
9 changed files with 69 additions and 7 deletions

View File

@@ -17,7 +17,9 @@ Inputs:
Outputs:
- `promptinput.Package` containing schema version, RunID, report metadata,
briefing content, Recent Changes, and source warnings
briefing content, Recent Changes, and source warnings. Briefing content
includes discussion key messages and short/long-term AFD narratives when the
Weather API provides them.
- optional JSON file written by `promptinput.Save`
## Boundaries

View File

@@ -43,6 +43,12 @@ func TestFetchBundleFromFixtures(t *testing.T) {
if bundle.Discussion == nil || len(bundle.Discussion.KeyMessages) != 2 {
t.Fatalf("Discussion = %#v, want key messages", bundle.Discussion)
}
if bundle.Discussion.ShortTerm == nil || bundle.Discussion.ShortTerm.Narrative != "A weak boundary may trigger isolated showers." {
t.Fatalf("Discussion.ShortTerm = %#v, want short-term AFD narrative", bundle.Discussion.ShortTerm)
}
if bundle.Discussion.LongTerm == nil || bundle.Discussion.LongTerm.Narrative != "Warmer temperatures and periodic rain chances continue into the weekend." {
t.Fatalf("Discussion.LongTerm = %#v, want long-term AFD narrative", bundle.Discussion.LongTerm)
}
if len(bundle.Sources) != 8 {
t.Fatalf("Sources length = %d, want 8", len(bundle.Sources))
}

View File

@@ -11,6 +11,10 @@
"shortTerm": {
"title": "Short Term",
"narrative": "A weak boundary may trigger isolated showers."
},
"longTerm": {
"title": "Long Term",
"narrative": "Warmer temperatures and periodic rain chances continue into the weekend."
}
}
}

View File

@@ -33,7 +33,7 @@ func TestFetchAndSaveBundle(t *testing.T) {
case "/alerts/active":
_, _ = w.Write([]byte(`{"data":{"alerts":[]}}`))
case "/discussion":
_, _ = w.Write([]byte(`{"data":{"product":"discussion","issuedAt":"2026-05-29T09:25:00-05:00","keyMessages":[]}}`))
_, _ = w.Write([]byte(`{"data":{"product":"discussion","issuedAt":"2026-05-29T09:25:00-05:00","keyMessages":[],"shortTerm":{"title":"Short Term","narrative":"Short-term AFD narrative for saved bundle."},"longTerm":{"title":"Long Term","narrative":"Long-term AFD narrative for saved bundle."}}}`))
default:
http.NotFound(w, r)
}
@@ -195,6 +195,9 @@ func TestGenerateReportWritesReportAndPreflight(t *testing.T) {
if !strings.Contains(string(data), `"recentChanges"`) || !strings.Contains(string(data), `data_package.v1`) {
t.Fatalf("data package missing expected content:\n%s", string(data))
}
if !strings.Contains(string(data), "Short-term AFD narrative for generated report.") || !strings.Contains(string(data), "Long-term AFD narrative for generated report.") {
t.Fatalf("data package missing AFD short/long-term discussion:\n%s", string(data))
}
preflight, err := os.ReadFile(result.PreflightPath)
if err != nil {
t.Fatalf("read preflight: %v", err)
@@ -881,7 +884,7 @@ func dailyBundleServer(t *testing.T) *httptest.Server {
case "/alerts/active":
_, _ = w.Write([]byte(`{"data":{"alerts":[{"event":"Flood Watch","effective":"2026-05-29T05:00:00-05:00","expires":"2026-05-29T09:00:00-05:00"}]}}`))
case "/discussion":
_, _ = w.Write([]byte(`{"data":{"product":"discussion","issuedAt":"2026-05-29T09:25:00-05:00","keyMessages":["Storms are most likely during the morning."]}}`))
_, _ = w.Write([]byte(`{"data":{"product":"discussion","issuedAt":"2026-05-29T09:25:00-05:00","keyMessages":["Storms are most likely during the morning."],"shortTerm":{"title":"Short Term","narrative":"Short-term AFD narrative for generated report."},"longTerm":{"title":"Long Term","narrative":"Long-term AFD narrative for generated report."}}}`))
default:
http.NotFound(w, r)
}

View File

@@ -67,6 +67,12 @@ func TestDailyBriefingFromRepresentativeFixture(t *testing.T) {
if len(pkg.Daily.Discussion.KeyMessages) != 1 {
t.Fatalf("Discussion key messages length = %d, want 1", len(pkg.Daily.Discussion.KeyMessages))
}
if pkg.Daily.Discussion.ShortTerm != "Morning showers taper as a weak boundary shifts east." {
t.Fatalf("Discussion.ShortTerm = %q, want short-term AFD narrative", pkg.Daily.Discussion.ShortTerm)
}
if pkg.Daily.Discussion.LongTerm != "Warmer and more humid conditions return with periodic rain chances." {
t.Fatalf("Discussion.LongTerm = %q, want long-term AFD narrative", pkg.Daily.Discussion.LongTerm)
}
if pkg.Daily.OutdoorWindows.Best == nil || pkg.Daily.OutdoorWindows.Worst == nil {
t.Fatalf("OutdoorWindows = %#v, want best and worst", pkg.Daily.OutdoorWindows)
}

View File

@@ -44,7 +44,12 @@ func TestStormBriefingWithActiveAlert(t *testing.T) {
Alerts: &forecast.AlertRun{Alerts: []json.RawMessage{
json.RawMessage(`{"event":"Severe Thunderstorm Warning","headline":"Severe storms near Testville","severity":"Severe","effective":"2026-05-29T06:30:00-05:00","expires":"2026-05-29T08:30:00-05:00"}`),
}},
Discussion: &forecast.Discussion{Product: "discussion", KeyMessages: []string{"Storms may intensify quickly."}},
Discussion: &forecast.Discussion{
Product: "discussion",
KeyMessages: []string{"Storms may intensify quickly."},
ShortTerm: &forecast.DiscussionSection{Narrative: "Short-term storm coverage peaks this morning."},
LongTerm: &forecast.DiscussionSection{Narrative: "Long-term pattern stays unsettled after the event."},
},
WeatherStory: &forecast.WeatherStory{Raw: json.RawMessage(`{"headline":"Storm risk"}`)},
Sources: []forecast.Source{{Name: "hourly", FetchedAt: time.Now()}},
}
@@ -75,6 +80,12 @@ func TestStormBriefingWithActiveAlert(t *testing.T) {
if pkg.Storm.WeatherStory == nil {
t.Fatal("WeatherStory = nil, want available story context")
}
if pkg.Storm.Discussion.ShortTerm != "Short-term storm coverage peaks this morning." {
t.Fatalf("Discussion.ShortTerm = %q, want short-term AFD narrative", pkg.Storm.Discussion.ShortTerm)
}
if pkg.Storm.Discussion.LongTerm != "Long-term pattern stays unsettled after the event." {
t.Fatalf("Discussion.LongTerm = %q, want long-term AFD narrative", pkg.Storm.Discussion.LongTerm)
}
if len(pkg.Storm.WhatToWatchNext) == 0 {
t.Fatal("WhatToWatchNext length = 0, want watch inputs")
}

View File

@@ -43,7 +43,12 @@ func TestThreeDayBriefingBuildsOutlookDays(t *testing.T) {
},
},
AlertOverlaps: []forecast.AlertOverlap{{Event: "Flood Watch"}},
Discussion: &forecast.Discussion{Product: "discussion", KeyMessages: []string{"Unsettled stretch."}},
Discussion: &forecast.Discussion{
Product: "discussion",
KeyMessages: []string{"Unsettled stretch."},
ShortTerm: &forecast.DiscussionSection{Narrative: "Short-term rain chances remain focused today."},
LongTerm: &forecast.DiscussionSection{Narrative: "Long-term warmth builds into the weekend."},
},
},
{
Date: "2026-05-30",
@@ -80,4 +85,10 @@ func TestThreeDayBriefingBuildsOutlookDays(t *testing.T) {
if len(pkg.ThreeDay.RelevantAlerts) != 1 {
t.Fatalf("RelevantAlerts length = %d, want 1", len(pkg.ThreeDay.RelevantAlerts))
}
if pkg.ThreeDay.Discussion.ShortTerm != "Short-term rain chances remain focused today." {
t.Fatalf("Discussion.ShortTerm = %q, want short-term AFD narrative", pkg.ThreeDay.Discussion.ShortTerm)
}
if pkg.ThreeDay.Discussion.LongTerm != "Long-term warmth builds into the weekend." {
t.Fatalf("Discussion.LongTerm = %q, want long-term AFD narrative", pkg.ThreeDay.Discussion.LongTerm)
}
}

View File

@@ -54,7 +54,12 @@ func TestWeekendBriefingBuildsPlanningInputs(t *testing.T) {
},
},
AlertOverlaps: []forecast.AlertOverlap{{Event: "Flood Watch"}},
Discussion: &forecast.Discussion{Product: "discussion", KeyMessages: []string{"Timing may shift."}},
Discussion: &forecast.Discussion{
Product: "discussion",
KeyMessages: []string{"Timing may shift."},
ShortTerm: &forecast.DiscussionSection{Narrative: "Short-term showers exit before the weekend."},
LongTerm: &forecast.DiscussionSection{Narrative: "Long-term weekend rain timing remains uncertain."},
},
},
}
@@ -85,4 +90,10 @@ func TestWeekendBriefingBuildsPlanningInputs(t *testing.T) {
if len(pkg.Weekend.Planning.UncertaintyInputs) == 0 {
t.Fatal("UncertaintyInputs length = 0, want discussion context")
}
if pkg.Weekend.Discussion.ShortTerm != "Short-term showers exit before the weekend." {
t.Fatalf("Discussion.ShortTerm = %q, want short-term AFD narrative", pkg.Weekend.Discussion.ShortTerm)
}
if pkg.Weekend.Discussion.LongTerm != "Long-term weekend rain timing remains uncertain." {
t.Fatalf("Discussion.LongTerm = %q, want long-term AFD narrative", pkg.Weekend.Discussion.LongTerm)
}
}

View File

@@ -54,7 +54,15 @@
"issuedAt": "2026-05-29T09:25:00-05:00",
"keyMessages": [
"Storms are most likely during the morning."
]
],
"shortTerm": {
"title": "Short Term",
"narrative": "Morning showers taper as a weak boundary shifts east."
},
"longTerm": {
"title": "Long Term",
"narrative": "Warmer and more humid conditions return with periodic rain chances."
}
},
"sources": [
{