# 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 curated `HourlyRenderContext`, not from the raw prompt data package. Weather data selection, derivation, module execution, LLM generation, and artifact paths are handled before template rendering. ## Editing Rules - Use Go `text/template` syntax. - 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 strings can be guarded with `{{ with .Field }}...{{ end }}`. - Slices can be rendered with `{{ range .Items }}...{{ else }}...{{ end }}`. ## Hourly Template Variables These are the complete variables currently available to `internal/reporttemplate/templates/hourly.md.tmpl`. ### Report Metadata | Variable | Type | Description | | --- | --- | --- | | `.ReportTitle` | string | Display title for the report. Currently `Hourly Report`. | | `.LocationName` | string | Prompt/report location label, such as `Brentwood, MO`. Falls back to source location if configured location metadata is unavailable. | | `.ValidPeriod` | string | Friendly local valid period label, such as `2026-05-29 at 8:30 AM to 2026-05-29 at 2:30 PM`. | | `.GeneratedAt` | string | Friendly local generation time label. | ### 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: ```gotemplate ## Summary {{ .GeneratedText.Summary }} {{ with .GeneratedText.Confidence }} ## Confidence {{ . }} {{ end }} ``` ### Current Conditions | Variable | Type | Description | | --- | --- | --- | | `.CurrentConditions` | string | Deterministic one-line current conditions summary. May include condition text, temperature, apparent temperature, humidity, and wind. | Example value: ```text Partly cloudy; 74 F; feels like 76 F; humidity 71%; wind S 8 mph. ``` ### Hourly Forecast | Variable | Type | Description | | --- | --- | --- | | `.HourlyForecast` | []HourlyForecastRow | Ordered rows for the hourly report valid period. | | `.HourlyForecast[].Time` | string | Friendly local period start time, or the source period name if no start label is available. | | `.HourlyForecast[].Summary` | string | Hourly text description, falling back to the period name. | | `.HourlyForecast[].Temperature` | string | Rounded temperature label such as `75 F`, or empty. | | `.HourlyForecast[].Precipitation` | string | Rounded precipitation probability label such as `70% precipitation`, or empty. | | `.HourlyForecast[].Wind` | string | Wind label such as `wind S 10 mph, gusts 18 mph`, or empty. | Example: ```gotemplate ## Hourly Forecast {{ range .HourlyForecast }} - {{ .Time }}: {{ .Summary }}{{ with .Temperature }}; {{ . }}{{ end }}{{ with .Precipitation }}; {{ . }}{{ end }}{{ with .Wind }}; {{ . }}{{ end }} {{ else }} - No hourly forecast rows available. {{ end }} ``` ### Precipitation Timing | Variable | Type | Description | | --- | --- | --- | | `.PrecipitationTiming` | string | Deterministic precipitation timing summary. May include peak probability, precipitation windows, and thunder mention. | Example value: ```text Peak precipitation probability 70% at 10 AM; 2026-05-29 at 10:00 AM to 2026-05-29 at 12:00 PM (max 70% at 10 AM); Thunder is mentioned in the forecast. ``` ### Alerts | Variable | Type | Description | | --- | --- | --- | | `.Alerts` | []string | Alert labels for active alerts overlapping the report period. Empty when there are no relevant alert overlaps. | Example: ```gotemplate ## Alerts {{ range .Alerts }} - {{ . }} {{ else }} - No active alert overlaps for this report period. {{ end }} ``` ### SPC Outlooks | Variable | Type | Description | | --- | --- | --- | | `.SPCOutlooks` | []string | SPC convective outlook labels overlapping the report period. Empty when there are no overlapping outlooks. | Example value: ```text Slight Risk from 2026-05-29 at 7:00 AM to 2026-05-29 at 3:00 PM ``` ### Forecast Discussion | Variable | Type | Description | | --- | --- | --- | | `.ForecastDiscussion.KeyMessages` | []string | AFD key messages included for the hourly report. | | `.ForecastDiscussion.ShortTerm` | string | AFD short-term discussion text, or empty if unavailable. | Example: ```gotemplate ## Forecast Discussion {{ range .ForecastDiscussion.KeyMessages }} - {{ . }} {{ end }}{{ with .ForecastDiscussion.ShortTerm }} {{ . }} {{ end }} ``` ### SPC Discussion | Variable | Type | Description | | --- | --- | --- | | `.SPCDiscussions` | []string | SPC discussion labels retained for overlapping qualifying outlook periods. Empty when there is no relevant SPC discussion. | Example: ```gotemplate ## SPC Discussion {{ range .SPCDiscussions }} - {{ . }} {{ else }} - No overlapping SPC discussion. {{ end }} ``` ### Weather Story | Variable | Type | Description | | --- | --- | --- | | `.WeatherStory` | string | Weather story title and description, or `No weather story available.` | ## Common Patterns Use `with` for optional strings: ```gotemplate {{ with .GeneratedText.Confidence }} ## Confidence {{ . }} {{ end }} ``` Use `range` with `else` for optional lists: ```gotemplate {{ range .SPCOutlooks }} - {{ . }} {{ else }} - No overlapping SPC outlooks. {{ end }} ``` Keep punctuation outside optional blocks when possible: ```gotemplate - {{ .Time }}: {{ .Summary }}{{ with .Temperature }}; {{ . }}{{ end }} ``` ## Validation After editing a template, run: ```bash go test ./internal/reporttemplate ./internal/generatedtext ./internal/app ``` For a full check, run: ```bash 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`.