# Generated Text Internals This document describes structured generated-text handling in `internal/generatedtext`. ## Purpose `internal/generatedtext` validates structured text returned for generated-text-template reports and builds curated render contexts for templates. It also owns the generated-text catalog that connects report definitions to validators, render-context builders, schema assets, and template assets. ## Inputs And Outputs Inputs: - raw GeneratedText JSON for Daily, Today, Tomorrow Report, or Hourly Report - report metadata from `internal/briefing` - a module snapshot from `internal/module` - validated generated text Outputs: - typed `Daily` generated text - typed `Today` generated text - typed `Tomorrow` generated text - typed `Hourly` generated text - normalized stable JSON for validated generated text - typed `DailyRenderContext` values for `internal/reporttemplate` - typed `TodayRenderContext` values for `internal/reporttemplate` - typed `TomorrowRenderContext` values for `internal/reporttemplate` - typed `HourlyRenderContext` values for `internal/reporttemplate` - generated-text catalog handlers for report definitions that use `generated_text_template` ## JSON Contracts Daily, Today, and Tomorrow use the same day-style generated-text JSON shape: ```json { "summary": "string", "forecast_discussion": ["string"], "precipitation_timing": "string", "confidence": "string" } ``` The day-style contract requires `summary` after trimming whitespace. `forecast_discussion` must contain at least one nonblank paragraph after trimming blank items. `precipitation_timing` and `confidence` are optional and omitted from normalized JSON when blank. Unknown fields are rejected. The report-specific Go API is: | Report | Type | Validator | Schema ID | Template ID | Prompt ID | | --- | --- | --- | --- | --- | --- | | Daily Report | `Daily` | `ValidateDaily` | `daily` | `daily` | `weather.daily_generated_text` | | Today Report | `Today` | `ValidateToday` | `today` | `today` | `weather.today_generated_text` | | Tomorrow Report | `Tomorrow` | `ValidateTomorrow` | `tomorrow` | `tomorrow` | `weather.tomorrow_generated_text` | Hourly generated text uses the same top-level field names, but `forecast_discussion` is a single string: ```json { "summary": "string", "forecast_discussion": "string", "precipitation_timing": "string", "confidence": "string" } ``` Hourly `summary` and `forecast_discussion` are required after trimming whitespace. `precipitation_timing` and `confidence` are optional and omitted from normalized JSON when blank. Unknown fields are rejected. The Hourly catalog entry uses type `Hourly`, validator `ValidateHourly`, schema ID `hourly`, template ID `hourly`, and prompt ID `weather.hourly_generated_text`. ## Render Contexts Daily, Today, Tomorrow, and Hourly render contexts all include: - display metadata derived from report metadata; - validated generated text; - typed module outputs decoded from the module snapshot; - collected facts; - derived facts. Daily, Today, and Tomorrow share common civil-day render-context fields such as forecast date labels, valid period, generated-at labels, current conditions, hourly forecast, precipitation timing, alert digest, SPC outlooks, AFD, SPC discussion, weather story, daily summary, and ordered daypart summaries. Each civil-day report keeps its report-specific planning module: - Daily exposes `DailyPlanning`. - Today exposes `TodayPlanning`. - Tomorrow exposes `TomorrowPlanning`. Today's ordered daypart context omits unavailable or elapsed dayparts according to Today report rules. Daily and Tomorrow use fallback daypart behavior. ## Boundaries - This package owns typed generated-text validation and render-context shaping. - It owns generated-text catalog lookup for schema/template combinations. - It uses typed module snapshot decoding through `module.StanzaValue`. - It does not invoke Scriptorium, write state artifacts, choose report definitions, compare snapshots, or own embedded template/schema files. - It renders through `internal/reporttemplate`; embedded asset lookup remains in `internal/reporttemplate`. - It does not use a Go JSON Schema dependency; schema enforcement in Go is limited to typed JSON decoding, unknown-field rejection, and required-field checks. ## Failure Behavior - Malformed generated-text JSON fails with decode context. - Unknown generated-text JSON fields fail during decoding. - Empty required fields fail after trimming whitespace. - Daily, Today, and Tomorrow forecast discussion fails when no nonblank paragraphs remain. - Missing optional render-context stanzas become nil module pointers. - Invalid render metadata, including missing timezone, missing generated time, or invalid valid period, fails before template rendering. - Unsupported generated-text schema IDs, template IDs, or schema/template combinations fail during catalog lookup with report ID context. ## Tests Inspect: - `internal/generatedtext/hourly_test.go` - `internal/generatedtext/daily_test.go` - `internal/generatedtext/today_test.go` - `internal/generatedtext/tomorrow_test.go` - `internal/generatedtext/catalog_test.go` - `internal/generatedtext/render_context_test.go` ## Invariants - Render contexts are curated structs, not raw prompt-input packages. - Required generated text is normalized before downstream artifact storage. - Generated-text-template reports must have one catalog entry matching their report definition schema and template IDs. - Missing optional weather narrative stanzas produce empty or fallback render context fields rather than forcing raw module data into templates.