Refresh deterministic domain documentation

This commit is contained in:
2026-07-31 01:29:48 +00:00
parent f9eef80233
commit 9303502b32
4 changed files with 146 additions and 200 deletions

View File

@@ -1,75 +1,61 @@
# Changes Internals # Changes Internals
This document describes structured Recent Changes comparison. `internal/changes` deterministically compares a compatible prior module
snapshot with the current snapshot. It returns compact structured changes for
prompt input; it never reads state, finds a prior report, renders Markdown, or
compares generated text. Snapshot construction belongs to
[module internals](module.md), and prior-snapshot discovery belongs to
[state internals](state.md).
## Purpose ## Comparison inputs and output
`internal/changes` compares current and prior module snapshots and emits Each comparator receives a prior snapshot, a current snapshot, and
compact change records for prompt input data packages. `Thresholds`. A `Change` has a stable type and message plus previous and
current values where useful. Changes are sorted by type and then message, so
the same inputs always yield the same order.
## Inputs And Outputs Threshold values are supplied by application orchestration from the
[Recent Changes configuration](../config.md#recent_change); this package does
not load configuration or choose defaults. Numeric changes are emitted when
the absolute difference meets the configured threshold. Precipitation also
requires a change between its low, possible, likely, and high categories.
Inputs: ## Strategies
- prior module snapshot | Comparator | Required snapshot data | Compared values |
- current module snapshot | --- | --- | --- |
- comparison thresholds from configuration | `CompareDaily` | `derived_daily_summary`, `derived_daypart_summaries` | Low and high temperature, daily precipitation probability and timing, peak gust, alerts, and aggregate indicators |
| `CompareThreeDay` | `derived_daypart_summaries` | Per-day temperatures, precipitation probability and timing, peak gust, indicators, and added or removed outlook days |
| `CompareWeekend` | `derived_daypart_summaries` | The three-day values with weekend-prefixed change types |
Outputs: For daily comparison, `alert_digest` and `precip_timing` are optional: alerts
are compared when present, and timing is compared only when both snapshots
contain it. The multi-day comparators build their day map from daypart
summaries. A missing or added day becomes a dedicated change rather than a
comparison against invented data.
- ordered `changes.Change` items with type, message, previous value, and current The application selects a comparator only after state lookup establishes a
value where useful compatible prior snapshot. Daily, Today, and Tomorrow use the daily comparator;
Three-day and Weekend use their named comparators. Other report types, such as
Storm, produce no Recent Changes list.
## Boundaries ## Missing data and failures
- This package compares structured module snapshot data only. Required stanzas that are absent or cannot be decoded return an error with the
- It does not read filesystem state, find prior snapshots, render Markdown, snapshot and stanza context. Optional stanzas may be absent. A snapshot with no
invoke Scriptorium, or compare generated report text. eligible predecessor is not a comparison failure: the caller supplies an empty
change list without invoking this package.
## Config Fields Used The package has no filesystem, transport, CLI, renderer, or persistence
behavior. It does not decide report compatibility or retain snapshots.
The app maps these fields into comparison thresholds: ## Verification and invariants
- `recent_change.temperature_degrees` Focused tests cover the daily, three-day, and weekend strategies, threshold
- `recent_change.precip_probability_points` boundaries, indicator and alert changes, and missing required stanzas:
- `recent_change.wind_gust_miles_per_hour`
- `recent_change.precip_timing_shift_minutes`
## External Adapters Used ```sh
go test ./internal/changes
```
None. Recent Changes always compare structured snapshot values, never report prose.
## State Or Manifest Behavior
None directly. The app loads prior module snapshots through `internal/state`
before calling comparison functions.
## Skip And Resume Behavior
No resume behavior. When the app has no prior comparable snapshot, it sends an
empty Recent Changes list without calling a comparison function.
## Failure Behavior
- Daily comparison requires `derived_daily_summary` and
`derived_daypart_summaries` stanzas. It also uses `alert_digest` and
`precip_timing` when present.
- 3-Day comparison requires `derived_daypart_summaries`.
- Weekend comparison requires `derived_daypart_summaries`.
- Storm Report comparison returns no changes.
## Tests
Inspect:
- `internal/changes/daily_test.go`
- `internal/changes/three_day_test.go`
- `internal/changes/weekend_test.go`
- `internal/app/app_test.go`
## Invariants
- Recent Changes are based on structured snapshots, not Markdown report text.
- Report compatibility is determined outside this package by report definitions
and state lookup.
- Output stays compact enough for prompt input.

View File

@@ -1,94 +1,67 @@
# Fact Contracts Internals # Fact Contracts Internals
This document describes the fact contract boundary. `internal/facts` is the deterministic boundary between a collected weather
bundle and report-scoped facts. It preserves normalized source values and then
selects and summarizes the values needed for one resolved report. Provider
transport and normalized bundle semantics belong to
[weather-data internals](weather-data.md); report identity and valid-period
selection belong to [report registry internals](report-registry.md).
## Purpose ## Collected facts
`internal/facts` separates normalized upstream facts collected for a report run `BuildCollected` projects a `weatherdata.Bundle` into `CollectedFacts`. It
from conservative report-scoped facts derived from them. The package gives app retains the fetched timestamp and every normalized product: observations,
orchestration one place to build reusable facts before module execution. current conditions, hourly, narrative, alerts, discussion, daily, weather
story, and convective outlook data. Source provenance and warnings are copied
into their own slices so downstream consumers can inspect data completeness
without treating it as an ordinary weather fact.
## Inputs And Outputs A nil bundle produces an empty collected value. Collection itself, missing
source policy, and source hashes are outside this package.
Inputs: ## Report-scoped derivation
- `weatherdata.Bundle` from the Weather API adapter `BuildDerived` requires a valid resolved period and a valid report timezone. It
- resolved report definition and valid period uses half-open period overlap to select hourly, narrative, daily, and alert
- report timezone data; it also derives precipitation timing. Convective outlooks are retained
- configured daypart definitions only when their valid interval overlaps the report period, with discussions
kept for represented outlook days. Both collections are sorted deterministically.
Outputs: Report identity controls the summary shape:
- `facts.CollectedFacts` with normalized source facts plus separate source | Report family | Derived summary |
provenance and warnings. SPC convective outlook source data is carried | --- | --- |
through when present in the bundle, including upstream geometry and source | Hourly | Rolling-period selections and precipitation timing; no daily or daypart summary |
provenance. | Daily, Today, Tomorrow | One local civil-day summary and its dayparts |
- `facts.DerivedFacts` with valid-period forecast slices, alert overlaps, | Three-day, Weekend | One clipped daily summary for each overlapping local day |
report-period SPC convective outlooks and discussions, daily summaries, | Storm | One summary for the explicit report window |
daypart summaries, and Storm Report window summary
Hourly Report uses the generic valid-period hourly and narrative selection `DaypartSummaries` is collected from the resulting daily or storm summaries.
for its rolling six-hour window. Its derived facts include precipitation timing The detailed grouping, daypart-window, and alert rules are owned by
from the selected hourly periods, alert overlaps for the six-hour period, and [forecast derivation](forecast-derivation.md).
SPC outlooks/discussions overlapping that period. It does not build daily
summaries, daypart summaries, or a storm-window summary.
## Boundaries ## Missing data and failures
- This package owns fact assembly and reusable deterministic derivation for a Optional normalized products remain nil or yield empty selections; the package
report run. does not create substitute values. A present convective-outlook run with no
- SPC convective outlook derivation selects already-collected outlooks whose matching outlooks produces non-nil empty outlook and discussion slices, while
half-open valid intervals overlap the resolved report period and retains a missing run produces nil slices.
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 Derivation fails for an invalid report period, invalid timezone, unsupported
report ID, or when a requested daily summary has no hourly forecast data.
Invalid daypart definitions surface from forecast derivation. The package does
not access the CLI, filesystem, subprocesses, or network.
- `dayparts[].name` ## Verification and invariants
- `dayparts[].start`
- `dayparts[].end`
- `weather_api.timezone`
## External Adapters Used Focused tests cover collected-fact separation, report-period selection,
hourly and storm behavior, daily and partial-day summaries, and convective
outlook selection:
None directly. Collected facts are built from `weatherdata.Bundle`. ```sh
go test ./internal/facts
```
## State Or Manifest Behavior Facts are derived once for a resolved report from already collected data.
They remain reusable structured values: prompt wording, state persistence,
None. Source provenance and warnings remain data fields for downstream metadata prior-report comparison, and template presentation are owned elsewhere.
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.
- Hourly 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.

View File

@@ -1,76 +1,63 @@
# Forecast Derivation Internals # Forecast Derivation Internals
This document describes deterministic forecast summarization in `internal/forecast` deterministically selects and summarizes normalized
`internal/forecast`. forecast data. It has no transport, filesystem, CLI, subprocess, or report
registry dependency. Its summaries are consumed by
[fact contracts](facts.md) and later module builders.
## Purpose ## Period and daypart semantics
`internal/forecast` converts normalized weather data into daily and period Selections use `timeutil.Period` half-open overlap: a value is selected only
summaries used by fact builders and module builders. when both intervals share time. `BuildDailySummary` creates one local civil
day; `BuildPeriodDailySummaries` intersects every local civil day with the
requested period, preserving partial first and last days.
## Inputs And Outputs `ResolveDayparts` converts each configured name, start clock, and end clock
into a local window. An end clock at or before its start clock wraps into the
next civil day. The daypart and timezone defaults are defined in the
[configuration reference](../config.md), not here.
Inputs: ## Deterministic summaries
- `weatherdata.Bundle` `BuildDailySummary` requires an hourly run with at least one period. It adds
- local date or resolved report period the selected narrative periods, discussion, alert overlaps, source provenance,
- timezone source warnings, and one `DaypartSummary` per resolved window. A daypart keeps
- configured daypart definitions its selected hourly periods and derives temperature and apparent-temperature
ranges, timed precipitation and wind maxima, dominant and notable conditions,
and weather indicators.
Outputs: Indicators are deterministic checks over normalized values and condition text:
heat, cold, and wind use package-owned numeric cutoffs; snow, ice, fog, and
wind text are detected from the forecast description. `BuildPrecipTiming`
sorts periods, records the maximum and first precipitation, groups contiguous
periods at or above its package-owned probability threshold, and records
thunder mentions.
- `forecast.DailySummary` for one local civil day Alert overlap parsing supports the normalized alert payload's available timing
- one clipped daily summary per local day or partial day from fields. Unparseable alerts and invalid intervals are ignored; valid overlaps
`BuildPeriodDailySummaries` are clipped to the requested period and ordered by alert start time.
- daypart summaries with selected hourly periods, ranges, timed maximums,
conditions, indicators, and alert overlaps
## Boundaries ## Missing data and failures
- This package groups, selects, and summarizes already-normalized forecast Empty selections yield empty summary fields rather than generated prose.
data. Direct daily or period-summary calls fail when their required bundle, valid
- It does not perform HTTP calls, parse CLI flags, resolve report definitions, period, hourly data, or daypart definitions are invalid. A nil location uses
compare prior snapshots, build prompt input packages, or invoke Scriptorium. UTC when these APIs are called directly. Optional narrative, discussion, and
alerts remain absent when their normalized products are absent.
## Config Fields Used Forecast thresholds used for brief indicators and precipitation timing are
implementation rules. User-configurable Recent Changes thresholds are applied
by [changes internals](changes.md), whose defaults are documented in
[configuration](../config.md).
- `dayparts[].name` ## Verification and invariants
- `dayparts[].start`
- `dayparts[].end`
Threshold constants for basic indicators live in forecast code rather than Focused tests cover local civil days, clipped periods, daypart resolution,
configuration. summary metrics, precipitation windows, threshold helpers, and alert overlap:
## External Adapters Used ```sh
go test ./internal/forecast ./internal/timeutil
```
None directly. Forecast data arrives through `weatherdata.Bundle`. The package preserves normalized inputs as inspectable structured values and
never decides report identity, delivery, or presentation wording.
## State Or Manifest Behavior
None. Source warnings and provenance from the bundle are carried into summaries
for later metadata and module output.
## Skip And Resume Behavior
None. Missing optional source context can produce empty selections, but missing
required hourly data fails summarization.
## Failure Behavior
- A nil bundle or missing hourly forecast data returns an error.
- Invalid daypart definitions return parse errors with context.
- Alert records without parseable RFC3339 timing are skipped.
- Empty selected periods produce empty summaries rather than generated prose.
## Tests
Inspect:
- `internal/forecast/derive_test.go`
- `internal/timeutil/periods_test.go`
## Invariants
- Go owns report-period selection and meteorological summarization.
- Weather facts come from normalized source data.
- Outputs remain JSON-inspectable and independent of CLI, state, and adapters.

View File

@@ -26,10 +26,10 @@ the paths safe to use below the configured workspace.
| Generated-text source and result | `snapshots/<group>/<date>/generated_text_raw.<run-id>.json` and `generated_text_result.<run-id>.json` | | Generated-text source and result | `snapshots/<group>/<date>/generated_text_raw.<run-id>.json` and `generated_text_result.<run-id>.json` |
| Generated-text render context | `snapshots/<group>/<date>/render_context.<run-id>.json` | | Generated-text render context | `snapshots/<group>/<date>/render_context.<run-id>.json` |
Notification records use their own configured date directory, because they The configured notification root separates notification artifacts from report
are not necessarily tied to a report valid period. Report producers create artifacts; single-report notification paths use the report's valid date. Report
parent directories as needed and write the report body; state is responsible producers create parent directories as needed and write the report body; state
for the surrounding paths and saved run artifacts. is responsible for the surrounding paths and saved run artifacts.
Batch Distributor notifications are derived separately as Batch Distributor notifications are derived separately as
`notifications/batches/<batch>/<local-date>/distributor.<batch-run-id>.json`. `notifications/batches/<batch>/<local-date>/distributor.<batch-run-id>.json`.