2026-06-14 08:22:09 -05:00
# Report Templates
2026-07-31 01:07:32 +00:00
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 |
| --- | --- | --- | --- |
2026-07-31 03:53:47 +00:00
| Daily | `templates/daily.md.tmpl` (`daily` ) | `daily` | `weather.daily_generated_text` ; `internal/promptassets/assets/prompts/daily/` |
| Today | `templates/today.md.tmpl` (`today` ) | `today` | `weather.today_generated_text` ; `internal/promptassets/assets/prompts/today/` |
| Tomorrow | `templates/tomorrow.md.tmpl` (`tomorrow` ) | `tomorrow` | `weather.tomorrow_generated_text` ; `internal/promptassets/assets/prompts/tomorrow/` |
| Hourly | `templates/hourly.md.tmpl` (`hourly` ) | `hourly` | `weather.hourly_generated_text` ; `internal/promptassets/assets/prompts/hourly/` |
2026-07-31 01:07:32 +00:00
2026-07-31 03:53:47 +00:00
The matching schemas and Promptkit definitions are embedded by
2026-08-13 01:36:56 +00:00
`internal/promptassets` . The generated-text catalog requires each report's
exact schema/template pair; keep the matching prompt definition aligned with
that report-specific triple.
2026-07-31 01:07:32 +00:00
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.
2026-06-14 08:22:09 -05:00
## Editing Rules
2026-07-31 01:07:32 +00:00
- 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.
2026-08-13 02:17:11 +00:00
- Render every `.GeneratedText` value through `plainText` . It preserves prose
and paragraph breaks while escaping Markdown and HTML syntax, removing code
indentation, and replacing control characters. Never interpolate generated
prose directly: repository templates alone own headings, lists, links, and
other Markdown structure.
2026-07-31 01:07:32 +00:00
- 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
2026-08-13 02:11:25 +00:00
`.Modules.DerivedDaypartSummaries` , which is a map. The Today partial uses
`.Modules.HasDaypartDetails` to ensure its heading has either rows or the
explicit no-details fallback.
2026-07-31 01:07:32 +00:00
Minimal optional-value pattern:
2026-06-14 08:22:09 -05:00
```gotemplate
2026-07-31 01:07:32 +00:00
{{ with .Modules.CurrentConditions }}
Currently, it is {{ with .TemperatureF }}{{ . }}°F{{ end }}.
{{ else }}
Current conditions are unavailable.
{{ end }}
2026-06-14 08:22:09 -05:00
```
2026-07-31 01:07:32 +00:00
Minimal list pattern:
2026-06-15 16:54:41 +00:00
2026-07-31 01:07:32 +00:00
```gotemplate
{{ range .GeneratedText.ForecastDiscussion }}
2026-08-13 02:17:11 +00:00
{{ plainText . }}
2026-07-31 01:07:32 +00:00
{{ end }}
```
2026-06-16 15:45:23 +00:00
2026-07-31 01:07:32 +00:00
## Registered Functions
2026-06-15 16:54:41 +00:00
2026-07-31 01:07:32 +00:00
Templates have these helpers in addition to Go template built-ins:
2026-06-15 16:54:41 +00:00
2026-07-31 01:07:32 +00:00
| Function | Accepts | Returns true when |
2026-06-15 16:54:41 +00:00
| --- | --- | --- |
2026-07-31 01:07:32 +00:00
| `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 |
2026-08-13 02:17:11 +00:00
| `plainText` | a generated prose string | a readable plain-text rendering that preserves paragraph breaks without allowing dynamic Markdown or HTML structure |
2026-06-15 16:54:41 +00:00
2026-07-31 01:07:32 +00:00
For example, the alert partial uses the first two functions to decide whether
to render the section:
2026-06-15 16:54:41 +00:00
2026-07-31 01:07:32 +00:00
```gotemplate
{{ if hasRelevantAlerts .Modules.AlertDigest }}
## Alert Digest
{{ end }}
```
2026-06-15 14:45:49 +00:00
2026-07-31 01:07:32 +00:00
## Render Context
2026-06-15 14:45:49 +00:00
2026-08-13 02:11:25 +00:00
Every rendered template receives one typed context with these three top-level
2026-07-31 01:07:32 +00:00
fields:
2026-06-15 14:45:49 +00:00
2026-07-31 01:07:32 +00:00
| Field | Purpose |
| --- | --- |
| `.Report` | Display labels and canonical report timing metadata. |
2026-07-31 05:03:02 +00:00
| `.GeneratedText` | Validated prose supplied by Promptkit. |
2026-07-31 01:07:32 +00:00
| `.Modules` | Deterministic, typed values prepared for Markdown rendering. |
2026-06-16 15:45:23 +00:00
2026-07-31 01:07:32 +00:00
### Report Metadata
2026-06-15 15:04:14 +00:00
2026-07-31 01:07:32 +00:00
All contexts provide `.Report.Title` , `.Report.GeneratedAt` ,
`.Report.GeneratedAtLabel` , `.Report.ValidPeriod` , and `.Report.Timezone` .
2026-06-15 14:45:49 +00:00
2026-07-31 01:07:32 +00:00
Hourly additionally provides `.Report.LocationName` and
`.Report.ValidPeriodLabel` .
2026-06-15 14:45:49 +00:00
2026-07-31 01:07:32 +00:00
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.
2026-06-15 14:45:49 +00:00
2026-07-31 01:07:32 +00:00
### Validated GeneratedText Prose
2026-06-14 08:57:53 -05:00
2026-07-31 05:03:02 +00:00
GeneratedText is prose returned by Promptkit and validated before rendering.
2026-07-31 01:07:32 +00:00
It is not a source for deterministic weather facts.
2026-06-14 08:22:09 -05:00
2026-07-31 01:07:32 +00:00
| Field | Hourly type | Daily, Today, and Tomorrow type | Notes |
| --- | --- | --- | --- |
2026-08-13 01:47:47 +00:00
| `.GeneratedText.Summary` | `string` | `string` | Required; at most 4,000 characters. |
| `.GeneratedText.ForecastDiscussion` | `string` | `[]string` | Required; Hourly permits 12,000 characters. Day-style values permit up to 12 paragraphs of 4,000 characters each. |
| `.GeneratedText.PrecipitationTiming` | `string` | `string` | Required field, at most 4,000 characters; an empty string represents no supported prose. The precipitation partial uses nonempty prose only when deterministic windows exist. |
2026-06-15 20:46:11 +00:00
2026-07-31 01:07:32 +00:00
The JSON schema rejects unknown properties and defines the required fields, but
the schema body and validation behavior are documented in [Generated Text
2026-08-13 01:47:47 +00:00
internals](internal/generatedtext.md). All validated generated prose together
is limited to 20,000 characters, so template edits can rely on a bounded prose
surface.
2026-06-14 08:22:09 -05:00
2026-07-31 01:07:32 +00:00
### Deterministic Module Values
2026-06-14 08:22:09 -05:00
2026-07-31 01:07:32 +00:00
Module values are deterministic outputs built from collected and derived facts.
Module pointers can be nil when their source or policy permits omission.
2026-06-14 08:22:09 -05:00
2026-07-31 01:07:32 +00:00
| Module field | Available in |
| --- | --- |
2026-08-13 02:11:25 +00:00
| `.Modules.CurrentConditions` , `.Modules.HourlyForecast` , `.Modules.PrecipTiming` , `.Modules.AlertDigest` , `.Modules.SPCConvectiveOutlooks` , `.Modules.AreaForecastDiscussion` , `.Modules.SPCConvectiveDiscussion` , `.Modules.WeatherStory` | All four contexts |
2026-07-31 01:07:32 +00:00
| `.Modules.DerivedDailySummary` , `.Modules.DerivedDaypartSummaries` , `.Modules.Dayparts` | Daily, Today, Tomorrow |
2026-08-13 02:11:25 +00:00
| `.Modules.HasDaypartDetails` | Today |
2026-07-31 01:07:32 +00:00
| `.Modules.OutdoorWindows` , `.Modules.DailyPlanning` | Daily |
| `.Modules.TodayPlanning` | Today |
| `.Modules.TomorrowPlanning` | Tomorrow |
2026-06-14 08:22:09 -05:00
2026-07-31 01:07:32 +00:00
The repository templates currently use the following nested display values.
They are the preferred surface for comparable edits:
2026-06-14 08:22:09 -05:00
2026-07-31 01:07:32 +00:00
| 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` |
2026-06-14 08:22:09 -05:00
2026-07-31 01:07:32 +00:00
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).
2026-06-14 08:22:09 -05:00
2026-07-31 01:07:32 +00:00
## Validate Changes
2026-06-14 08:22:09 -05:00
2026-07-31 01:07:32 +00:00
Run the focused checks after editing templates, partials, prompts, or schemas:
2026-06-14 08:22:09 -05:00
2026-07-31 01:07:32 +00:00
```sh
2026-06-14 08:22:09 -05:00
go test ./internal/reporttemplate ./internal/generatedtext ./internal/app
git diff --check
```
2026-07-31 01:07:32 +00:00
The render-context and template tests cover Daily, Today, Tomorrow, and Hourly
contexts. Run the repository-wide test suite before merging a broader change.