Reconcile internal implementation guides

This commit is contained in:
2026-08-13 04:25:40 +00:00
parent b985c5faac
commit 88fde0df7f
4 changed files with 103 additions and 143 deletions

View File

@@ -1,59 +1,36 @@
# Collection Internals
`internal/collect` is the application-facing boundary for collecting the
`internal/collect` is the small application-facing boundary that obtains one
normalized Weather API bundle. The external HTTP contract belongs in the
[Weather API integration guide](../integrations/weatherapi.md); normalized data
semantics belong in [weather-data internals](weather-data.md).
[Weather API integration guide](../integrations/weatherapi.md); normalized
source values belong in [weather-data internals](weather-data.md).
## Contract
`Run` accepts a `context.Context` and a `Request` containing effective
`config.Config`. It constructs the Weather API adapter from that configuration,
calls `FetchBundle`, and returns `Result{Bundle: *weatherdata.Bundle}`.
`Run` receives a context and effective configuration in `Request`. It creates
the Weather API adapter, calls `FetchBundle`, and returns the adapter's
normalized bundle in `Result`. Adapter construction errors are wrapped as
weather-collection setup errors and fetch errors as bundle-collection errors.
The adapter's successful readiness request for current conditions is reused as
that normalized source; collection does not trigger a second identical current
conditions request.
The package neither chooses reports nor derives facts, builds modules, invokes
Promptkit, writes files, or sends notifications. Request scheduling, endpoint
retrieval, response limits, and source-level warnings belong to the Weather
API adapter and its integration contract.
After readiness succeeds, the adapter obtains the other independent source
responses concurrently. It merges their normalized results in the established
source order, so provenance, warnings, and source-local failures remain
deterministic. Cancellation remains authoritative for every in-flight request.
## Application Use
The package wraps adapter construction failures, including an invalid Weather
API base URL, as weather-collection setup errors and fetch failures as
bundle-collection errors. It does not retry, persist, select reports, derive
facts, build modules, invoke Promptkit, or notify Distributor.
`internal/app` owns the `Collector` interface used by report workflows and
tests. Its default implementation delegates to `collect.Run`; callers may
substitute a collector at that boundary. Application orchestration owns
collection timing, reuse across a workflow, and the handling of nil collection
results. See [app orchestration internals](app-orchestration.md) for that
flow.
Fetch failures retain Weather API endpoint context but do not project upstream
response bodies into application-facing errors. Oversized response bodies fail
collection before source decoding.
## Verification
The required hourly product must contain one or more periods with usable time
bounds. An invalid hourly product fails collection before derivation begins.
Focused package tests cover a successful fetch and wrapping failures from
adapter construction and bundle retrieval:
## Application Composition
`internal/app` owns the narrow `Collector` interface used by workflow tests;
the production implementation delegates to `collect.Run`. Generation, batch
execution, and explicit bundle fetching all use this boundary. Application
orchestration rejects a nil collector result or a nil bundle before report work
can continue.
Single-report generation and a batch each collect once. A batch passes the same
normalized collection to planning and to every report it generates. Collection
failure prevents later workflow work for that request.
## Boundaries And Invariants
Collection owns adapter creation and retrieval of one normalized bundle. It
must not make report, period, batch, prompt, module, filesystem, or notification
decisions.
- App-facing Weather API collection always passes through this package.
- The returned value is normalized source data, not facts or prompt input.
- Context cancellation is passed to the Weather API adapter.
- Errors retain whether setup or fetching failed.
Focused tests are in `internal/collect/collect_test.go`; orchestration use is
also covered by `internal/app/app_test.go`.
```sh
go test ./internal/collect
```

View File

@@ -2,74 +2,46 @@
`internal/forecast` deterministically selects and summarizes normalized
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.
registry dependency. The report-scoped caller is [fact
contracts](facts.md), which owns the choice of data required by each report.
## Period and daypart semantics
## Daily Derivation
Selections use `timeutil.Period` half-open overlap: a value is selected only
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.
`BuildDailySummary` builds one summary for one local civil day. The facts
layer calls it for Daily, Today, and Tomorrow reports; it does not provide a
multi-day or arbitrary-period summary constructor. `timeutil.Period` supplies
the shared half-open overlap rule used while selecting source values.
`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.
`ResolveDayparts` turns configured local clock ranges into windows. A range
whose end is not after its start continues into the next civil day. The
available daypart and timezone settings are defined in the
[configuration reference](../config.md).
## Deterministic summaries
The summary keeps selected hourly and narrative values, the discussion,
source warnings and provenance, alert overlaps, and one summary for each
resolved daypart. Daypart summaries derive their measurements, conditions,
weather indicators, and precipitation timing from normalized forecast
periods. `BuildPrecipTiming` is also available to the facts layer for a
report's selected hourly periods.
`BuildDailySummary` requires an hourly run with at least one period. It adds
the selected narrative periods, discussion, alert overlaps, source provenance,
source warnings, and one `DaypartSummary` per resolved window. A daypart keeps
its selected hourly periods and derives temperature and apparent-temperature
ranges, timed precipitation and wind maxima, dominant and notable conditions,
and weather indicators.
## Boundaries And Failures
Hourly precipitation probabilities must be finite percentages from 0 through
100. Daily-summary construction rejects invalid values before they can affect
timed maxima or precipitation windows.
Daily-summary construction requires a bundle with hourly forecast data,
valid precipitation probabilities, and valid daypart definitions. Optional
normalized products remain absent when unavailable. Invalid alerts are ignored
while valid overlaps are selected for the relevant day or daypart window.
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.
Ice detection uses the bounded condition vocabulary `ice`, `icy`, `freezing`,
and `sleet` as whole words.
Thresholds, text classification, unit normalization, and alert selection are
package implementation rules. Report identity, period selection, and the
resulting derived-fact shape are owned by [fact contracts](facts.md); external
source semantics are owned by [weather-data internals](weather-data.md).
Daypart temperature and apparent-temperature ranges are Fahrenheit values, and
timed wind maxima are mph values. When only metric source fields are present,
they are converted to those units before they are stored or evaluated against
indicator thresholds; populated US-customary fields take precedence.
## Verification
Alert overlap parsing supports the normalized alert payload's available timing
fields. Unparseable alerts and invalid intervals are ignored; valid overlaps
are clipped to the requested period and ordered by alert start time.
`DailySummary.AlertOverlaps` is limited to the local civil day, while each
daypart evaluates the full alert run against its complete window, including the
next-day portion of an overnight window.
## Missing data and failures
Empty selections yield empty summary fields rather than generated prose.
Direct daily or period-summary calls fail when their required bundle, valid
period, hourly data, or daypart definitions are invalid. A nil location uses
UTC when these APIs are called directly. Optional narrative, discussion, and
alerts remain absent when their normalized products are absent.
Forecast thresholds used for brief indicators and precipitation timing are
implementation rules.
## Verification and invariants
Focused tests cover local civil days, clipped periods, daypart resolution,
summary metrics, precipitation-window threshold behavior, and alert overlap:
Focused `internal/forecast` tests exercise daily and overnight dayparts,
summary derivation, invalid precipitation data, precipitation timing, and
alert overlap handling. `internal/facts` tests cover the report-scoped caller:
```sh
go test ./internal/forecast ./internal/timeutil
go test ./internal/forecast ./internal/facts
```
The package preserves normalized inputs as inspectable structured values and
never decides report identity, delivery, or presentation wording.

View File

@@ -1,27 +1,35 @@
# Prompt Input Internals
`internal/promptinput` converts report metadata, an ordered module snapshot, and source warnings into the YAML `data_package` supplied inline to Promptkit. It owns the package schema, grouping, serialization, and validation; it does not choose an output destination, collect weather, execute a provider, or retain packages after a command ends.
`internal/promptinput` turns prepared report metadata and an ordered module
snapshot into the YAML data package passed to Promptkit. The externally visible
prompt and inline-input contract is owned by the [Promptkit integration
guide](../integrations/promptkit.md); preparation of the inputs is owned by
[prepared report internals](prepared-report.md).
## Package Construction
`Build` produces `weatherreporter.data_package.v4`. Its metadata projection comes from the prepared report identity and copies the run ID; report ID, variant, prompt ID, generation time, timezone, local current date, and valid period; ordered briefing stanzas; and prompt-safe source-warning summaries. Warning summaries include only source, code, severity, message, and completeness impact; raw transport and provenance fields such as endpoints never cross into the provider input. Prompt input contains no historical comparison section.
`Build` projects report identity, the report-local current date, source-warning
summaries, and each snapshot output's prompt-facing value into a package. It
does not expose source transport or provenance details. The module snapshot
defines stanza order and selects curated prompt values; the corresponding
module contracts are documented in [module internals](module.md) and [briefing
internals](briefing.md).
Briefing is a flat ordered set of stanza values. `Build` uses each output's `DataPackageValue`, so curated prompt exports take precedence and rich values are used only as a fallback. Prompt exports are selected by the [briefing registry](briefing.md), while the rich-versus-prompt contract is in [module internals](module.md).
`MarshalYAML` validates the package before serializing it. Serialization emits
the metadata stanza first, then groups the remaining recognized stanzas in the
package's fixed category order while preserving snapshot order within a
category. `Validate` enforces the supported schema version, required report
identity and period values, and a nonempty, complete ordered briefing.
## YAML Ordering And Validation
This package does not collect weather, choose an output destination, execute a
provider, or persist data packages. The application passes its in-memory YAML
to the Promptkit adapter as part of prepared report execution.
Serialization keeps `metadata` directly under `briefing`. Every other known stanza is placed in one category and emitted in category order while preserving its original module order:
## Verification
| Category | Current stanzas |
| --- | --- |
| `applicable_risk_products` | alert digest, SPC convective outlooks |
| `derived_summaries` | deterministic summaries, precipitation timing, outdoor windows, and planning values |
| `narrative_products` | narrative forecast, discussions, and weather story |
| `raw_data` | current conditions and hourly forecast |
`Validate` requires the v4 schema version, report identity and period fields, and at least one ordered briefing stanza. `MarshalYAML` validates before serializing. Normal application execution passes marshalled YAML directly to Promptkit.
Focused tests cover construction, curated exports, category ordering, serialization, and validation:
Focused tests cover package construction, report-local dates, validation,
curated snapshot exports, deterministic YAML grouping, and safe source-warning
projection:
```sh
go test ./internal/promptinput

View File

@@ -1,34 +1,37 @@
# Report Registry Internals
`internal/report` owns report identities, valid-period resolution, exact prompt identity and version, output names, default module composition, and Distributor path declarations. Public command syntax belongs in the [CLI reference](../cli.md); configuration aliases and overrides belong in the [configuration reference](../config.md).
`internal/report` owns the in-process registry of report identities and the
resolution of a report's valid period. Command names and configuration aliases
belong to the [CLI reference](../cli.md) and [configuration
reference](../config.md), respectively.
## Definitions And Resolution
## Registry And Resolution
Each `Definition` declares a stable ID and display name, prompt ID and version, template and generated-text schema IDs, valid-period resolver, default output name, Distributor path templates, module list, and fixed batch eligibility. `Resolved` combines a definition with one valid period and run identity.
`DefaultRegistry` supplies the maintained definitions. `Lookup` returns a
definition by its internal ID, while `Resolve` combines it with a request time,
location, and optional date to produce `Resolved`. The result carries the
definition, generation time, timezone, and resolved valid period; its metadata
and output-name helpers keep derived identity values consistent for callers.
| Report ID | Prompt version | Default profile | Period policy | Fixed batch flag | Default output |
| --- | --- | --- | --- | --- | --- |
| `daily` | `2.0.0` | `weather-balanced` | Explicit local civil day | Dynamic Daily inclusion is app-owned | `daily-YYYY-MM-DD.md` |
| `today` | `2.0.0` | `weather-balanced` | Selected or current local civil day | Morning | `today.md` |
| `tomorrow` | `2.0.0` | `weather-balanced` | Next local civil day | Evening | `tomorrow.md` |
| `hourly` | `2.0.0` | `weather-light` | Rolling six-hour interval | — | `hourly.md` |
Definitions carry the internal collaborators needed downstream: prompt and
template identity, module configuration, output naming, Distributor path
templates, and fixed batch eligibility. The external prompt contract is owned
by the [Promptkit integration guide](../integrations/promptkit.md), template
surface by the [report template guide](../templates.md), and published
Distributor paths by the [Distributor bundle guide](../integrations/distributor/pkg-bundle.md).
Daily derives its filename and run identity from the resolved valid-period start
in the effective timezone, so multiple Daily items have distinct destinations
and identifiers. Exact template fields and schema assets belong to [report
templates](../templates.md) and [generated-text internals](generatedtext.md).
Prompt assets own default profile selection; the registry stores no provider
setting.
`WithModuleOverrides` returns an independently cloned registry with replacement
module configuration for recognized report IDs. The application owns batch
planning and data-dependent inclusion; see [app orchestration
internals](app-orchestration.md).
## Collaborators And Boundaries
The registry never collects weather data, parses CLI flags, writes output,
executes Promptkit, or delivers a report.
`DefaultRegistry`, `Lookup`, `Resolve`, and report-name helpers prevent callers from duplicating report identity rules. Registry overrides clone a recognized definition and replace its module list. `DistributorPathTemplates` are consumed by app orchestration; their rendered external bundle-path contract is documented in the [Distributor bundle guide](../integrations/distributor/pkg-bundle.md).
## Verification
`morning` and `evening` are registry-owned batch names. Fixed flags declare Today and Tomorrow eligibility; app orchestration determines data-dependent Daily membership and the actual batch plan.
The registry never collects weather data, parses CLI flags, writes output, executes Promptkit, or delivers a report.
Focused tests cover definition completeness, command and alias lookup, period resolution, run IDs, output names, composition defaults, and override validation:
Focused tests protect retained report definitions, period resolution, Daily
run-ID disambiguation, and rejection of retired command or configuration names:
```sh
go test ./internal/report