# 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 - `facts.DerivedFacts` with valid-period forecast slices, alert overlaps, daily summaries, daypart summaries, and Storm Report window summary ## Boundaries - This package owns fact assembly and reusable deterministic derivation for a report run. - 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. ## 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. - Source provenance and warnings stay separate from ordinary fact fields. - Prompt-specific wording and one-off presentation decisions stay outside this package.