# Report Templates This guide is for maintainers editing Weatherreporter's embedded Markdown templates. Templates format already validated report inputs; they do not select sources, derive weather facts, or validate generated prose. For those details, see [Generated Text internals](internal/generatedtext.md) and [Report Template internals](internal/reporttemplate.md). ## Template Assets Only the generated-text reports use repository-native Markdown templates. Each report has one matching template ID, generated-text schema ID, and prompt source: | Report | Template | Schema | Prompt ID and source | | --- | --- | --- | --- | | Daily | `templates/daily.md.tmpl` (`daily`) | `daily` | `weather.daily_generated_text`; `prompts/daily.generated_text.md` | | Today | `templates/today.md.tmpl` (`today`) | `today` | `weather.today_generated_text`; `prompts/today.generated_text.md` | | Tomorrow | `templates/tomorrow.md.tmpl` (`tomorrow`) | `tomorrow` | `weather.tomorrow_generated_text`; `prompts/tomorrow.generated_text.md` | | Hourly | `templates/hourly.md.tmpl` (`hourly`) | `hourly` | `weather.hourly_generated_text`; `prompts/hourly.generated_text.md` | The matching schema files are under `internal/reporttemplate/schemas/`. The generated-text catalog pairs each schema ID with its template ID; keep the matching report prompt source aligned with that pair. Shared partials are under `internal/reporttemplate/templates/partials/`: | Partial | Used by | | --- | --- | | `alert_digest.md.tmpl` | Daily, Today, Tomorrow, and Hourly | | `precipitation_timing.md.tmpl` | Daily, Today, Tomorrow, and Hourly | | `daypart_forecast.md.tmpl` | Daily and Tomorrow | | `today_daypart_forecast.md.tmpl` | Today | All shared partials are parsed whenever any top-level template is rendered. A syntax error in a partial can therefore prevent every generated-text report from rendering. ## Editing Rules - Use Go `text/template` syntax and keep changes to Markdown structure, ordering, and display conditions. - Templates use `missingkey=error`; reference only documented fields and guard optional module pointers with `with` or `if`. - Prefer `.Modules` for deterministic display values. Do not add weather calculations, source selection, or prompt-input shaping to a template. - Keep generated prose in `.GeneratedText`; do not restate deterministic facts in generated prose merely to compensate for a template change. - When changing the generated-prose contract, update the matching prompt, schema, validator, render context, and template together. The validation and catalog rules are owned by [Generated Text internals](internal/generatedtext.md). - Use `.Modules.Dayparts` for ordered daypart output. Do not range over `.Modules.DerivedDaypartSummaries`, which is a map. Minimal optional-value pattern: ```gotemplate {{ with .Modules.CurrentConditions }} Currently, it is {{ with .TemperatureF }}{{ . }}°F{{ end }}. {{ else }} Current conditions are unavailable. {{ end }} ``` Minimal list pattern: ```gotemplate {{ range .GeneratedText.ForecastDiscussion }} {{ . }} {{ end }} ``` ## Registered Functions Templates have these helpers in addition to Go template built-ins: | Function | Accepts | Returns true when | | --- | --- | --- | | `hasRelevantAlerts` | an alert-digest value or pointer | its `Relevant` slice is nonempty | | `hasEnhancedOrHigherSPCRisk` | an SPC outlook value or pointer | its `RiskDigest` contains an Enhanced, Moderate, or High Risk entry | | `isEnhancedOrHigherSPCRisk` | one SPC risk-digest entry | its `LabelText`, or fallback `RiskLabel`, is Enhanced, Moderate, or High Risk | For example, the alert partial uses the first two functions to decide whether to render the section: ```gotemplate {{ if hasRelevantAlerts .Modules.AlertDigest }} ## Alert Digest {{ end }} ``` ## Render Context Every rendered template receives one typed context with these five top-level fields: | Field | Purpose | | --- | --- | | `.Report` | Display labels and canonical report timing metadata. | | `.GeneratedText` | Validated prose supplied by Scriptorium. | | `.Modules` | Deterministic, typed values prepared for Markdown rendering. | | `.Collected` | Normalized upstream facts for advanced use. | | `.Derived` | Shared calculated facts for advanced use. | `.Collected` and `.Derived` are available for an exceptional display need, but they are lower-level contracts. Keep reusable weather derivation in Go and use the module surface for normal template work. ### Report Metadata All contexts provide `.Report.Title`, `.Report.GeneratedAt`, `.Report.GeneratedAtLabel`, `.Report.ValidPeriod`, and `.Report.Timezone`. Hourly additionally provides `.Report.LocationName` and `.Report.ValidPeriodLabel`. Daily, Today, and Tomorrow additionally provide `.Report.ForecastDate`, `.Report.ForecastDateLabel`, and `.Report.ForecastDayName`. Their valid-period field remains canonical timing data; use the supplied display labels instead of formatting timestamps in a template. ### Validated GeneratedText Prose GeneratedText is prose returned by Scriptorium and validated before rendering. It is not a source for deterministic weather facts. | Field | Hourly type | Daily, Today, and Tomorrow type | Notes | | --- | --- | --- | --- | | `.GeneratedText.Summary` | `string` | `string` | Required. | | `.GeneratedText.ForecastDiscussion` | `string` | `[]string` | Required; range over the day-style paragraph slice. | | `.GeneratedText.PrecipitationTiming` | `string` | `string` | Optional prose used by the precipitation partial when deterministic windows exist. | | `.GeneratedText.Confidence` | `string` | `string` | Optional validated prose; the current templates do not render it. | The JSON schema rejects unknown properties and defines the required fields, but the schema body and validation behavior are documented in [Generated Text internals](internal/generatedtext.md). ### Deterministic Module Values Module values are deterministic outputs built from collected and derived facts. Module pointers can be nil when their source or policy permits omission. | Module field | Available in | | --- | --- | | `.Modules.Metadata`, `.Modules.CurrentConditions`, `.Modules.HourlyForecast`, `.Modules.PrecipTiming`, `.Modules.AlertDigest`, `.Modules.SPCConvectiveOutlooks`, `.Modules.AreaForecastDiscussion`, `.Modules.SPCConvectiveDiscussion`, `.Modules.WeatherStory` | All four contexts | | `.Modules.DerivedDailySummary`, `.Modules.DerivedDaypartSummaries`, `.Modules.Dayparts` | Daily, Today, Tomorrow | | `.Modules.OutdoorWindows`, `.Modules.DailyPlanning` | Daily | | `.Modules.TodayPlanning` | Today | | `.Modules.TomorrowPlanning` | Tomorrow | The repository templates currently use the following nested display values. They are the preferred surface for comparable edits: | Area | Values | | --- | --- | | Current conditions | `.TemperatureF`, `.ConditionText`, `.ConditionTextLower`, `.ApparentTemperatureF`, `.RelativeHumidityPercent`, `.WindDirectionText`, `.WindSpeedMph` | | Hourly periods | `.Periods`, `.HourLabel`, `.Name`, `.TemperatureF`, `.TextDescription`, `.TextDescriptionLower`, `.MentionPrecipitation`, `.ProbabilityOfPrecipitationPercent` | | Dayparts | `.Dayparts[].Key` and `.Dayparts[].Summary` fields `DisplayName`, `DominantCondition`, `DominantConditionDisplay`, `TemperatureTrend`, `TemperatureStartPhraseF`, `TemperatureEndPhraseF`, `TemperaturePeakPhraseF`, `TemperatureSteadyPhraseF`, `TemperaturePhraseF`, `MentionPrecipitation`, and `MaxPopPercent` | | Precipitation timing | `.PrecipitationWindows`, plus each window's `PeriodBegins`, `PeriodBeginsHourLabel`, `PeriodEnds`, `PeriodEndsHourLabel`, `ExpectationPhrase`, `MaxPopPercent`, `MaxPopTime`, and `MaxPopHourLabel` | | Alert digest | `.AlertDigest.Relevant` entries' `Event`, `Headline`, `PeriodBegins`, and `PeriodEnds` | | SPC risk digest | `.SPCConvectiveOutlooks.RiskDigest` entries' `LabelText`, `RiskLabel`, `PeriodBegins`, and `PeriodEnds` | Other fields on these typed modules remain available when a template has a well-defined display need. Their module contracts and weather derivation belong to [Module contract internals](internal/module.md), [Module builder internals](internal/briefing.md), and [Forecast derivation internals](internal/forecast-derivation.md). ## Validate Changes Run the focused checks after editing templates, partials, prompts, or schemas: ```sh go test ./internal/reporttemplate ./internal/generatedtext ./internal/app git diff --check ``` The render-context and template tests cover Daily, Today, Tomorrow, and Hourly contexts. Run the repository-wide test suite before merging a broader change.