Revise the report template for the tomorrow report.

This commit is contained in:
2026-06-14 23:26:45 -05:00
parent 74bd69834c
commit 473f252aee
6 changed files with 409 additions and 93 deletions

View File

@@ -1,18 +1,54 @@
# Tomorrow Generated Text
TASK: You are writing structured prose slots for a short-term hourly weather report.
Create structured prose for the Tomorrow Report using the supplied
weatherreporter data package.
The calling application will render the final Markdown report. Your job is not to write the full report. Return only a JSON object matching the configured schema.
Return JSON that matches the Scriptorium-registered schema for
`weather.tomorrow_generated_text`.
Use only the supplied `data_package`. Do not invent weather details, times, hazards, probabilities, or impacts that are not supported by the data.
Fields:
The report focuses on the valid period in `report.valid_period`, typically the next several hours for the configured location.
- `summary`: concise overview of tomorrow's weather.
- `forecast_discussion`: one or more paragraph strings explaining the main
weather drivers and forecast reasoning.
- `precipitation_timing`: optional plain-language precipitation timing context.
- `confidence`: optional uncertainty or confidence note.
Return these fields:
Use deterministic facts from the data package for weather details. Do not
invent watches, warnings, precipitation windows, temperatures, or timing.
- `summary`: required. 1-2 sentences summarizing the main weather story for the valid period.
- `forecast_discussion`: required. 2-3 sentences explaining the broader setup, trend, or forecast reasoning most relevant to the valid period.
- `precipitation_timing`: optional. Include only when the deterministic `precip_timing` module contains precipitation windows.
- `confidence`: optional. Include only if uncertainty, timing spread, or conflicting signals materially affect how the reader should interpret the forecast.
Return JSON only.
# summary
The summary should typically consist of two sentences.
If an active warning is relevant during the report period, lead with the hazard. Otherwise, the first sentence should state the most likely local weather outcome for the valid period, including the overall character of the weather and expected temperature/temperature range.
The second sentence should state the most important active hazard, caveat, uncertainty, or alternate outcome, if one exists. If there is no meaningful caveat, the second sentence may be omitted, or may briefly say that no major complications are apparent.
In the lead, distinguish the main weather outcome from the caveat. If showers and thunderstorms have different timing, state that difference rather than combining them as a single risk throughout the valid period. If the main caveat is a regional severe-weather or precipitation risk displaced from the report location, state that limitation clearly.
Example style:
- “Sunday is expected to be warm and dry, with mostly clear skies. There is a slight chance of isolated showers and thunderstorms developing from late afternoon into early evening.”
# forecast_discussion
Use narrative products to explain the “why” behind the local forecast when useful.
Useful context may include:
- synoptic pattern
- fronts or boundaries
- shortwaves, troughs, or ridges
- instability, moisture, shear, forcing, or capping
- regional placement of precipitation or severe-weather chances
- hazard types and timing windows
- confidence or uncertainty
- conditional outcomes
- relevant notes about the following day or days
# precipitation_timing
Use 1-2 sentences to add practical context, including:
- Whether the precipitation is associated with a moving frontal boundary, convective initiation, or wide stratiform rain (if this can be determined from the data package);
- The expected type, intensity, and duration of the precipitation; and
- Any caveats or uncertainty with respect to the onset, duration, or occurrance of the precipitation.

View File

@@ -220,23 +220,39 @@ func TestRenderTomorrow(t *testing.T) {
},
Modules: testTomorrowModules{
Dayparts: []testTomorrowDaypart{
{
Key: "overnight",
Summary: testDaypartSummary{
DisplayName: "Overnight",
TemperatureTrend: "falling",
TemperatureStartPhraseF: "mid 60s",
TemperatureEndPhraseF: "upper 50s",
DominantConditionDisplay: "Partly cloudy",
DominantConditionLower: "partly cloudy",
},
},
{
Key: "morning",
Summary: testDaypartSummary{
DisplayName: "Morning",
TemperaturePhraseF: "low 60s",
DominantConditionLower: "partly cloudy",
DisplayName: "Morning",
TemperatureTrend: "rising",
TemperatureStartPhraseF: "upper 50s",
TemperatureEndPhraseF: "upper 60s",
DominantConditionDisplay: "Sunny",
DominantConditionLower: "sunny",
},
},
{
Key: "afternoon",
Summary: testDaypartSummary{
DisplayName: "Afternoon",
TemperaturePhraseF: "mid 70s",
DominantConditionLower: "showers",
MaxPopPercent: intPtr(70),
MaxPopTimeLabel: "3:00 PM",
MentionPrecipitation: true,
DisplayName: "Afternoon",
TemperatureTrend: "steady",
TemperatureSteadyPhraseF: "upper 70s",
DominantConditionDisplay: "Sunny",
DominantConditionLower: "sunny",
MaxPopPercent: intPtr(70),
MaxPopTimeLabel: "3:00 PM",
MentionPrecipitation: true,
},
},
},
@@ -256,8 +272,9 @@ func TestRenderTomorrow(t *testing.T) {
"**Forecast date:** Monday, June 15, 2026",
"**Updated:** Sunday, June 14, 2026 at 9:14 AM",
"Tomorrow starts dry before showers return later in the day.",
"- **Morning:** low 60s and partly cloudy.",
"- **Afternoon:** mid 70s and showers. Precipitation chances peak at 70% at 3:00 PM.",
"- **Overnight:** Partly cloudy, with temperatures falling from the mid 60s to the upper 50s.",
"- **Morning:** Sunny, with temperatures rising from the upper 50s to the upper 60s.",
"- **Afternoon:** Sunny, with temperatures in the upper 70s. Chance of precipitation is 70%.",
"- **3:00 PM** to **6:00 PM**: Precipitation is expected during this period. The peak precipitation chance is 70% at 3:00 PM.",
"A few showers may linger into early evening.",
"Clouds increase after sunrise.",
@@ -267,9 +284,18 @@ func TestRenderTomorrow(t *testing.T) {
t.Fatalf("rendered template missing %q:\n%s", want, text)
}
}
for _, unwanted := range []string{
"upper 50s.\n\n- **Morning:**",
"upper 60s.\n\n- **Afternoon:**",
} {
if strings.Contains(text, unwanted) {
t.Fatalf("rendered template includes blank lines between daypart bullets:\n%s", text)
}
}
assertOrderedText(t, text, []string{
"# Monday's Weather",
"## Daypart Forecast",
"- **Overnight:**",
"- **Morning:**",
"- **Afternoon:**",
"## Precipitation Timing",
@@ -296,9 +322,11 @@ func TestRenderTomorrowOmitsPrecipitationTimingWithoutWindows(t *testing.T) {
{
Key: "morning",
Summary: testDaypartSummary{
DisplayName: "Morning",
TemperaturePhraseF: "low 60s",
DominantConditionLower: "clear",
DisplayName: "Morning",
TemperatureTrend: "steady",
TemperatureSteadyPhraseF: "low 60s",
DominantConditionDisplay: "Clear",
DominantConditionLower: "clear",
},
},
},
@@ -436,15 +464,21 @@ type testTomorrowDaypart struct {
}
type testDaypartSummary struct {
DisplayName string
TempRangeF string
TemperaturePhraseF string
MaxPopPercent *int
MaxPopTime string
MaxPopTimeLabel string
MentionPrecipitation bool
DominantCondition string
DominantConditionLower string
DisplayName string
TempRangeF string
TemperaturePhraseF string
TemperatureTrend string
TemperatureStartPhraseF string
TemperatureEndPhraseF string
TemperaturePeakPhraseF string
TemperatureSteadyPhraseF string
MaxPopPercent *int
MaxPopTime string
MaxPopTimeLabel string
MentionPrecipitation bool
DominantCondition string
DominantConditionLower string
DominantConditionDisplay string
}
type testCurrentConditions struct {

View File

@@ -6,10 +6,8 @@
{{ .GeneratedText.Summary }}
## Daypart Forecast
{{ with .Modules.Dayparts }}{{ range . }}{{ $daypart := . }}
- **{{ if .Summary.DisplayName }}{{ .Summary.DisplayName }}{{ else }}{{ .Key }}{{ end }}:** {{ if .Summary.TemperaturePhraseF }}{{ .Summary.TemperaturePhraseF }}{{ with .Summary.DominantConditionLower }} and {{ . }}{{ end }}{{ else }}{{ with .Summary.DominantConditionLower }}{{ . }}{{ else }}Forecast details are limited{{ end }}{{ end }}.{{ if .Summary.MentionPrecipitation }}{{ with .Summary.MaxPopPercent }} Precipitation chances peak at {{ . }}%{{ with $daypart.Summary.MaxPopTimeLabel }} at {{ . }}{{ else }}{{ with $daypart.Summary.MaxPopTime }} at {{ . }}{{ end }}{{ end }}.{{ end }}{{ end }}
{{ end }}{{ else }}
- No daypart forecast details are available.
{{ with .Modules.Dayparts }}{{ range . }}{{ $daypart := . }}- **{{ if .Summary.DisplayName }}{{ .Summary.DisplayName }}{{ else }}{{ .Key }}{{ end }}:** {{ with .Summary.DominantConditionDisplay }}{{ . }}{{ else }}{{ with .Summary.DominantCondition }}{{ . }}{{ else }}Forecast details are limited{{ end }}{{ end }}{{ if eq .Summary.TemperatureTrend "rising" }}{{ with .Summary.TemperatureStartPhraseF }}, with temperatures rising from the {{ . }}{{ with $daypart.Summary.TemperatureEndPhraseF }} to the {{ . }}{{ end }}{{ end }}{{ else if eq .Summary.TemperatureTrend "falling" }}{{ with .Summary.TemperatureStartPhraseF }}, with temperatures falling from the {{ . }}{{ with $daypart.Summary.TemperatureEndPhraseF }} to the {{ . }}{{ end }}{{ end }}{{ else if eq .Summary.TemperatureTrend "peaking" }}{{ with .Summary.TemperaturePeakPhraseF }}, with temperatures peaking in the {{ . }}{{ end }}{{ else }}{{ with .Summary.TemperatureSteadyPhraseF }}, with temperatures in the {{ . }}{{ else }}{{ with .Summary.TemperaturePhraseF }}, with temperatures in the {{ . }}{{ end }}{{ end }}{{ end }}.{{ if .Summary.MentionPrecipitation }}{{ with .Summary.MaxPopPercent }} Chance of precipitation is {{ . }}%.{{ end }}{{ end }}
{{ end }}{{ else }}- No daypart forecast details are available.
{{ end }}
{{ with .Modules.PrecipTiming }}{{ with .PrecipitationWindows }}