# 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, including upstream geometry and source provenance. - `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 Near-Term Report uses the generic valid-period hourly and narrative selection for its rolling six-hour window. Its derived facts include precipitation timing from the selected hourly periods, alert overlaps for the six-hour period, and SPC outlooks/discussions overlapping that period. It does not build daily summaries, daypart summaries, or a storm-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. - Derived SPC outlook records preserve the collected outlook fields, including geometry, for downstream components that need source-level facts. Prompt modules decide which fields are exposed to Scriptorium. - 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. - Near-Term Report can derive its default module facts without daily or daypart 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 and the already-collected outlook run. - Source provenance and warnings stay separate from ordinary fact fields. - Prompt-specific wording and one-off presentation decisions stay outside this package.