# Fact Contracts Internals This document describes the fact contract boundary. ## Purpose `internal/facts` separates normalized upstream facts collected for a report run from conservative report-scoped facts derived from them. The package gives app orchestration one place to build reusable facts before module execution. ## Inputs And Outputs Inputs: - `weatherdata.Bundle` from the Weather API adapter - resolved report definition and valid period - report timezone - configured daypart definitions Outputs: - `facts.CollectedFacts` with normalized source facts plus separate source provenance and warnings. SPC convective outlook source data is carried through when present in the bundle. - `facts.DerivedFacts` with valid-period forecast slices, alert overlaps, report-period SPC convective outlooks and discussions, daily summaries, daypart summaries, and Storm Report window summary ## Boundaries - This package owns fact assembly and reusable deterministic derivation for a report run. - SPC convective outlook derivation selects already-collected outlooks whose half-open valid intervals overlap the resolved report period and retains discussions for represented outlook days. - It does not fetch upstream data, build prompt wording, compare prior snapshots, write workflow state, invoke Scriptorium, or define modules. ## Config Fields Used - `dayparts[].name` - `dayparts[].start` - `dayparts[].end` - `weather_api.timezone` ## External Adapters Used None directly. Collected facts are built from `weatherdata.Bundle`. ## State Or Manifest Behavior None. Source provenance and warnings remain data fields for downstream metadata and inspection. ## Failure Behavior - Invalid or missing report valid periods return an error. - Invalid timezone names return an error. - Missing required hourly forecast data returns the underlying forecast derivation error for reports that require daily summaries. - Missing optional narrative, alert, discussion, daily, or weather story data produces empty or nil derived fields. - Missing optional SPC convective outlook data produces a nil collected field. - A present SPC convective outlook source with no report-period matches produces non-nil empty derived outlook and discussion slices. ## Tests Inspect: - `internal/facts/facts_test.go` - `internal/app/app_test.go` ## Invariants - Collected facts are built once from a fetched bundle. - Derived facts are scoped to one resolved report. - SPC convective outlook selection uses the resolved report period, not server-current active filtering. - Source provenance and warnings stay separate from ordinary fact fields. - Prompt-specific wording and one-off presentation decisions stay outside this package.