12 KiB
Report Templates
Purpose
This guide describes the implemented Markdown report template surface for
weatherreporter. It is for maintainers editing embedded report templates,
especially the hourly report template.
Templates are Go text/template files. The current implemented template is:
internal/reporttemplate/templates/hourly.md.tmpl
The hourly template is rendered from a structured 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 currently registered.
- 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.Timing |
string | Required prose about timing of notable weather changes or hazards. |
.GeneratedText.Impacts |
string | Required prose about practical near-term impacts. |
.GeneratedText.Confidence |
string | Optional confidence or uncertainty note. Empty when omitted by the LLM. |
Example:
## Summary
{{ .GeneratedText.Summary }}
{{ with .GeneratedText.Confidence }}
## Confidence
{{ . }}
{{ end }}
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.
| 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.TemperatureF |
*float64 | Current temperature. |
.Modules.CurrentConditions.ApparentTemperatureF |
*float64 | Apparent temperature. |
.Modules.CurrentConditions.RelativeHumidityPercent |
*float64 | Relative humidity. |
.Modules.CurrentConditions.WindDirection |
string | 16-point compass wind direction. |
.Modules.CurrentConditions.WindSpeedMph |
*float64 | 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[].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[].TemperatureF |
*float64 | Forecast temperature. |
.Modules.HourlyForecast.Periods[].ProbabilityOfPrecipitationPercent |
*float64 | Forecast precipitation probability. |
.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 }}
- {{ .PeriodBegins }}: {{ .TextDescription }}{{ with .TemperatureF }}; {{ . }} F{{ end }}{{ with .ProbabilityOfPrecipitationPercent }}; {{ . }}% precipitation{{ 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[].PeriodEnds |
string | Friendly local window end; 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.ThunderMentioned |
bool | Whether thunder is mentioned in the forecast text. |
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. |
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[].PeriodBegins |
string | Friendly outlook period start. |
.Modules.SPCConvectiveOutlooks.Outlooks[].PeriodEnds |
string | Friendly outlook period end. |
.Modules.SPCConvectiveOutlooks.Outlooks[].ImageURL |
string | Source image URL. |
.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 currently exercise the hourly template through
internal/generatedtext/render_context_test.go and
internal/reporttemplate/reporttemplate_test.go.