24 KiB
Report Templates
Purpose
This guide describes the implemented Markdown report template surface for
weatherreporter. It is for maintainers editing embedded report templates,
especially generated-text-template reports.
Templates are Go text/template files. The implemented top-level templates
are:
internal/reporttemplate/templates/daily.md.tmplinternal/reporttemplate/templates/today.md.tmplinternal/reporttemplate/templates/tomorrow.md.tmplinternal/reporttemplate/templates/hourly.md.tmpl
Shared named partials live under internal/reporttemplate/templates/partials/:
alert_digest.md.tmpl, used by Daily, Today, Tomorrow, and Hourly for the combined Alerts and Risk Products sectiondaypart_forecast.md.tmpl, used by Daily and Tomorrowtoday_daypart_forecast.md.tmpl, used by Todayprecipitation_timing.md.tmpl, used by Daily, Today, Tomorrow, and Hourly
Templates are rendered from structured contexts such as DailyRenderContext,
TodayRenderContext, TomorrowRenderContext, and HourlyRenderContext.
Weather data collection, derivation, module execution, generated text
validation, and artifact paths are handled before template rendering.
Editing Rules
- Use Go
text/templatesyntax. - Keep templates focused on Markdown layout, headings, ordering, and simple conditional display.
- Do not put weather derivation, source selection, or path construction logic in templates.
- Missing template keys are errors. A misspelled variable will fail rendering.
- No custom template functions are registered.
- Named partials are invoked with
{{ template "name" . }}. Pass the current render context (.) unless the partial is intentionally designed for a narrower value. - Optional module stanzas are pointers and should be guarded with
{{ with .Modules.WeatherStory }}...{{ end }}. - Slices can be rendered with
{{ range .Items }}...{{ else }}...{{ end }}.
Hourly Context
The hourly template receives five top-level values:
| Variable | Type | Description |
|---|---|---|
.Report |
HourlyReportContext | Display metadata and friendly labels for the rendered report. |
.GeneratedText |
Hourly | Structured text returned by Scriptorium. |
.Modules |
HourlyTemplateModules | Preferred deterministic template surface, keyed by module purpose. |
.Collected |
facts.CollectedFacts | Normalized upstream facts for advanced template use. |
.Derived |
facts.DerivedFacts | Shared derived facts for advanced template use. |
Prefer .Modules for normal template edits. .Collected and .Derived are
available when a template needs lower-level facts, but templates should still
avoid nontrivial derivation.
Report
| Variable | Type | Description |
|---|---|---|
.Report.Title |
string | Display title. Currently Hourly Report. |
.Report.LocationName |
string | Prompt/report location label, such as Brentwood, MO. |
.Report.GeneratedAt |
time.Time | Canonical generation timestamp. |
.Report.GeneratedAtLabel |
string | Friendly local generation time label. |
.Report.ValidPeriod |
timeutil.Period | Canonical valid period. |
.Report.ValidPeriodLabel |
string | Friendly local valid period label, such as 2026-05-29 at 8:30 AM to 2026-05-29 at 2:30 PM. |
.Report.Timezone |
string | Effective report timezone. |
GeneratedText
These fields are written by Scriptorium as structured JSON, validated by weatherreporter, and then inserted into the render context.
| Variable | Type | Description |
|---|---|---|
.GeneratedText.Summary |
string | Required short prose summary. |
.GeneratedText.ForecastDiscussion |
string | Required prose for the Forecast Discussion section. |
.GeneratedText.PrecipitationTiming |
string | Optional prose rendered after deterministic precipitation windows. |
.GeneratedText.Confidence |
string | Optional confidence or uncertainty note. Empty when omitted by the LLM; not rendered by the current hourly template. |
Example:
{{ .GeneratedText.Summary }}
## Forecast Discussion
{{ .GeneratedText.ForecastDiscussion }}
Tomorrow Context
The Tomorrow template receives five top-level values:
| Variable | Type | Description |
|---|---|---|
.Report |
TomorrowReportContext | Display metadata and friendly labels for the rendered report. |
.GeneratedText |
Tomorrow | Structured text returned by Scriptorium. |
.Modules |
TomorrowTemplateModules | Preferred deterministic template surface, keyed by module purpose. |
.Collected |
facts.CollectedFacts | Normalized upstream facts for advanced template use. |
.Derived |
facts.DerivedFacts | Shared derived facts for advanced template use. |
Tomorrow report metadata includes .Report.Title, .Report.ForecastDate,
.Report.ForecastDateLabel, .Report.ForecastDayName,
.Report.GeneratedAt, .Report.GeneratedAtLabel, .Report.ValidPeriod, and
.Report.Timezone.
Tomorrow generated text uses the same .GeneratedText.Summary,
.GeneratedText.PrecipitationTiming, and .GeneratedText.Confidence fields as
Hourly. .GeneratedText.ForecastDiscussion is a slice of paragraphs and should
be rendered with range.
Tomorrow uses the shared alert_digest, daypart_forecast, and
precipitation_timing partials.
Tomorrow modules include the Hourly module fields plus:
| Variable | Type | Description |
|---|---|---|
.Modules.DerivedDailySummary |
*briefing.DerivedDailySummaryModule | Daily summary facts for the forecast date. |
.Modules.DerivedDaypartSummaries |
*map[string]briefing.DerivedDaypartSummaryModule | Raw daypart summary map, when direct keyed access is needed. |
.Modules.Dayparts |
[]generatedtext.TomorrowDaypartContext | Ordered daypart summaries for deterministic template rendering. |
.Modules.TomorrowPlanning |
*briefing.TomorrowPlanningModule | Planning facts for the next local civil day. |
Prefer .Modules.Dayparts over ranging through
.Modules.DerivedDaypartSummaries; it follows configured daypart order and
falls back to sorted keys for any unmatched entries.
Daily Context
The Daily template receives the same five top-level values as Tomorrow, using
DailyReportContext, Daily, and DailyTemplateModules.
Daily report metadata includes .Report.Title, .Report.ForecastDate,
.Report.ForecastDateLabel, .Report.ForecastDayName,
.Report.GeneratedAt, .Report.GeneratedAtLabel, .Report.ValidPeriod, and
.Report.Timezone.
Daily generated text uses .GeneratedText.Summary,
.GeneratedText.ForecastDiscussion, .GeneratedText.PrecipitationTiming, and
.GeneratedText.Confidence. Forecast discussion is a slice of paragraphs and
should be rendered with range.
Daily uses the shared alert_digest, daypart_forecast, and
precipitation_timing partials.
Daily uses template ID daily, generated-text schema ID daily, and prompt
source internal/reporttemplate/prompts/daily.generated_text.md.
Daily modules include the Hourly module fields plus:
| Variable | Type | Description |
|---|---|---|
.Modules.DerivedDailySummary |
*briefing.DerivedDailySummaryModule | Daily summary facts for the forecast date. |
.Modules.DerivedDaypartSummaries |
*map[string]briefing.DerivedDaypartSummaryModule | Raw daypart summary map, when direct keyed access is needed. |
.Modules.Dayparts |
[]generatedtext.DailyDaypartContext | Ordered daypart summaries for deterministic template rendering. |
.Modules.DailyPlanning |
*briefing.DailyPlanningModule | Planning facts for the selected local civil day. |
Prefer .Modules.Dayparts over ranging through
.Modules.DerivedDaypartSummaries; it follows configured daypart order and
falls back to sorted keys for any unmatched entries.
Today Context
The Today template receives the same five top-level values as Tomorrow, using
TodayReportContext, Today, and TodayTemplateModules.
Today report metadata includes .Report.Title, .Report.ForecastDate,
.Report.ForecastDateLabel, .Report.ForecastDayName,
.Report.GeneratedAt, .Report.GeneratedAtLabel, .Report.ValidPeriod, and
.Report.Timezone.
Today generated text uses .GeneratedText.Summary,
.GeneratedText.ForecastDiscussion, .GeneratedText.PrecipitationTiming, and
.GeneratedText.Confidence. Forecast discussion is a slice of paragraphs and
should be rendered with range.
Today uses the today_daypart_forecast partial so elapsed or missing dayparts
can be omitted while Daily and Tomorrow keep their fallback row. It also uses
the shared alert_digest and precipitation_timing partials.
Today uses template ID today, generated-text schema ID today, and prompt
source internal/reporttemplate/prompts/today.generated_text.md.
Today modules include the Hourly module fields plus:
| Variable | Type | Description |
|---|---|---|
.Modules.DerivedDailySummary |
*briefing.DerivedDailySummaryModule | Daily summary facts for the forecast date. |
.Modules.DerivedDaypartSummaries |
*map[string]briefing.DerivedDaypartSummaryModule | Raw daypart summary map, when direct keyed access is needed. |
.Modules.Dayparts |
[]generatedtext.TodayDaypartContext | Ordered daypart summaries for deterministic template rendering. |
.Modules.TodayPlanning |
*briefing.TodayPlanningModule | Planning facts for the current local civil day. |
Prefer .Modules.Dayparts over ranging through
.Modules.DerivedDaypartSummaries; it follows configured daypart order and
falls back to sorted keys for any unmatched entries.
Modules
.Modules exposes typed outputs from the same module pipeline used for the
prompt data package. Module fields are pointers because missing-data policy may
omit a stanza.
Templates render from rich module values, not from the curated YAML data package. Some fields documented below are deterministic wording helpers for Markdown templates and are intentionally omitted from data packages passed to Scriptorium. The data package is a prompt input, while the render context is the template surface.
| Variable | Type | Description |
|---|---|---|
.Modules.Metadata |
*briefing.MetadataModule | Report metadata module output, when present. |
.Modules.CurrentConditions |
*briefing.CurrentConditionsModule | Current conditions from /conditions/current. |
.Modules.HourlyForecast |
*briefing.HourlyForecastModule | Hourly forecast periods overlapping the report valid period. |
.Modules.PrecipTiming |
*briefing.PrecipTimingModule | Derived precipitation timing facts and threshold windows. |
.Modules.AlertDigest |
*briefing.AlertDigestModule | Active alert status and relevant alert overlaps. |
.Modules.SPCConvectiveOutlooks |
*briefing.SPCConvectiveOutlooksModule | SPC outlooks that overlap the report valid period. |
.Modules.AreaForecastDiscussion |
*briefing.AreaForecastDiscussionModule | AFD key messages and configured discussion sections. |
.Modules.SPCConvectiveDiscussion |
*briefing.SPCConvectiveDiscussionModule | SPC discussions retained for qualifying overlapping categorical risk days. |
.Modules.WeatherStory |
*briefing.WeatherStoryModule | Latest NWS weather story, when available. |
Current Conditions
Common fields:
| Variable | Type | Description |
|---|---|---|
.Modules.CurrentConditions.ConditionText |
string | Current condition text. |
.Modules.CurrentConditions.ConditionTextLower |
string | Lower-case current condition text for inline sentences. |
.Modules.CurrentConditions.TemperatureF |
*int | Rounded current temperature. |
.Modules.CurrentConditions.ApparentTemperatureF |
*int | Rounded apparent temperature. |
.Modules.CurrentConditions.RelativeHumidityPercent |
*int | Rounded relative humidity. |
.Modules.CurrentConditions.WindDirection |
string | 16-point compass wind direction. |
.Modules.CurrentConditions.WindDirectionText |
string | Lower-case full wind direction text, such as northwest. |
.Modules.CurrentConditions.WindSpeedMph |
*int | Rounded wind speed. |
Example:
{{ with .Modules.CurrentConditions }}
{{ .ConditionText }}{{ with .TemperatureF }}; {{ . }} F{{ end }}{{ with .WindDirection }}; wind {{ . }}{{ end }}{{ with .WindSpeedMph }} {{ . }} mph{{ end }}
{{ else }}
No current conditions available.
{{ end }}
Hourly Forecast
Common period fields:
| Variable | Type | Description |
|---|---|---|
.Modules.HourlyForecast.Periods |
[]briefing.HourlyForecastPeriod | Ordered periods for the hourly report valid period. |
.Modules.HourlyForecast.Periods[].HourLabel |
string | Friendly hour label such as 4:00 PM. |
.Modules.HourlyForecast.Periods[].PeriodBegins |
string | Friendly local period start label. |
.Modules.HourlyForecast.Periods[].PeriodEnds |
string | Friendly local period end label. |
.Modules.HourlyForecast.Periods[].Name |
string | Source period name. |
.Modules.HourlyForecast.Periods[].TextDescription |
string | Hourly forecast text. |
.Modules.HourlyForecast.Periods[].TextDescriptionLower |
string | Lower-case hourly forecast text for inline sentences. |
.Modules.HourlyForecast.Periods[].TemperatureF |
*float64 | Forecast temperature. |
.Modules.HourlyForecast.Periods[].ProbabilityOfPrecipitationPercent |
*float64 | Forecast precipitation probability. |
.Modules.HourlyForecast.Periods[].MentionPrecipitation |
bool | True when precipitation probability meets the hourly mention threshold. |
.Modules.HourlyForecast.Periods[].WindDirection |
string | 16-point compass wind direction. |
.Modules.HourlyForecast.Periods[].WindSpeedMph |
*float64 | Wind speed. |
.Modules.HourlyForecast.Periods[].WindGustMph |
*float64 | Wind gust. |
Example:
{{ with .Modules.HourlyForecast }}{{ range .Periods }}
- **{{ .HourLabel }}:**{{ with .TemperatureF }} {{ . }}°F{{ end }} and {{ .TextDescriptionLower }}.{{ if .MentionPrecipitation }}{{ with .ProbabilityOfPrecipitationPercent }} Probability of precipitation is {{ . }}%.{{ end }}{{ end }}
{{ else }}
- No hourly forecast rows available.
{{ end }}{{ end }}
Precipitation Timing
Common fields:
| Variable | Type | Description |
|---|---|---|
.Modules.PrecipTiming.MaxPopPercent |
*int | Highest hourly precipitation probability in the valid period. |
.Modules.PrecipTiming.MaxPopTime |
string | Friendly local time for the highest hourly precipitation probability. |
.Modules.PrecipTiming.ProbabilityThreshold |
float64 | Threshold used to define precipitation windows. |
.Modules.PrecipTiming.PrecipitationWindows |
[]briefing.PrecipitationWindowModule | One or more threshold precipitation windows. |
.Modules.PrecipTiming.PrecipitationWindows[].PeriodBegins |
string | Friendly local window start. |
.Modules.PrecipTiming.PrecipitationWindows[].PeriodBeginsHourLabel |
string | Friendly window start hour, such as 4:00 PM. |
.Modules.PrecipTiming.PrecipitationWindows[].PeriodEnds |
string | Friendly local window end; omitted for open windows. |
.Modules.PrecipTiming.PrecipitationWindows[].PeriodEndsHourLabel |
string | Friendly window end hour; omitted for open windows. |
.Modules.PrecipTiming.PrecipitationWindows[].MaxPopPercent |
*int | Highest precipitation probability inside the window. |
.Modules.PrecipTiming.PrecipitationWindows[].MaxPopTime |
string | Friendly local time for the window maximum. |
.Modules.PrecipTiming.PrecipitationWindows[].MaxPopHourLabel |
string | Friendly hour label for the window maximum. |
.Modules.PrecipTiming.PrecipitationWindows[].PrecipitationType |
string | Conservatively inferred precipitation type, such as showers and thunderstorms. |
.Modules.PrecipTiming.PrecipitationWindows[].ExpectationPhrase |
string | Probability-based sentence used by precipitation timing templates. |
.Modules.PrecipTiming.ThunderMentioned |
bool | Whether thunder is mentioned in the forecast text. |
Daypart Summaries
Daily, Today, and Tomorrow templates should use .Modules.Dayparts for
ordered daypart rendering. Each item has Key and Summary; Summary is a
rich briefing.DerivedDaypartSummaryModule.
The shared daypart partials render from these same .Modules.Dayparts values.
Edit daypart_forecast.md.tmpl for common Daily/Tomorrow wording, and edit
today_daypart_forecast.md.tmpl for Today-specific omission behavior.
Common rich daypart fields:
| Variable | Type | Description |
|---|---|---|
.Modules.Dayparts[].Summary.DisplayName |
string | Human-readable daypart label. |
.Modules.Dayparts[].Summary.PeriodBegins |
string | Friendly local daypart start. |
.Modules.Dayparts[].Summary.PeriodEnds |
string | Friendly local daypart end. |
.Modules.Dayparts[].Summary.TempRangeF |
string | Rounded temperature range or single temperature. |
.Modules.Dayparts[].Summary.TemperaturePhraseF |
string | Temperature phrase used for steady template wording. |
.Modules.Dayparts[].Summary.TemperatureTrend |
string | Trend category such as rising, falling, peaking, or steady. |
.Modules.Dayparts[].Summary.TemperatureStartPhraseF |
string | Starting temperature phrase for rising/falling wording. |
.Modules.Dayparts[].Summary.TemperatureEndPhraseF |
string | Ending temperature phrase for rising/falling wording. |
.Modules.Dayparts[].Summary.TemperaturePeakPhraseF |
string | Peak temperature phrase for peaking wording. |
.Modules.Dayparts[].Summary.TemperatureSteadyPhraseF |
string | Steady temperature phrase. |
.Modules.Dayparts[].Summary.MaxPopPercent |
*int | Highest precipitation probability in the daypart. |
.Modules.Dayparts[].Summary.MaxPopTime |
string | Friendly local time for the highest precipitation probability. |
.Modules.Dayparts[].Summary.MaxPopTimeLabel |
string | Clock-style label for deterministic precipitation timing text. |
.Modules.Dayparts[].Summary.MentionPrecipitation |
bool | True when precipitation probability should be mentioned by the template. |
.Modules.Dayparts[].Summary.DominantCondition |
string | Dominant condition text. |
.Modules.Dayparts[].Summary.DominantConditionLower |
string | Lower-case condition text for inline sentences. |
.Modules.Dayparts[].Summary.DominantConditionDisplay |
string | Display-case condition text for bullet starts. |
.Modules.Dayparts[].Summary.NotableConditions |
[]string | Notable condition labels retained for the daypart. |
Template-only daypart helpers such as TemperaturePhraseF,
DominantConditionLower, DominantConditionDisplay, and MaxPopTimeLabel
remain available here even though they are not serialized into data-package
YAML.
Alert Digest
| Variable | Type | Description |
|---|---|---|
.Modules.AlertDigest.Checked |
bool | Whether alert data was checked successfully. |
.Modules.AlertDigest.ActiveCount |
int | Active alert count from the source. |
.Modules.AlertDigest.RelevantCount |
int | Alert count overlapping the report period. |
.Modules.AlertDigest.Missing |
bool | True when alert data is unavailable. |
.Modules.AlertDigest.Relevant |
[]briefing.AlertSummary | Relevant alert summaries. |
.Modules.AlertDigest.Relevant[].Event |
string | Alert event name. |
.Modules.AlertDigest.Relevant[].Headline |
string | Alert headline. |
.Modules.AlertDigest.Relevant[].Severity |
string | Alert severity. |
.Modules.AlertDigest.Relevant[].PeriodBegins |
string | Friendly local alert applicability start. |
.Modules.AlertDigest.Relevant[].PeriodEnds |
string | Friendly local alert applicability end. |
.Modules.AlertDigest.Relevant[].Instruction |
string | Alert instruction text, when provided. |
.Modules.AlertDigest.Relevant[].Description |
string | Alert description text, when provided. |
SPC Outlooks And Discussion
| Variable | Type | Description |
|---|---|---|
.Modules.SPCConvectiveOutlooks.Checked |
bool | Whether SPC outlook data was checked successfully. |
.Modules.SPCConvectiveOutlooks.AsOf |
string | Friendly source as-of time. |
.Modules.SPCConvectiveOutlooks.IssuedAt |
string | Friendly source issue time. |
.Modules.SPCConvectiveOutlooks.Outlooks |
[]briefing.SPCConvectiveOutlookRecord | Overlapping outlook records. |
.Modules.SPCConvectiveOutlooks.Outlooks[].Day |
int | SPC day number. |
.Modules.SPCConvectiveOutlooks.Outlooks[].OutlookType |
string | Outlook type, such as categorical. |
.Modules.SPCConvectiveOutlooks.Outlooks[].Label |
string | Short outlook label. |
.Modules.SPCConvectiveOutlooks.Outlooks[].LabelText |
string | Human-readable outlook label. |
.Modules.SPCConvectiveOutlooks.Outlooks[].BackgroundDefinition |
*briefing.SPCOutlookBackgroundDefinition | Embedded background context for known SPC outlook products. |
.Modules.SPCConvectiveOutlooks.Outlooks[].BackgroundDefinition.PlainLanguage |
string | Plain-language outlook definition. |
.Modules.SPCConvectiveOutlooks.Outlooks[].BackgroundDefinition.OfficialDescription |
string | Official or source-aligned outlook definition. |
.Modules.SPCConvectiveOutlooks.Outlooks[].BackgroundDefinition.RelativeLevel |
string | Relative categorical risk level, when defined. |
.Modules.SPCConvectiveOutlooks.Outlooks[].PeriodBegins |
string | Friendly outlook period start. |
.Modules.SPCConvectiveOutlooks.Outlooks[].PeriodEnds |
string | Friendly outlook period end. |
.Modules.SPCConvectiveOutlooks.Outlooks[].ImageURL |
string | Source image URL. |
.Modules.SPCConvectiveOutlooks.RiskDigest |
[]briefing.SPCConvectiveOutlookDigest | Curated categorical outlooks for the shared Alerts and Risk Products section. |
.Modules.SPCConvectiveOutlooks.RiskDigest[].LabelText |
string | Human-readable outlook label. |
.Modules.SPCConvectiveOutlooks.RiskDigest[].RiskLabel |
string | Sentence-style risk label for report rendering. |
.Modules.SPCConvectiveOutlooks.RiskDigest[].PeriodBegins |
string | Friendly outlook period start. |
.Modules.SPCConvectiveOutlooks.RiskDigest[].PeriodEnds |
string | Friendly outlook period end. |
.Modules.SPCConvectiveDiscussion.IncludedBecause |
string | Criterion used to include discussions. |
.Modules.SPCConvectiveDiscussion.Discussions |
[]briefing.SPCConvectiveDiscussionRecord | Retained discussion records. |
.Modules.SPCConvectiveDiscussion.Discussions[].Headline |
string | Discussion headline. |
.Modules.SPCConvectiveDiscussion.Discussions[].Summary |
string | Discussion summary. |
.Modules.SPCConvectiveDiscussion.Discussions[].Discussion |
string | Full discussion text. |
Area Forecast Discussion
| Variable | Type | Description |
|---|---|---|
.Modules.AreaForecastDiscussion.Product |
string | Source product identifier. |
.Modules.AreaForecastDiscussion.KeyMessages |
[]string | AFD key messages. |
.Modules.AreaForecastDiscussion.ShortTerm |
string | AFD short-term section text. |
.Modules.AreaForecastDiscussion.LongTerm |
string | AFD long-term section text. |
Weather Story
| Variable | Type | Description |
|---|---|---|
.Modules.WeatherStory.Available |
bool | True when a story is available. |
.Modules.WeatherStory.OfficeID |
string | Source office ID. |
.Modules.WeatherStory.PeriodBegins |
string | Friendly story period start. |
.Modules.WeatherStory.PeriodEnds |
string | Friendly story period end. |
.Modules.WeatherStory.UpdatedAt |
*time.Time | Canonical update timestamp. |
.Modules.WeatherStory.Title |
string | Story title. |
.Modules.WeatherStory.Description |
string | Story description. |
.Modules.WeatherStory.AltText |
string | Story image alt text. |
.Modules.WeatherStory.Priority |
bool | Source priority flag. |
.Modules.WeatherStory.Order |
int | Source order. |
.Modules.WeatherStory.DownloadURL |
string | Source download URL. |
Collected And Derived Facts
The template also receives the full facts.CollectedFacts and
facts.DerivedFacts structs:
.Collectedcontains normalized source data and provenance from upstream Weather API fetches..Derivedcontains shared slices and calculations used across modules, such as valid-period hourly periods, precipitation timing, alert overlaps, and SPC filtering inputs.
These values are intentionally lower-level than .Modules. Use them when a
template needs a specific field that is not exposed by a module, but keep
calculation-heavy changes in Go.
Validation
After editing a template, run:
go test ./internal/reporttemplate ./internal/generatedtext ./internal/app
For a full check, run:
go test ./...
go run ./cmd/weatherreporter --help
git diff --check
Template render tests exercise the Daily, Today, Tomorrow, and Hourly
templates through internal/generatedtext/render_context_test.go and
internal/reporttemplate/reporttemplate_test.go.