# Module Contract Internals This document describes the module contract in `internal/module`. ## Purpose `internal/module` defines the shared identifiers and data envelopes used for prompt-facing modules. Report definitions use module IDs for composition, module builders produce outputs with stanza names, prompt input packages consume snapshots, and Recent Changes compares snapshot stanzas. ## Inputs And Outputs Inputs: - ordered `module.ConfigItem` values from report definitions or config overrides - `module.Output` values produced by module builders Outputs: - stable `module.ID` constants - typed option structs for registered modules - `module.Snapshot` with schema version `weatherreporter.modules.v1` - ordered snapshot outputs with module ID, stanza name, and typed value - typed stanza lookup through `module.StanzaValue` ## Registered Module IDs The registry recognizes these IDs: - `metadata` - `current_conditions` - `narrative_forecast` - `hourly_forecast` - `derived_daily_summary` - `derived_daypart_summaries` - `precip_timing` - `alert_digest` - `spc_convective_outlooks` - `area_forecast_discussion` - `spc_convective_discussion` - `weather_story` - `outdoor_windows` - `tomorrow_planning` Every registered module has a builder. Report composition entries that refer to unknown or unimplemented module IDs fail validation instead of being skipped. ## Options Most modules use an empty options struct, including `spc_convective_outlooks` and `spc_convective_discussion`. `area_forecast_discussion` accepts: ```yaml sections: - product - key_messages - short_term - long_term ``` An omitted or empty `sections` list includes all available discussion sections. Invalid option shapes fail during config normalization or composition validation. ## SPC Convective Module Outputs `spc_convective_outlooks` emits a prompt-facing risk-product stanza with: - `checked` - `as_of` - `issued_at` - `location_id` - `location_name` - `outlook_count` - `outlooks` Each outlook entry may include `day`, `outlook_type`, `label`, `label_text`, `period_begins`, `period_ends`, `issued_at`, `contains_location`, and `image_url`. It omits GeoJSON geometry, source URL, expiration time, and severity rank. `spc_convective_discussion` emits a narrative stanza only when a retained report-period categorical outlook has severity rank `3` or higher and matching discussion text is available. Its output includes `included_because` and `discussions`; each discussion may include `day`, `period_begins`, `period_ends`, `headline`, `summary`, `discussion`, and `updated_at`. Discussions are included only for SPC days whose retained categorical outlooks meet the severity threshold. ## Boundaries - This package owns module identifiers, config item envelopes, output envelopes, snapshot validation, and typed stanza lookup. - It does not define report IDs, execute builders, fetch weather data, derive forecast facts, write state, or invoke Scriptorium. ## State Or Manifest Behavior `module.Snapshot` values are persisted by `internal/state` as JSON. Snapshot validation rejects missing schema version, missing module IDs, missing stanza names, duplicate module outputs, and duplicate stanza names while preserving output order. ## Failure Behavior - Snapshot construction fails for duplicate module outputs or duplicate stanza names. - Typed stanza lookup returns `found=false` for missing stanzas. - Typed stanza lookup wraps JSON marshal/decode failures with stanza context. ## Tests Inspect: - `internal/module/module_test.go` - `internal/briefing/modules_test.go` - `internal/report/period_test.go` ## Invariants - `internal/module` does not import `internal/report`. - Module IDs are stable strings. - Each emitted module output has exactly one stanza name and one typed value. - Snapshot output order is caller-owned and preserved.