# 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. The implemented contracts are Tomorrow Report and Hourly Report. ## Inputs And Outputs Inputs: - raw GeneratedText JSON for Tomorrow Report or Hourly Report - report metadata from `internal/briefing` - a module snapshot from `internal/module` - validated generated text Outputs: - typed `Tomorrow` generated text - typed `Hourly` generated text - normalized stable JSON for validated generated text - typed `TomorrowRenderContext` values for `internal/reporttemplate` - typed `HourlyRenderContext` values for `internal/reporttemplate` - generated-text catalog handlers for report definitions that use `generated_text_template` The hourly generated text JSON accepts: ```json { "summary": "string", "forecast_discussion": "string", "precipitation_timing": "string", "confidence": "string" } ``` `summary` and `forecast_discussion` are required after trimming whitespace. `precipitation_timing` and `confidence` are optional and omitted from normalized JSON when blank. The Tomorrow generated text JSON accepts: ```json { "summary": "string", "forecast_discussion": ["string"], "precipitation_timing": "string", "confidence": "string" } ``` `summary` is required 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. ## 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. - 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/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.