Add a roadmap and implementation plan for rolling near-term forecast reports
This commit is contained in:
@@ -1,519 +1,442 @@
|
|||||||
# SPC Convective Outlook Implementation Roadmap
|
# Near-Term Report Implementation Roadmap
|
||||||
|
|
||||||
## Purpose
|
## Purpose
|
||||||
|
|
||||||
This roadmap defines the concrete implementation sequence for
|
This roadmap defines the concrete implementation sequence for
|
||||||
`docs/roadmap/outlook.md`. It is written for an LLM coding agent that will
|
`docs/roadmap/near-term.md`. It is written for an LLM coding agent that will
|
||||||
implement the stages in order.
|
implement each stage in order.
|
||||||
|
|
||||||
This is a future-work roadmap. Until a stage is implemented, non-roadmap docs
|
This is a future-work roadmap. Until a stage is implemented, non-roadmap docs
|
||||||
must not describe SPC convective outlook behavior as available.
|
must not describe `near_term` behavior as available.
|
||||||
|
|
||||||
## Source Feature Roadmap
|
## Source Feature Roadmap
|
||||||
|
|
||||||
Use `docs/roadmap/outlook.md` as the authoritative feature roadmap for intent,
|
Use `docs/roadmap/near-term.md` as the authoritative feature roadmap for user
|
||||||
target shape, and policy decisions. This implementation roadmap is the
|
intent, target behavior, and policy choices. This document is the implementation
|
||||||
step-by-step work plan. If the two documents conflict, update the feature
|
plan. If the target behavior changes, update `near-term.md` first, then update
|
||||||
roadmap first when the target behavior changes, then update this implementation
|
this roadmap.
|
||||||
plan.
|
|
||||||
|
|
||||||
## Locked Implementation Decisions
|
## Locked Implementation Decisions
|
||||||
|
|
||||||
- Preserve existing CLI syntax, generated artifact paths, report IDs, prompt
|
- Add report ID `near_term` and Go constant `report.NearTerm`.
|
||||||
IDs, distributor behavior, and Scriptorium invocation.
|
- Add CLI command `weatherreporter generate near-term`.
|
||||||
- Use source key and module IDs based on `spc_convective_outlooks`.
|
- Do not add `--date`, `--start`, `--end`, or duration flags for this report.
|
||||||
- Add two modules:
|
- Do not add `near_term` to morning or evening batches in the first
|
||||||
- `spc_convective_outlooks` in `applicable_risk_products`;
|
implementation.
|
||||||
- `spc_convective_discussion` in `narrative_products`.
|
- Define the valid-period duration with a package-owned constant, initially
|
||||||
- Fetch the upstream Weather API source once in
|
`6` hours.
|
||||||
`internal/adapters/weatherapi`; module builders must not fetch upstream data.
|
- Resolve the valid period as `[generation_time, generation_time + 6h)` in the
|
||||||
- Use a Weather API adapter constant for the path:
|
effective report timezone.
|
||||||
`/outlooks/convective`.
|
- Use prompt ID `weather.near_term_report`.
|
||||||
- Initial query parameters for the outlook endpoint are `format=json` and the
|
- Use artifact group `near-term` and batch output name `near-term.md`.
|
||||||
configured `tz`; do not send `precision`.
|
- Add comparison strategy constant `CompareRollingWindow = "rolling_window"`.
|
||||||
- Treat the source as optional under existing missing-source policy.
|
- Declare `CompatiblePriorIDs: []report.ID{report.NearTerm}`, but do not emit
|
||||||
- Treat `data: null` as no latest run and therefore missing/unavailable
|
Recent Changes for `near_term` in the first implementation.
|
||||||
optional source data.
|
- Keep state prior lookup returning `nil` for rolling-window reports until a
|
||||||
- Treat a non-null run with empty `outlooks` and `discussions` arrays as
|
future comparison algorithm is designed.
|
||||||
checked, non-missing empty data.
|
- Default module order is:
|
||||||
- Preserve GeoJSON geometry in collected facts and persisted bundle artifacts,
|
1. `metadata`
|
||||||
but omit geometry from prompt-facing module output.
|
2. `current_conditions`
|
||||||
- Filter outlooks by overlap with the resolved report valid period in Go.
|
3. `hourly_forecast`
|
||||||
- Do not depend on `/outlooks/convective/active` or
|
4. `precip_timing`
|
||||||
`/outlooks/convective/location` for initial behavior.
|
5. `alert_digest`
|
||||||
- Include SPC discussion text only when at least one retained report-period
|
6. `spc_convective_outlooks`
|
||||||
categorical outlook has `severity_rank >= 3`.
|
7. `area_forecast_discussion`
|
||||||
- Define the discussion threshold as an internal constant, initially `3`, not a
|
8. `spc_convective_discussion`
|
||||||
config field.
|
9. `weather_story`
|
||||||
|
- Configure the near-term `area_forecast_discussion` module with only
|
||||||
|
`key_messages` and `short_term` sections.
|
||||||
|
- Do not include daily/daypart modules in the default near-term composition.
|
||||||
|
- Alert and SPC modules must use existing valid-period overlap behavior.
|
||||||
|
- Preserve existing public behavior for all current report types.
|
||||||
|
|
||||||
## Stage 1: Weather Data Contract
|
## Stage 1: Report Identity And Period Resolution
|
||||||
|
|
||||||
Goal: add typed collected SPC outlook facts without changing fetching or prompt
|
Goal: add the `near_term` report definition, constants, registry entry, and
|
||||||
output yet.
|
rolling six-hour valid-period resolver.
|
||||||
|
|
||||||
Files to inspect:
|
|
||||||
|
|
||||||
- `internal/weatherdata/bundle.go`
|
|
||||||
- `internal/facts/facts.go`
|
|
||||||
- `internal/module/module.go`
|
|
||||||
- `internal/briefing/modules.go`
|
|
||||||
- `internal/weatherdata` and `internal/facts` tests
|
|
||||||
|
|
||||||
Implementation:
|
|
||||||
|
|
||||||
- Add `ConvectiveOutlookRun` to `internal/weatherdata`.
|
|
||||||
- Add `ConvectiveOutlook` with fields matching the consumed Weather API
|
|
||||||
outlook fields:
|
|
||||||
- `id`
|
|
||||||
- `provider`
|
|
||||||
- `product`
|
|
||||||
- `day`
|
|
||||||
- `outlookType`
|
|
||||||
- `label`
|
|
||||||
- `labelText`
|
|
||||||
- `forecaster`
|
|
||||||
- `severityRank`
|
|
||||||
- `validFrom`
|
|
||||||
- `validTo`
|
|
||||||
- `issuedAt`
|
|
||||||
- `expiresAt`
|
|
||||||
- `sourceUrl`
|
|
||||||
- `imageUrl`
|
|
||||||
- `containsLocation`
|
|
||||||
- `geometry`
|
|
||||||
- Store `geometry` as `json.RawMessage` or an equivalent JSON-preserving type;
|
|
||||||
do not introduce a GeoJSON dependency.
|
|
||||||
- Add `ConvectiveOutlookDiscussion` with:
|
|
||||||
- `day`
|
|
||||||
- `headline`
|
|
||||||
- `summary`
|
|
||||||
- `discussion`
|
|
||||||
- `updatedAt`
|
|
||||||
- Add `SPCConvectiveOutlooks *ConvectiveOutlookRun` to
|
|
||||||
`weatherdata.Bundle`.
|
|
||||||
- Add matching fields to `facts.CollectedFacts`, `BuildCollected`, and
|
|
||||||
`CollectedFacts.Bundle`.
|
|
||||||
- Add module constants in `internal/module`:
|
|
||||||
- `SPCConvectiveOutlooks ID = "spc_convective_outlooks"`
|
|
||||||
- `SPCConvectiveDiscussion ID = "spc_convective_discussion"`
|
|
||||||
- Add option structs:
|
|
||||||
- `SPCConvectiveOutlooksOptions struct{}`
|
|
||||||
- `SPCConvectiveDiscussionOptions struct{}`
|
|
||||||
- Add fact requirement constants:
|
|
||||||
- `CollectedSPCConvectiveOutlooks`
|
|
||||||
- `RequiresDerivedSPCConvectiveOutlooks`
|
|
||||||
- Wire the new collected requirement into
|
|
||||||
`briefing.collectedFactAvailable`.
|
|
||||||
|
|
||||||
Acceptance criteria:
|
|
||||||
|
|
||||||
- No Weather API request is added in this stage.
|
|
||||||
- No report default module list changes in this stage.
|
|
||||||
- No prompt package shape changes in this stage.
|
|
||||||
- Existing tests pass.
|
|
||||||
|
|
||||||
Suggested validation:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
go test ./internal/weatherdata ./internal/facts ./internal/module ./internal/briefing
|
|
||||||
```
|
|
||||||
|
|
||||||
This stage is small enough for one implementation prompt.
|
|
||||||
|
|
||||||
## Stage 2: Weather API Adapter Fetch
|
|
||||||
|
|
||||||
Goal: fetch `/outlooks/convective`, decode it into the new weather data
|
|
||||||
contract, and preserve source provenance.
|
|
||||||
|
|
||||||
Files to inspect:
|
|
||||||
|
|
||||||
- `internal/adapters/weatherapi/client.go`
|
|
||||||
- `internal/adapters/weatherapi/client_test.go`
|
|
||||||
- `internal/adapters/weatherapi/testdata/`
|
|
||||||
- `docs/roadmap/outlook.md`
|
|
||||||
- upstream Weather API docs under the Convective Outlooks section
|
|
||||||
|
|
||||||
Implementation:
|
|
||||||
|
|
||||||
- Add a package-level adapter constant, for example:
|
|
||||||
`convectiveOutlooksEndpoint = "/outlooks/convective"`.
|
|
||||||
- Add source-name constant or narrowly scoped source key for
|
|
||||||
`spc_convective_outlooks`.
|
|
||||||
- Add fan-out fetch for the new optional source.
|
|
||||||
- Build the query as:
|
|
||||||
- `format=json`
|
|
||||||
- `tz=<configured weather_api.timezone>`
|
|
||||||
- Do not send `precision` to this endpoint.
|
|
||||||
- Do not send `units` to this endpoint. If existing query helpers add units by
|
|
||||||
default, add a route-specific option such as `omitUnits` so the final request
|
|
||||||
remains `format` plus `tz`.
|
|
||||||
- Decode the Weather API envelope and payload into
|
|
||||||
`weatherdata.ConvectiveOutlookRun`.
|
|
||||||
- Preserve `data: null` as optional missing/unavailable source data through
|
|
||||||
existing missing-source policy.
|
|
||||||
- Preserve a non-null run with empty arrays as checked non-missing source data.
|
|
||||||
- Record source provenance:
|
|
||||||
- source name `spc_convective_outlooks`;
|
|
||||||
- endpoint constant path;
|
|
||||||
- exact query parameters sent;
|
|
||||||
- fetched time;
|
|
||||||
- issued time from run `issuedAt` when present, otherwise `asOf`;
|
|
||||||
- source hash over compact raw `data` JSON;
|
|
||||||
- source warnings when missing-source policy emits them.
|
|
||||||
- Add `internal/adapters/weatherapi/testdata/convective_outlooks.json`.
|
|
||||||
|
|
||||||
Acceptance criteria:
|
|
||||||
|
|
||||||
- Complete fixture fetch includes `bundle.SPCConvectiveOutlooks`.
|
|
||||||
- Source record is present and non-missing when the payload has a non-null run.
|
|
||||||
- `data: null` follows optional missing-source policy.
|
|
||||||
- Non-null empty arrays do not produce a missing-source warning.
|
|
||||||
- Adapter tests assert request path and query, including absence of
|
|
||||||
`precision`.
|
|
||||||
- Existing hourly required-source behavior is unchanged.
|
|
||||||
|
|
||||||
Suggested validation:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
go test ./internal/adapters/weatherapi ./internal/weatherdata ./internal/facts
|
|
||||||
```
|
|
||||||
|
|
||||||
This stage is small enough for one implementation prompt.
|
|
||||||
|
|
||||||
## Stage 3: Report-Period Outlook Filtering
|
|
||||||
|
|
||||||
Goal: derive report-scoped SPC outlook facts by valid-period overlap.
|
|
||||||
|
|
||||||
Files to inspect:
|
|
||||||
|
|
||||||
- `internal/facts/facts.go`
|
|
||||||
- `internal/forecast/derive.go`
|
|
||||||
- `internal/timeutil/periods.go`
|
|
||||||
- `internal/report/*_report.go`
|
|
||||||
- existing forecast/facts tests for period slicing
|
|
||||||
|
|
||||||
Implementation:
|
|
||||||
|
|
||||||
- Add a report-scoped derived value for retained outlooks and discussions.
|
|
||||||
Recommended shape:
|
|
||||||
- `DerivedFacts.SPCConvectiveOutlooks []weatherdata.ConvectiveOutlook`
|
|
||||||
- `DerivedFacts.SPCConvectiveDiscussions []weatherdata.ConvectiveOutlookDiscussion`
|
|
||||||
- Add a small deterministic helper under `internal/facts`; this first
|
|
||||||
implementation is report-scoped selection of already-collected facts, not a
|
|
||||||
broader meteorological derivation.
|
|
||||||
- Select outlooks whose half-open valid interval overlaps the resolved report
|
|
||||||
valid period.
|
|
||||||
- Treat missing `severityRank` as lower than the discussion threshold, but do
|
|
||||||
not drop the outlook from the risk-product module solely because rank is
|
|
||||||
missing.
|
|
||||||
- Retain discussion records only for days represented by retained outlooks.
|
|
||||||
- Sort retained outlooks deterministically by:
|
|
||||||
- day;
|
|
||||||
- outlook type;
|
|
||||||
- severity rank descending when present;
|
|
||||||
- valid start;
|
|
||||||
- label;
|
|
||||||
- id.
|
|
||||||
- Sort retained discussions by day, then updated time when present.
|
|
||||||
- Keep empty retained slices distinct from a missing collected source.
|
|
||||||
|
|
||||||
Acceptance criteria:
|
|
||||||
|
|
||||||
- Daily Today, Daily Tomorrow, 3-Day, Weekend, and Storm valid periods select
|
|
||||||
expected outlooks by overlap.
|
|
||||||
- Tomorrow and multi-day reports do not depend on server-current active
|
|
||||||
filtering.
|
|
||||||
- Empty retained results are still available to modules as checked empty data
|
|
||||||
when the collected source exists.
|
|
||||||
|
|
||||||
Suggested validation:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
go test ./internal/facts ./internal/forecast ./internal/report
|
|
||||||
```
|
|
||||||
|
|
||||||
This stage is small enough for one implementation prompt.
|
|
||||||
|
|
||||||
## Stage 4: Prompt Category Plumbing
|
|
||||||
|
|
||||||
Goal: prepare prompt-package category placement without registering
|
|
||||||
builderless modules.
|
|
||||||
|
|
||||||
Files to inspect:
|
|
||||||
|
|
||||||
- `internal/module/module.go`
|
|
||||||
- `internal/promptinput/package.go`
|
|
||||||
- `internal/promptinput/package_test.go`
|
|
||||||
|
|
||||||
Implementation:
|
|
||||||
|
|
||||||
- Add prompt input category mapping:
|
|
||||||
- `spc_convective_outlooks` -> `applicable_risk_products`;
|
|
||||||
- `spc_convective_discussion` -> `narrative_products`.
|
|
||||||
- Use synthetic module snapshots in tests if needed; do not add module
|
|
||||||
definitions to `defaultModuleDefinitions` until the real builders are added
|
|
||||||
in Stages 5 and 6.
|
|
||||||
|
|
||||||
Acceptance criteria:
|
|
||||||
|
|
||||||
- Prompt category tests prove both new stanzas route to the intended groups.
|
|
||||||
- The module registry still rejects unknown or builderless modules.
|
|
||||||
- No report default includes the new modules yet.
|
|
||||||
|
|
||||||
Suggested validation:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
go test ./internal/module ./internal/promptinput
|
|
||||||
```
|
|
||||||
|
|
||||||
This stage is small enough for one implementation prompt.
|
|
||||||
|
|
||||||
## Stage 5: SPC Convective Outlooks Module
|
|
||||||
|
|
||||||
Goal: add the prompt-facing risk-product module.
|
|
||||||
|
|
||||||
Files to inspect:
|
|
||||||
|
|
||||||
- `internal/briefing/alert_digest_module.go`
|
|
||||||
- `internal/briefing/weather_story_module.go`
|
|
||||||
- `internal/briefing/module_format_helpers.go`
|
|
||||||
- `internal/briefing/base_modules_test.go`
|
|
||||||
- `docs/roadmap/outlook.md`
|
|
||||||
|
|
||||||
Implementation:
|
|
||||||
|
|
||||||
- Add `internal/briefing/spc_convective_outlooks_module.go`.
|
|
||||||
- Add the `SPCConvectiveOutlooks` module definition to
|
|
||||||
`defaultModuleDefinitions` in the same change as its real builder.
|
|
||||||
- Register stanza `spc_convective_outlooks`.
|
|
||||||
- Require:
|
|
||||||
- `CollectedSPCConvectiveOutlooks`;
|
|
||||||
- `RequiresDerivedSPCConvectiveOutlooks`.
|
|
||||||
- Use `MissingDataEmpty` so checked empty data can emit an explicit empty
|
|
||||||
risk-product stanza.
|
|
||||||
- Build from collected source metadata plus derived retained outlooks.
|
|
||||||
- Emit concise prompt-facing fields:
|
|
||||||
- `checked`;
|
|
||||||
- `as_of`;
|
|
||||||
- `issued_at`;
|
|
||||||
- `location_id`;
|
|
||||||
- `location_name`;
|
|
||||||
- `outlook_count`;
|
|
||||||
- `outlooks`.
|
|
||||||
- For each outlook, emit:
|
|
||||||
- `day`;
|
|
||||||
- `outlook_type`;
|
|
||||||
- `label`;
|
|
||||||
- `label_text`;
|
|
||||||
- `period_begins`;
|
|
||||||
- `period_ends`;
|
|
||||||
- `issued_at`;
|
|
||||||
- `contains_location`;
|
|
||||||
- `image_url`.
|
|
||||||
- Use human-readable local time helpers consistent with current modules.
|
|
||||||
- Do not emit GeoJSON geometry.
|
|
||||||
- If the source was checked and no retained outlooks overlap the report
|
|
||||||
period, emit `checked: true`, `outlook_count: 0`, and an empty or omitted
|
|
||||||
`outlooks` list according to the existing YAML style for empty lists.
|
|
||||||
|
|
||||||
Acceptance criteria:
|
|
||||||
|
|
||||||
- Module output is deterministic and omits geometry.
|
|
||||||
- Checked empty data produces an explicit checked-empty stanza.
|
|
||||||
- Missing collected source follows registry missing-data behavior.
|
|
||||||
- Module tests cover populated, checked-empty, and missing cases.
|
|
||||||
|
|
||||||
Suggested validation:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
go test ./internal/briefing ./internal/module ./internal/promptinput
|
|
||||||
```
|
|
||||||
|
|
||||||
This stage is small enough for one implementation prompt.
|
|
||||||
|
|
||||||
## Stage 6: SPC Convective Discussion Module
|
|
||||||
|
|
||||||
Goal: add optional SPC discussion narrative context with a severity threshold.
|
|
||||||
|
|
||||||
Files to inspect:
|
|
||||||
|
|
||||||
- `internal/briefing/area_forecast_discussion_module.go`
|
|
||||||
- `internal/briefing/weather_story_module.go`
|
|
||||||
- `internal/briefing/module_format_helpers.go`
|
|
||||||
- `internal/briefing/base_modules_test.go`
|
|
||||||
|
|
||||||
Implementation:
|
|
||||||
|
|
||||||
- Add `internal/briefing/spc_convective_discussion_module.go`.
|
|
||||||
- Add the `SPCConvectiveDiscussion` module definition to
|
|
||||||
`defaultModuleDefinitions` in the same change as its real builder.
|
|
||||||
- Register stanza `spc_convective_discussion`.
|
|
||||||
- Require:
|
|
||||||
- `CollectedSPCConvectiveOutlooks`;
|
|
||||||
- `RequiresDerivedSPCConvectiveOutlooks`.
|
|
||||||
- Use `MissingDataOmit` so unavailable or below-threshold discussion text is
|
|
||||||
omitted.
|
|
||||||
- Define a package-private constant near the module, for example:
|
|
||||||
`defaultSPCConvectiveDiscussionMinimumSeverityRank = 3`.
|
|
||||||
- Build from derived retained outlooks and discussions.
|
|
||||||
- Include discussion text only when at least one retained categorical outlook
|
|
||||||
has
|
|
||||||
`severityRank >= defaultSPCConvectiveDiscussionMinimumSeverityRank`.
|
|
||||||
- When the threshold is not met, return `nil` output so the stanza is omitted.
|
|
||||||
- When threshold is met, include discussions for retained outlook days with:
|
|
||||||
- `day`;
|
|
||||||
- `period_begins`;
|
|
||||||
- `period_ends`;
|
|
||||||
- `headline`;
|
|
||||||
- `summary`;
|
|
||||||
- `discussion`;
|
|
||||||
- `updated_at`.
|
|
||||||
- Include a concise reason field such as:
|
|
||||||
`included_because: "categorical severity_rank >= 3"`.
|
|
||||||
|
|
||||||
Acceptance criteria:
|
|
||||||
|
|
||||||
- Slight Risk or higher retained categorical outlooks include matching
|
|
||||||
discussion records when available.
|
|
||||||
- Lower-risk retained outlooks still appear in `spc_convective_outlooks` but
|
|
||||||
do not emit `spc_convective_discussion`.
|
|
||||||
- Missing discussion text omits the stanza without failing report generation.
|
|
||||||
- Tests cover threshold below, threshold equal, threshold above, and missing
|
|
||||||
discussion cases.
|
|
||||||
|
|
||||||
Suggested validation:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
go test ./internal/briefing ./internal/promptinput
|
|
||||||
```
|
|
||||||
|
|
||||||
This stage is small enough for one implementation prompt.
|
|
||||||
|
|
||||||
## Stage 7: Report Composition And Config Examples
|
|
||||||
|
|
||||||
Goal: add the implemented modules to default report definitions and maintained
|
|
||||||
examples.
|
|
||||||
|
|
||||||
Files to inspect:
|
Files to inspect:
|
||||||
|
|
||||||
|
- `internal/report/definition.go`
|
||||||
|
- `internal/report/registry.go`
|
||||||
|
- `internal/report/period.go`
|
||||||
- `internal/report/daily_report.go`
|
- `internal/report/daily_report.go`
|
||||||
- `internal/report/three_day_report.go`
|
- `internal/report/period_test.go`
|
||||||
- `internal/report/weekend_report.go`
|
- `internal/state/filesystem.go`
|
||||||
- `internal/report/storm_report.go`
|
|
||||||
- `internal/config/reports.go`
|
|
||||||
- `internal/config/config_test.go`
|
|
||||||
- `examples/config.yml`
|
|
||||||
|
|
||||||
Implementation:
|
Implementation:
|
||||||
|
|
||||||
- Add `spc_convective_outlooks` to default report module lists for:
|
- Add `NearTerm ID = "near_term"` in `internal/report/definition.go`.
|
||||||
- Daily Today;
|
- Add `CompareRollingWindow ComparisonStrategy = "rolling_window"`.
|
||||||
- Daily Tomorrow;
|
- Add `internal/report/near_term_report.go`.
|
||||||
- 3-Day;
|
- Define a package-local duration constant in that file, for example:
|
||||||
- Weekend;
|
|
||||||
- Storm.
|
|
||||||
- Place `spc_convective_outlooks` immediately after `alert_digest` when
|
|
||||||
`alert_digest` is present.
|
|
||||||
- Add `spc_convective_discussion` immediately after
|
|
||||||
`area_forecast_discussion` when `area_forecast_discussion` is present.
|
|
||||||
- Update maintained example config module overrides if they enumerate module
|
|
||||||
lists.
|
|
||||||
- Keep CLI syntax and config field names unchanged.
|
|
||||||
|
|
||||||
Acceptance criteria:
|
```go
|
||||||
|
const nearTermHours = 6
|
||||||
|
```
|
||||||
|
|
||||||
- All default report module compositions validate.
|
- Add `nearTermDefinition()` returning:
|
||||||
- Example config loads successfully.
|
- `ID: NearTerm`
|
||||||
- Config override tests can include both new module IDs.
|
- `Name: "Near-Term Report"`
|
||||||
- Existing report IDs, prompt IDs, output names, and valid-period behavior are
|
- `PromptID: "weather.near_term_report"`
|
||||||
unchanged.
|
- `ComparisonStrategy: CompareRollingWindow`
|
||||||
|
- `ArtifactGroup: "near-term"`
|
||||||
|
- `BatchOutputName: "near-term.md"`
|
||||||
|
- `Generated: true`
|
||||||
|
- `CompatiblePriorIDs: []ID{NearTerm}`
|
||||||
|
- `Modules: nearTermModules()`
|
||||||
|
- no `Morning` or `Evening` membership
|
||||||
|
- `resolve: resolveNearTerm`
|
||||||
|
- Implement `resolveNearTerm` as generation-time anchored:
|
||||||
|
|
||||||
Suggested validation:
|
```go
|
||||||
|
localNow := req.Now.In(req.Location)
|
||||||
|
return timeutil.Period{
|
||||||
|
Start: localNow,
|
||||||
|
End: localNow.Add(nearTermHours * time.Hour),
|
||||||
|
}, nil
|
||||||
|
```
|
||||||
|
|
||||||
|
- Add `nearTermDefinition()` to `DefaultRegistry()`.
|
||||||
|
- Add `NearTerm` to `Registry.All()` in a stable order after
|
||||||
|
`DailyTomorrow` and before `ThreeDay`.
|
||||||
|
- Do not change `BatchReports`.
|
||||||
|
- Leave `state.FindPriorSnapshot` behavior unchanged for
|
||||||
|
`CompareRollingWindow`; it should return `nil` because it only supports
|
||||||
|
same-date and weekend lookup.
|
||||||
|
|
||||||
|
Tests:
|
||||||
|
|
||||||
|
- Add report period tests for:
|
||||||
|
- lookup succeeds for `NearTerm`;
|
||||||
|
- `Registry.All()` includes `NearTerm`;
|
||||||
|
- fixed generation time resolves to exactly six hours;
|
||||||
|
- timezone-aware start and end use the effective location;
|
||||||
|
- valid period is not civil-day truncated;
|
||||||
|
- metadata RunID includes `near_term`;
|
||||||
|
- batch membership remains unchanged.
|
||||||
|
- Update registry metadata/path tests to include:
|
||||||
|
- artifact group `near-term`;
|
||||||
|
- batch output name `near-term.md`;
|
||||||
|
- generated `true`;
|
||||||
|
- compatible prior IDs `[]ID{NearTerm}`;
|
||||||
|
- comparison strategy `CompareRollingWindow`.
|
||||||
|
|
||||||
|
Validation:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
go test ./internal/report ./internal/config ./internal/briefing ./internal/app
|
go test ./internal/report ./internal/state
|
||||||
```
|
```
|
||||||
|
|
||||||
This stage is small enough for one implementation prompt.
|
This stage is small enough for one implementation prompt.
|
||||||
|
|
||||||
## Stage 8: App And Prompt Workflow Coverage
|
## Stage 2: Module Compatibility And Default Composition
|
||||||
|
|
||||||
Goal: prove the end-to-end generated data package contains the new stanzas in
|
Goal: make existing modules compatible with `near_term` where appropriate and
|
||||||
the intended categories when fixture data warrants them.
|
declare the default module composition.
|
||||||
|
|
||||||
|
Files to inspect:
|
||||||
|
|
||||||
|
- `internal/report/near_term_report.go`
|
||||||
|
- `internal/briefing/modules.go`
|
||||||
|
- `internal/module/module.go`
|
||||||
|
- `internal/briefing/modules_test.go`
|
||||||
|
- `internal/briefing/base_modules_test.go`
|
||||||
|
- `internal/briefing/derived_modules_test.go`
|
||||||
|
|
||||||
|
Implementation:
|
||||||
|
|
||||||
|
- Add `nearTermModules()` in `internal/report/near_term_report.go`.
|
||||||
|
- Use explicit module items in this order:
|
||||||
|
- `module.Metadata`
|
||||||
|
- `module.CurrentConditions`
|
||||||
|
- `module.HourlyForecast`
|
||||||
|
- `module.PrecipTiming`
|
||||||
|
- `module.AlertDigest`
|
||||||
|
- `module.SPCConvectiveOutlooks`
|
||||||
|
- `module.AreaForecastDiscussion` with options:
|
||||||
|
|
||||||
|
```go
|
||||||
|
module.AreaForecastDiscussionOptions{
|
||||||
|
Sections: []string{"key_messages", "short_term"},
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
- `module.SPCConvectiveDiscussion`
|
||||||
|
- `module.WeatherStory`
|
||||||
|
- Expand module `SupportedReports` in `internal/briefing/modules.go`:
|
||||||
|
- include `report.NearTerm` in `allReports`;
|
||||||
|
- include `report.NearTerm` for `HourlyForecast`;
|
||||||
|
- do not include `report.NearTerm` for `NarrativeForecast`;
|
||||||
|
- do not include `report.NearTerm` in `daypartReports`;
|
||||||
|
- do not include `report.NearTerm` for `DerivedDailySummary`,
|
||||||
|
`DerivedDaypartSummaries`, `OutdoorWindows`, or `TomorrowPlanning`.
|
||||||
|
- Keep `PrecipTiming`, `AlertDigest`, `SPCConvectiveOutlooks`,
|
||||||
|
`AreaForecastDiscussion`, `SPCConvectiveDiscussion`, and `WeatherStory`
|
||||||
|
compatible through `allReports`.
|
||||||
|
- Do not add a new module ID in this stage.
|
||||||
|
|
||||||
|
Tests:
|
||||||
|
|
||||||
|
- Add or update module registry tests proving:
|
||||||
|
- default near-term composition validates;
|
||||||
|
- all default near-term modules have builders;
|
||||||
|
- daily/daypart-only modules reject `report.NearTerm`;
|
||||||
|
- `HourlyForecast` builds for `report.NearTerm`;
|
||||||
|
- AFD options for the near-term default include only key messages and short
|
||||||
|
term.
|
||||||
|
- Add a focused AFD module test that near-term options omit long term when the
|
||||||
|
source provides it.
|
||||||
|
|
||||||
|
Validation:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
go test ./internal/report ./internal/briefing ./internal/module
|
||||||
|
```
|
||||||
|
|
||||||
|
This stage is small enough for one implementation prompt.
|
||||||
|
|
||||||
|
## Stage 3: Derived Facts For Rolling Windows
|
||||||
|
|
||||||
|
Goal: teach `internal/facts` to build the facts needed by the near-term module
|
||||||
|
set without requiring daily summaries or daypart summaries.
|
||||||
|
|
||||||
|
Files to inspect:
|
||||||
|
|
||||||
|
- `internal/facts/facts.go`
|
||||||
|
- `internal/facts/facts_test.go`
|
||||||
|
- `internal/forecast`
|
||||||
|
- `internal/timeutil`
|
||||||
|
- `internal/briefing/modules.go`
|
||||||
|
|
||||||
|
Implementation:
|
||||||
|
|
||||||
|
- Add `report.NearTerm` handling in `facts.BuildDerived`.
|
||||||
|
- For near-term:
|
||||||
|
- populate `ValidPeriodHourlyPeriods` from the resolved six-hour valid
|
||||||
|
period;
|
||||||
|
- populate `ValidPeriodNarrativePeriods` if the existing generic selection
|
||||||
|
already does so, but do not require it for default near-term modules;
|
||||||
|
- build `PrecipTiming` from `ValidPeriodHourlyPeriods`;
|
||||||
|
- select alert overlaps using the near-term valid period;
|
||||||
|
- select SPC outlooks and discussions using the near-term valid period;
|
||||||
|
- do not build or require `DailySummaries`;
|
||||||
|
- do not build or require `DaypartSummaries`;
|
||||||
|
- do not build `StormWindowSummary`.
|
||||||
|
- Preserve existing daily, tomorrow, three-day, weekend, and storm derivation.
|
||||||
|
- If any existing helper assumes civil-day coverage, keep near-term on the
|
||||||
|
generic valid-period hourly path instead of reusing that helper.
|
||||||
|
|
||||||
|
Tests:
|
||||||
|
|
||||||
|
- Add facts tests for:
|
||||||
|
- valid-period hourly selection over a rolling six-hour window;
|
||||||
|
- precipitation timing based only on the near-term hourly slice;
|
||||||
|
- alert overlap inclusion/exclusion by near-term period;
|
||||||
|
- SPC outlook inclusion/exclusion by near-term period;
|
||||||
|
- SPC discussion records retained only for retained overlapping SPC days;
|
||||||
|
- no daily/daypart facts required.
|
||||||
|
- Add a regression test that an unsupported future report still returns an
|
||||||
|
actionable derivation error.
|
||||||
|
|
||||||
|
Validation:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
go test ./internal/facts ./internal/forecast ./internal/timeutil
|
||||||
|
```
|
||||||
|
|
||||||
|
This stage is small enough for one implementation prompt.
|
||||||
|
|
||||||
|
## Stage 4: App Report Mapping And CLI Command
|
||||||
|
|
||||||
|
Goal: add explicit `generate near-term` support while preserving existing CLI
|
||||||
|
syntax and app behavior.
|
||||||
|
|
||||||
|
Files to inspect:
|
||||||
|
|
||||||
|
- `internal/app/app.go`
|
||||||
|
- `internal/app/app_test.go`
|
||||||
|
- `internal/cli/root.go`
|
||||||
|
- `internal/cli/root_test.go`
|
||||||
|
- `cmd/weatherreporter/main.go`
|
||||||
|
|
||||||
|
Implementation:
|
||||||
|
|
||||||
|
- Add app report kind:
|
||||||
|
|
||||||
|
```go
|
||||||
|
ReportNearTerm ReportKind = "near-term"
|
||||||
|
```
|
||||||
|
|
||||||
|
- Map `ReportNearTerm` to `report.NearTerm` in `reportIDForCommand`.
|
||||||
|
- Add `near-term` to CLI generate report parsing.
|
||||||
|
- Add help usage line:
|
||||||
|
|
||||||
|
```text
|
||||||
|
weatherreporter generate near-term [--config PATH] [--units VALUE] [--tz NAME] [--out PATH]
|
||||||
|
```
|
||||||
|
|
||||||
|
- Reuse existing generate command flags:
|
||||||
|
- allow `--config`;
|
||||||
|
- allow `--units`;
|
||||||
|
- allow `--tz`;
|
||||||
|
- allow `--out`;
|
||||||
|
- do not allow or require `--date`;
|
||||||
|
- do not allow or require storm `--start` / `--end`.
|
||||||
|
- Ensure the CLI returns an actionable error for unknown report names as today.
|
||||||
|
- Do not change batch commands.
|
||||||
|
|
||||||
|
Tests:
|
||||||
|
|
||||||
|
- Add CLI parser tests for:
|
||||||
|
- `generate near-term`;
|
||||||
|
- `generate near-term --config PATH`;
|
||||||
|
- `generate near-term --units us --tz America/Chicago --out PATH`;
|
||||||
|
- rejected `generate near-term --date YYYY-MM-DD`;
|
||||||
|
- rejected storm-only `--start` / `--end` on near-term if current parser
|
||||||
|
behavior supports this distinction.
|
||||||
|
- Add app resolution tests proving `ReportNearTerm` resolves `report.NearTerm`.
|
||||||
|
- Update help tests to assert `generate near-term` appears.
|
||||||
|
- Existing generate and run command tests must remain unchanged.
|
||||||
|
|
||||||
|
Validation:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
go test ./internal/app ./internal/cli
|
||||||
|
go run ./cmd/weatherreporter --help
|
||||||
|
```
|
||||||
|
|
||||||
|
This stage is small enough for one implementation prompt.
|
||||||
|
|
||||||
|
## Stage 5: End-To-End Generation And Artifact Behavior
|
||||||
|
|
||||||
|
Goal: prove a near-term report can run through the app workflow and persist the
|
||||||
|
expected artifacts.
|
||||||
|
|
||||||
Files to inspect:
|
Files to inspect:
|
||||||
|
|
||||||
- `internal/app/app_test.go`
|
- `internal/app/app_test.go`
|
||||||
- `internal/promptinput/package_test.go`
|
- `internal/state`
|
||||||
- Weather API test server fixtures in `internal/adapters/weatherapi`
|
- `internal/promptinput`
|
||||||
- `internal/state` artifact path behavior if tests inspect saved files
|
- Weather API fixture server helpers in app tests
|
||||||
|
- distributor notification tests if report paths are asserted
|
||||||
|
|
||||||
Implementation:
|
Implementation:
|
||||||
|
|
||||||
- Extend app-level Weather API fixtures to serve convective outlook data.
|
- Add an app-level test that generates `ReportNearTerm` with fixture weather
|
||||||
- Add or update workflow tests so saved YAML contains:
|
data and fake Scriptorium.
|
||||||
- `briefing.applicable_risk_products.spc_convective_outlooks`;
|
- Assert:
|
||||||
- `briefing.narrative_products.spc_convective_discussion` when severity rank
|
- `ReportResult.Metadata.ReportID == report.NearTerm`;
|
||||||
is at least `3`;
|
- prompt ID is `weather.near_term_report`;
|
||||||
- no geometry in prompt-facing YAML.
|
- managed report path uses artifact group `near-term`;
|
||||||
- Add a workflow case where outlook data is checked but empty and the
|
- data package path uses artifact group `near-term`;
|
||||||
risk-product stanza remains explicit.
|
- module snapshot contains the near-term module list in order;
|
||||||
- Add a workflow case where severity rank is below `3` and the discussion
|
- data package categories are unchanged;
|
||||||
stanza is omitted.
|
- `hourly_forecast` contains only periods overlapping the six-hour window;
|
||||||
- Keep existing Recent Changes behavior unchanged unless a later roadmap adds
|
- `precip_timing` reflects only the six-hour window;
|
||||||
comparisons for SPC outlooks.
|
- `alert_digest` and SPC stanzas respect overlap behavior;
|
||||||
|
- AFD includes key messages and short term but not long term;
|
||||||
|
- `recent_changes.items` is empty when no rolling-window comparison exists.
|
||||||
|
- Add an app-level case for distributor notification only if existing tests
|
||||||
|
assert report-specific path rendering. The expected distributor templates
|
||||||
|
should work through existing `{artifact_group}`, `{report_id}`,
|
||||||
|
`{valid_start_date}`, and `{valid_start_time}` values without special
|
||||||
|
near-term behavior.
|
||||||
|
- Do not add `near_term` to scheduled batch tests.
|
||||||
|
|
||||||
Acceptance criteria:
|
Tests:
|
||||||
|
|
||||||
- Module snapshot and YAML data package remain deterministic.
|
- Add stale-key checks if generated YAML is inspected:
|
||||||
- Prompt category grouping preserves module order within categories.
|
- module intervals use `period_begins` / `period_ends`;
|
||||||
- No Scriptorium or distributor behavior changes are required.
|
- top-level metadata keeps canonical `valid_period`.
|
||||||
|
- Ensure optional output copy via `--out` continues to use existing app copy
|
||||||
|
behavior for generated reports.
|
||||||
|
|
||||||
Suggested validation:
|
Validation:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
go test ./internal/app ./internal/promptinput ./internal/state
|
go test ./internal/app ./internal/state ./internal/promptinput
|
||||||
```
|
```
|
||||||
|
|
||||||
This stage is small enough for one implementation prompt.
|
This stage is small enough for one implementation prompt.
|
||||||
|
|
||||||
## Stage 9: Implemented Documentation
|
## Stage 6: Config Overrides And Examples
|
||||||
|
|
||||||
Goal: update non-roadmap docs after the feature is implemented.
|
Goal: allow configured module overrides for `near_term` while keeping
|
||||||
|
maintained examples valid.
|
||||||
|
|
||||||
|
Files to inspect:
|
||||||
|
|
||||||
|
- `internal/config/reports.go`
|
||||||
|
- `internal/config/config_test.go`
|
||||||
|
- `docs/config.md` after implementation
|
||||||
|
- `examples/config.yml`
|
||||||
|
|
||||||
|
Implementation:
|
||||||
|
|
||||||
|
- Add report config key aliases:
|
||||||
|
- `near_term`
|
||||||
|
- `near-term`
|
||||||
|
- Map both aliases to `report.NearTerm`.
|
||||||
|
- Validate near-term module overrides through the existing module registry.
|
||||||
|
- Ensure incompatible modules fail clearly, for example
|
||||||
|
`derived_daily_summary` should not be compatible with `near_term`.
|
||||||
|
- Update config tests for:
|
||||||
|
- successful `reports.near_term.deterministic_modules`;
|
||||||
|
- successful `reports.near-term.deterministic_modules`;
|
||||||
|
- duplicate canonical near-term aliases rejected if both are present;
|
||||||
|
- incompatible daily-only module rejected.
|
||||||
|
- Update `examples/config.yml` only if it enumerates all report module
|
||||||
|
overrides. If it does not need a near-term override, do not add one just to
|
||||||
|
demonstrate the feature.
|
||||||
|
|
||||||
|
Validation:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
go test ./internal/config ./internal/report ./internal/briefing
|
||||||
|
```
|
||||||
|
|
||||||
|
This stage is small enough for one implementation prompt.
|
||||||
|
|
||||||
|
## Stage 7: Implemented Documentation
|
||||||
|
|
||||||
|
Goal: update non-roadmap documentation after the feature exists.
|
||||||
|
|
||||||
Files to inspect and update:
|
Files to inspect and update:
|
||||||
|
|
||||||
- `docs/integrations/weatherapi.md`
|
- `docs/cli.md`
|
||||||
- `docs/internal/weather-data.md`
|
- `docs/config.md`
|
||||||
- `docs/internal/facts.md`
|
- `docs/operations.md`
|
||||||
|
- `docs/internal/report-registry.md`
|
||||||
- `docs/internal/module.md`
|
- `docs/internal/module.md`
|
||||||
|
- `docs/internal/facts.md`
|
||||||
- `docs/internal/briefing.md`
|
- `docs/internal/briefing.md`
|
||||||
- `docs/internal/prompt-input.md`
|
- `docs/internal/prompt-input.md`
|
||||||
- `docs/config.md` if example module override behavior changes
|
- `examples/config.yml`, only if changed in Stage 6
|
||||||
- `examples/config.yml` if not already updated in Stage 7
|
|
||||||
|
|
||||||
Documentation requirements:
|
Documentation requirements:
|
||||||
|
|
||||||
- Describe only the implemented SPC behavior outside `docs/roadmap/`.
|
- Describe `generate near-term` in CLI docs after implementation.
|
||||||
- In the Weather API integration doc, include only the route, query, response
|
- Document that the first version is explicit generation only and is not part
|
||||||
fields, and missing/empty semantics used by weatherreporter.
|
of scheduled batches.
|
||||||
- In internal docs, distinguish:
|
- Document the six-hour rolling valid period and that the duration is an
|
||||||
- collected source facts and geometry/provenance;
|
internal constant, not a config field.
|
||||||
- derived report-period filtering;
|
- Document near-term report identity, artifact group, batch output name, prompt
|
||||||
- prompt-facing module output that omits geometry.
|
ID, and module composition in internal docs.
|
||||||
- In prompt-input docs, list:
|
- Document near-term config override keys only if Stage 6 implements them.
|
||||||
- `spc_convective_outlooks` under `applicable_risk_products`;
|
- Keep deferred items under roadmap docs only:
|
||||||
- `spc_convective_discussion` under `narrative_products`.
|
- configurable duration;
|
||||||
- Keep deferred route choices, geometry presentation, and user-configurable
|
- batch membership;
|
||||||
threshold ideas under roadmap docs only.
|
- dedicated `derived_near_term_summary`;
|
||||||
|
- near-term Recent Changes comparison output.
|
||||||
|
|
||||||
Acceptance criteria:
|
Acceptance criteria:
|
||||||
|
|
||||||
- Non-roadmap docs do not describe deferred SPC behavior as current behavior.
|
- Non-roadmap docs describe only implemented behavior.
|
||||||
|
- Docs do not imply a duration config field or scheduled batch behavior.
|
||||||
- Maintained examples load.
|
- Maintained examples load.
|
||||||
- Documentation links and module ID lists are consistent with code.
|
|
||||||
|
|
||||||
Suggested validation:
|
Validation:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
go test ./internal/config
|
go test ./internal/config
|
||||||
@@ -522,71 +445,54 @@ git diff --check
|
|||||||
|
|
||||||
This stage is small enough for one implementation prompt.
|
This stage is small enough for one implementation prompt.
|
||||||
|
|
||||||
## Stage 10: Final Validation
|
## Stage 8: Final Validation
|
||||||
|
|
||||||
Goal: validate the complete feature and guard against regressions.
|
Goal: run the complete project validation after implementation and docs are
|
||||||
|
updated.
|
||||||
|
|
||||||
Run:
|
Commands:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
go test ./internal/adapters/weatherapi ./internal/weatherdata ./internal/facts ./internal/forecast ./internal/briefing ./internal/module ./internal/report ./internal/config ./internal/app ./internal/promptinput
|
|
||||||
go test ./...
|
go test ./...
|
||||||
go run ./cmd/weatherreporter --help
|
go run ./cmd/weatherreporter --help
|
||||||
git diff --check
|
git diff --check
|
||||||
```
|
```
|
||||||
|
|
||||||
Manual review:
|
Manual checks:
|
||||||
|
|
||||||
- Confirm `/outlooks/convective` is referenced through an adapter constant.
|
- `weatherreporter --help` lists `generate near-term`.
|
||||||
- Confirm Weather API outlook requests do not send `precision`.
|
- No existing command syntax changed.
|
||||||
- Confirm no module builder performs Weather API calls.
|
- `near_term` is absent from morning and evening batch membership.
|
||||||
- Confirm prompt YAML omits GeoJSON geometry.
|
- No non-roadmap docs describe unimplemented deferred near-term work.
|
||||||
- Confirm checked-empty outlook data is not represented as missing data.
|
- No config examples include secrets or invalid module IDs.
|
||||||
- Confirm SPC discussion text appears only for categorical outlooks at severity
|
- Distributor bundle path rendering remains template-driven and does not need
|
||||||
rank `3` or higher.
|
report-specific branching.
|
||||||
- Confirm public CLI syntax, output paths, distributor upload behavior, and
|
|
||||||
Scriptorium argv remain unchanged.
|
|
||||||
|
|
||||||
This stage is small enough for one implementation prompt.
|
This stage is small enough for one implementation prompt.
|
||||||
|
|
||||||
## Deferred Work
|
## Deferred Work
|
||||||
|
|
||||||
Out of scope for the initial implementation:
|
Do not include these in the first implementation:
|
||||||
|
|
||||||
- use of `/outlooks/convective/active`;
|
- user-configurable near-term duration;
|
||||||
- use of `/outlooks/convective/location`;
|
- scheduled near-term batch membership or a new high-frequency batch command;
|
||||||
- per-report Weather API filters such as `day` or `outlookType`;
|
- dedicated `derived_near_term_summary`;
|
||||||
- user-configurable SPC discussion severity threshold;
|
- narrative forecast periods in the default near-term module list;
|
||||||
- prompt-facing GeoJSON geometry;
|
- separate AFD section modules;
|
||||||
- polygon distance, area, map summaries, or rendered images;
|
- rolling-window Recent Changes comparison output;
|
||||||
- Mesoscale Discussions, watches, WPC outlooks, radar, QPF, or other risk
|
- custom CLI duration flags;
|
||||||
products;
|
- distributor-specific behavior for near-term reports.
|
||||||
- Recent Changes comparisons for SPC outlook changes;
|
|
||||||
- module-owned upstream fetching.
|
|
||||||
|
|
||||||
## Global Validation Checklist
|
|
||||||
|
|
||||||
Before considering the feature complete:
|
|
||||||
|
|
||||||
- all focused package tests pass;
|
|
||||||
- `go test ./...` passes;
|
|
||||||
- `go run ./cmd/weatherreporter --help` still matches documented CLI syntax;
|
|
||||||
- `git diff --check` passes;
|
|
||||||
- examples load through config tests;
|
|
||||||
- non-roadmap docs describe only implemented behavior;
|
|
||||||
- no secret values or large raw geometry are introduced into prompt-facing
|
|
||||||
output;
|
|
||||||
- source provenance and warnings remain inspectable through existing metadata
|
|
||||||
and state artifacts.
|
|
||||||
|
|
||||||
## Open Questions
|
## Open Questions
|
||||||
|
|
||||||
No question blocks implementation.
|
None block implementation.
|
||||||
|
|
||||||
The recommended approach is to implement the locked decisions exactly as
|
Recommendation: keep the first version explicit and narrow: `generate
|
||||||
described above. The main viable alternative is to query
|
near-term`, six-hour constant, no batch membership, no Recent Changes output.
|
||||||
`/outlooks/convective/active` or `/outlooks/convective/location`, but that
|
This fits the existing registry/module architecture and lets prompt quality be
|
||||||
would make tomorrow, multi-day, weekend, and event reports depend on
|
tested before adding scheduler or comparison complexity.
|
||||||
server-current active filtering rather than report valid periods. That
|
|
||||||
alternative should be deferred unless fixture payloads from the base latest-run
|
Viable alternative: implement rolling-window Recent Changes immediately by
|
||||||
route prove too large or too irrelevant for prompt use.
|
finding the most recent prior `near_term` report with an overlapping or adjacent
|
||||||
|
window. That could be useful later, but it needs a well-defined comparison
|
||||||
|
contract and should not block the first report implementation.
|
||||||
|
|||||||
363
docs/roadmap/near-term.md
Normal file
363
docs/roadmap/near-term.md
Normal file
@@ -0,0 +1,363 @@
|
|||||||
|
# Near-Term Report Roadmap
|
||||||
|
|
||||||
|
## Purpose
|
||||||
|
|
||||||
|
This roadmap defines planned work to add a rolling `near_term` report focused
|
||||||
|
on the next several hours. The feature is not implemented yet, so this document
|
||||||
|
lives under `docs/roadmap/`.
|
||||||
|
|
||||||
|
The goal is a frequently generated report that helps readers understand what
|
||||||
|
matters in the immediate future: current conditions, hourly evolution,
|
||||||
|
precipitation timing, applicable hazards, applicable SPC risk products, and the
|
||||||
|
most relevant short-term narrative context.
|
||||||
|
|
||||||
|
## Target Behavior
|
||||||
|
|
||||||
|
Add a generated report with:
|
||||||
|
|
||||||
|
- report ID: `near_term`
|
||||||
|
- display name: `Near-Term Report`
|
||||||
|
- prompt ID: `weather.near_term_report`
|
||||||
|
- artifact group: `near-term`
|
||||||
|
- batch output name: `near-term.md`
|
||||||
|
- default valid-period length: 6 hours
|
||||||
|
- valid period: `[generation_time, generation_time + nearTermHours)`
|
||||||
|
|
||||||
|
The report should use a package-owned constant for the valid-period length, for
|
||||||
|
example:
|
||||||
|
|
||||||
|
```go
|
||||||
|
const nearTermHours = 6
|
||||||
|
```
|
||||||
|
|
||||||
|
Do not make the duration configurable in the first implementation. The constant
|
||||||
|
exists so the value can be changed later to 4 or 8 hours without changing
|
||||||
|
valid-period logic in multiple places.
|
||||||
|
|
||||||
|
## Locked Decisions
|
||||||
|
|
||||||
|
- The report ID is `near_term`.
|
||||||
|
- The report is rolling and generation-time anchored, not civil-day anchored.
|
||||||
|
- The first implementation covers the next 6 hours through a constant.
|
||||||
|
- The report should be generated explicitly by CLI command before deciding
|
||||||
|
whether it belongs in scheduled batches.
|
||||||
|
- The first implementation should declare `rolling_window` comparison policy
|
||||||
|
but should not emit Recent Changes output for `near_term`.
|
||||||
|
- Alert and SPC products should be included only when their valid periods
|
||||||
|
overlap the resolved near-term report period.
|
||||||
|
- SPC discussion text should keep the existing categorical-risk threshold rule
|
||||||
|
and should still require overlap with the near-term report period.
|
||||||
|
- The AFD stanza should include key messages and short term text by default.
|
||||||
|
- Daily-only modules should not be forced into this report.
|
||||||
|
|
||||||
|
## Report Definition
|
||||||
|
|
||||||
|
Add a report definition under `internal/report`, preferably in a focused
|
||||||
|
`near_term_report.go` file.
|
||||||
|
|
||||||
|
Definition fields:
|
||||||
|
|
||||||
|
- `ID`: `NearTerm`
|
||||||
|
- `Name`: `Near-Term Report`
|
||||||
|
- `PromptID`: `weather.near_term_report`
|
||||||
|
- `ComparisonStrategy`: `rolling_window`
|
||||||
|
- `ArtifactGroup`: `near-term`
|
||||||
|
- `BatchOutputName`: `near-term.md`
|
||||||
|
- `Generated`: `true`
|
||||||
|
- `CompatiblePriorIDs`: `[]ID{NearTerm}`
|
||||||
|
- `Modules`: near-term module list below
|
||||||
|
- `resolve`: rolling near-term resolver
|
||||||
|
|
||||||
|
Valid-period resolver:
|
||||||
|
|
||||||
|
```go
|
||||||
|
func resolveNearTerm(req ResolveRequest) (timeutil.Period, error) {
|
||||||
|
localNow := req.Now.In(req.Location)
|
||||||
|
return timeutil.Period{
|
||||||
|
Start: localNow,
|
||||||
|
End: localNow.Add(nearTermHours * time.Hour),
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
The implementation should use idiomatic package-local constants and avoid
|
||||||
|
duplicating duration literals in tests or app code.
|
||||||
|
|
||||||
|
## Default Module Composition
|
||||||
|
|
||||||
|
Default module order should be:
|
||||||
|
|
||||||
|
1. `metadata`
|
||||||
|
2. `current_conditions`
|
||||||
|
3. `hourly_forecast`
|
||||||
|
4. `precip_timing`
|
||||||
|
5. `alert_digest`
|
||||||
|
6. `spc_convective_outlooks`
|
||||||
|
7. `area_forecast_discussion`
|
||||||
|
8. `spc_convective_discussion`
|
||||||
|
9. `weather_story`
|
||||||
|
|
||||||
|
`area_forecast_discussion` should use options equivalent to:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
sections:
|
||||||
|
- key_messages
|
||||||
|
- short_term
|
||||||
|
```
|
||||||
|
|
||||||
|
Do not include these daily/daypart-oriented modules initially:
|
||||||
|
|
||||||
|
- `derived_daily_summary`
|
||||||
|
- `derived_daypart_summaries`
|
||||||
|
- `outdoor_windows`
|
||||||
|
- `tomorrow_planning`
|
||||||
|
|
||||||
|
If the report needs deterministic summary facts later, add a purpose-built
|
||||||
|
module such as `derived_near_term_summary` rather than stretching daily modules
|
||||||
|
into a rolling sub-daily context.
|
||||||
|
|
||||||
|
## Prompt Package Shape
|
||||||
|
|
||||||
|
The existing prompt-input category layout should remain unchanged:
|
||||||
|
|
||||||
|
- `applicable_risk_products`
|
||||||
|
- `derived_summaries`
|
||||||
|
- `narrative_products`
|
||||||
|
- `raw_data`
|
||||||
|
|
||||||
|
Near-term output is expected to emphasize:
|
||||||
|
|
||||||
|
- `current_conditions` and `hourly_forecast` under `raw_data`
|
||||||
|
- `precip_timing` under `derived_summaries`
|
||||||
|
- `alert_digest` and `spc_convective_outlooks` under
|
||||||
|
`applicable_risk_products`
|
||||||
|
- `area_forecast_discussion`, `spc_convective_discussion`, and
|
||||||
|
`weather_story` under `narrative_products`
|
||||||
|
|
||||||
|
Module interval fields should use the existing prompt-facing
|
||||||
|
`period_begins` / `period_ends` convention.
|
||||||
|
|
||||||
|
## Overlap And Filtering Rules
|
||||||
|
|
||||||
|
Hourly forecast:
|
||||||
|
|
||||||
|
- use only hourly periods overlapping the near-term valid period;
|
||||||
|
- preserve hourly period order;
|
||||||
|
- do not include the full daily forecast.
|
||||||
|
|
||||||
|
Narrative products:
|
||||||
|
|
||||||
|
- AFD key messages and short term text may be included because the AFD is an
|
||||||
|
official short-term forecast discussion product;
|
||||||
|
- narrative forecast periods are not included by default unless a later prompt
|
||||||
|
test shows they improve near-term output.
|
||||||
|
|
||||||
|
Alerts:
|
||||||
|
|
||||||
|
- include active alert overlaps only when the alert overlaps the near-term
|
||||||
|
valid period;
|
||||||
|
- if alerts were checked successfully and no alerts overlap, emit checked empty
|
||||||
|
alert context through existing module behavior.
|
||||||
|
|
||||||
|
SPC outlooks:
|
||||||
|
|
||||||
|
- include only retained outlooks whose valid periods overlap the near-term
|
||||||
|
valid period;
|
||||||
|
- do not include non-overlapping outlooks even if they are severe;
|
||||||
|
- keep current prompt-facing field exclusions for geometry, severity rank,
|
||||||
|
expiration time, and source URL.
|
||||||
|
|
||||||
|
SPC discussion:
|
||||||
|
|
||||||
|
- include discussion only for SPC days where a retained overlapping categorical
|
||||||
|
outlook has severity rank at least `3`;
|
||||||
|
- do not include discussion for low-risk, non-categorical-only, or
|
||||||
|
non-overlapping outlooks.
|
||||||
|
|
||||||
|
Weather story:
|
||||||
|
|
||||||
|
- include when available under current optional-source behavior;
|
||||||
|
- do not require the story valid period to exactly match the near-term period
|
||||||
|
unless later testing shows stale stories are a problem.
|
||||||
|
|
||||||
|
## Implementation Stages
|
||||||
|
|
||||||
|
### Stage 1: Report Registry
|
||||||
|
|
||||||
|
Goal: add the `near_term` report definition and valid-period resolver.
|
||||||
|
|
||||||
|
Files to inspect or update:
|
||||||
|
|
||||||
|
- `internal/report/definition.go`
|
||||||
|
- `internal/report/registry.go`
|
||||||
|
- `internal/report/period.go`
|
||||||
|
- new `internal/report/near_term_report.go`
|
||||||
|
- `internal/report/period_test.go`
|
||||||
|
- `docs/internal/report-registry.md` after implementation
|
||||||
|
|
||||||
|
Acceptance criteria:
|
||||||
|
|
||||||
|
- `report.DefaultRegistry().Lookup(report.NearTerm)` succeeds.
|
||||||
|
- `report.Registry.All()` includes the report in a stable order.
|
||||||
|
- resolving the report at a fixed generation time produces a half-open
|
||||||
|
six-hour period.
|
||||||
|
- report metadata and RunID include `near_term`.
|
||||||
|
|
||||||
|
### Stage 2: Module Compatibility And Defaults
|
||||||
|
|
||||||
|
Goal: allow existing relevant modules to build for `near_term` and define the
|
||||||
|
default near-term composition.
|
||||||
|
|
||||||
|
Files to inspect or update:
|
||||||
|
|
||||||
|
- `internal/briefing/modules.go`
|
||||||
|
- `internal/report/near_term_report.go`
|
||||||
|
- `internal/briefing/*_module_test.go`
|
||||||
|
- `internal/module/module_test.go`
|
||||||
|
|
||||||
|
Acceptance criteria:
|
||||||
|
|
||||||
|
- all default near-term modules validate and build from appropriate test facts;
|
||||||
|
- daily-only modules remain incompatible unless intentionally expanded;
|
||||||
|
- `area_forecast_discussion` defaults to key messages and short term only for
|
||||||
|
this report.
|
||||||
|
|
||||||
|
### Stage 3: Derived Facts
|
||||||
|
|
||||||
|
Goal: make `internal/facts` derive report-period facts for `near_term`.
|
||||||
|
|
||||||
|
Files to inspect or update:
|
||||||
|
|
||||||
|
- `internal/facts/facts.go`
|
||||||
|
- `internal/facts/facts_test.go`
|
||||||
|
- `internal/forecast` selection helpers, if needed
|
||||||
|
|
||||||
|
Acceptance criteria:
|
||||||
|
|
||||||
|
- valid-period hourly periods are sliced to the six-hour window;
|
||||||
|
- precipitation timing is built from that six-hour hourly slice;
|
||||||
|
- alert overlaps use the near-term valid period;
|
||||||
|
- SPC outlook and discussion derivation use the near-term valid period;
|
||||||
|
- no daily summaries or daypart summaries are required for the default
|
||||||
|
near-term module set.
|
||||||
|
|
||||||
|
### Stage 4: CLI And App Wiring
|
||||||
|
|
||||||
|
Goal: add explicit generation support without changing existing commands.
|
||||||
|
|
||||||
|
Files to inspect or update:
|
||||||
|
|
||||||
|
- `internal/app`
|
||||||
|
- `internal/cli`
|
||||||
|
- `cmd/weatherreporter/main.go`
|
||||||
|
- `docs/cli.md` after implementation
|
||||||
|
|
||||||
|
Expected command:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
weatherreporter generate near-term [--config PATH] [--units VALUE] [--tz NAME] [--out PATH]
|
||||||
|
```
|
||||||
|
|
||||||
|
Acceptance criteria:
|
||||||
|
|
||||||
|
- command maps to `report.NearTerm`;
|
||||||
|
- no `--date`, `--start`, or `--end` flags are added for this report;
|
||||||
|
- existing `generate daily`, `generate tomorrow`, `generate three-day`,
|
||||||
|
`generate weekend`, and `generate storm` behavior is unchanged;
|
||||||
|
- CLI help lists the new command.
|
||||||
|
|
||||||
|
### Stage 5: Prompt Input And State Coverage
|
||||||
|
|
||||||
|
Goal: prove the managed module snapshot and data package contain the intended
|
||||||
|
near-term stanzas.
|
||||||
|
|
||||||
|
Files to inspect or update:
|
||||||
|
|
||||||
|
- `internal/app/app_test.go`
|
||||||
|
- `internal/promptinput`
|
||||||
|
- `internal/state`
|
||||||
|
|
||||||
|
Acceptance criteria:
|
||||||
|
|
||||||
|
- generated module snapshot includes the near-term default module list in
|
||||||
|
order;
|
||||||
|
- data package uses existing categories;
|
||||||
|
- alert/SPC stanzas are omitted or checked-empty according to current module
|
||||||
|
behavior and valid-period overlap;
|
||||||
|
- report metadata preserves canonical `valid_period`;
|
||||||
|
- module interval labels use `period_begins` and `period_ends`.
|
||||||
|
|
||||||
|
### Stage 6: Documentation And Examples
|
||||||
|
|
||||||
|
Goal: document implemented behavior only after the code exists.
|
||||||
|
|
||||||
|
Files to inspect or update after implementation:
|
||||||
|
|
||||||
|
- `docs/cli.md`
|
||||||
|
- `docs/internal/report-registry.md`
|
||||||
|
- `docs/internal/module.md`
|
||||||
|
- `docs/internal/facts.md`
|
||||||
|
- `docs/internal/briefing.md`
|
||||||
|
- `docs/config.md` only if report module overrides need an example update
|
||||||
|
- `examples/config.yml` only if it lists report module overrides
|
||||||
|
|
||||||
|
Acceptance criteria:
|
||||||
|
|
||||||
|
- non-roadmap docs describe `near_term` only after implementation;
|
||||||
|
- future or deferred near-term summary modules remain under roadmap docs;
|
||||||
|
- examples, if updated, load through existing config tests.
|
||||||
|
|
||||||
|
## Test Plan
|
||||||
|
|
||||||
|
Focused tests:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
go test ./internal/report ./internal/briefing ./internal/facts
|
||||||
|
go test ./internal/app ./internal/cli ./internal/promptinput
|
||||||
|
```
|
||||||
|
|
||||||
|
Full validation:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
go test ./...
|
||||||
|
go run ./cmd/weatherreporter --help
|
||||||
|
git diff --check
|
||||||
|
```
|
||||||
|
|
||||||
|
Important coverage:
|
||||||
|
|
||||||
|
- six-hour valid-period resolution from a fixed generation time;
|
||||||
|
- timezone-aware period start and end;
|
||||||
|
- no civil-day truncation;
|
||||||
|
- hourly periods selected by overlap with the rolling window;
|
||||||
|
- alert overlap filtering;
|
||||||
|
- SPC outlook filtering by overlap;
|
||||||
|
- SPC discussion filtering by overlap plus categorical severity threshold;
|
||||||
|
- AFD key messages and short term included;
|
||||||
|
- daily-only modules rejected or absent from default near-term composition;
|
||||||
|
- distributor bundle path templates render sensibly for a sub-daily report,
|
||||||
|
especially valid start time variables.
|
||||||
|
|
||||||
|
## Deferred Work
|
||||||
|
|
||||||
|
Do not include these in the first implementation:
|
||||||
|
|
||||||
|
- user-configurable near-term duration;
|
||||||
|
- adding `near_term` to scheduled morning or evening batches;
|
||||||
|
- a dedicated `derived_near_term_summary` module;
|
||||||
|
- narrative forecast periods by default;
|
||||||
|
- separate AFD section modules;
|
||||||
|
- near-term-specific Recent Changes comparison output;
|
||||||
|
- CLI flags for custom near-term duration.
|
||||||
|
|
||||||
|
## Open Questions
|
||||||
|
|
||||||
|
None block the roadmap.
|
||||||
|
|
||||||
|
Recommendation: implement the first version as explicit `generate near-term`
|
||||||
|
only, with no scheduled batch membership. That keeps the user-visible behavior
|
||||||
|
small while the prompt and generated report are tested.
|
||||||
|
|
||||||
|
Viable alternative: add `near_term` to a frequent scheduled batch immediately.
|
||||||
|
That may be useful operationally, but it should wait until the report cadence
|
||||||
|
and downstream distributor behavior are clear.
|
||||||
@@ -1,190 +0,0 @@
|
|||||||
# SPC Convective Outlook Roadmap
|
|
||||||
|
|
||||||
## Purpose
|
|
||||||
|
|
||||||
This roadmap defines future work to add SPC convective outlook support to
|
|
||||||
`weatherreporter`. The feature is not implemented yet, so current user,
|
|
||||||
operator, integration, and internal documentation must not describe it as
|
|
||||||
available behavior until the implementation lands.
|
|
||||||
|
|
||||||
The goal is to consume location-filtered SPC convective outlook facts from the
|
|
||||||
Weather API once per report run, preserve source provenance, and expose concise
|
|
||||||
prompt-facing risk and narrative stanzas without making modules responsible for
|
|
||||||
upstream fetching.
|
|
||||||
|
|
||||||
## Upstream Contract
|
|
||||||
|
|
||||||
The Weather API currently documents these convective outlook routes:
|
|
||||||
|
|
||||||
- `GET /outlooks/convective`
|
|
||||||
- `GET /outlooks/convective/active`
|
|
||||||
- `GET /outlooks/convective/location`
|
|
||||||
|
|
||||||
The initial `weatherreporter` integration should use the latest-run route,
|
|
||||||
`/outlooks/convective`, because report valid periods may target tomorrow,
|
|
||||||
multi-day, weekend, or event windows. The `/active` and `/location` routes
|
|
||||||
filter using the server's current UTC time, which is useful for "active right
|
|
||||||
now" views but is too narrow for report-period-oriented generation.
|
|
||||||
|
|
||||||
Important response semantics:
|
|
||||||
|
|
||||||
- `data: null` means no latest outlook run exists.
|
|
||||||
- a non-null `data` object with empty `outlooks` and `discussions` arrays means
|
|
||||||
the endpoint was checked successfully and no matching outlooks were present.
|
|
||||||
- `precision` and unknown query parameters are rejected by the upstream
|
|
||||||
outlook routes.
|
|
||||||
- outlook GeoJSON coordinates use longitude, latitude order.
|
|
||||||
- `format`, `units`, and `tz` are supported by the upstream contract, but the
|
|
||||||
initial weatherreporter request should send only values needed for JSON
|
|
||||||
decoding and local-time presentation.
|
|
||||||
|
|
||||||
## Locked Decisions
|
|
||||||
|
|
||||||
- Add the source as an optional Weather API source named
|
|
||||||
`spc_convective_outlooks`.
|
|
||||||
- Define the Weather API endpoint path with a package-level constant in the
|
|
||||||
Weather API adapter rather than embedding a string literal throughout the
|
|
||||||
implementation.
|
|
||||||
- Start with the base latest-run route, not `/active` or `/location`.
|
|
||||||
- Fetch outlook facts once in the Weather API adapter and expose them through
|
|
||||||
`weatherdata.Bundle`, `facts.CollectedFacts`, and report-scoped derived
|
|
||||||
filtering.
|
|
||||||
- Do not let module builders make Weather API calls.
|
|
||||||
- Keep GeoJSON geometry in collected facts and persisted bundle/debug artifacts,
|
|
||||||
but omit geometry from prompt-facing module output by default.
|
|
||||||
- Add two prompt-facing modules backed by the same collected source:
|
|
||||||
`spc_convective_outlooks` and `spc_convective_discussion`.
|
|
||||||
- Place `spc_convective_outlooks` under `applicable_risk_products`.
|
|
||||||
- Place `spc_convective_discussion` under `narrative_products`, immediately
|
|
||||||
after `area_forecast_discussion` in report module order when both are present.
|
|
||||||
- Include SPC outlook discussion text only when at least one retained
|
|
||||||
categorical outlook for the report valid period has `severity_rank >= 3`.
|
|
||||||
- Define that threshold as an internal constant so it can be adjusted later
|
|
||||||
without searching through module code.
|
|
||||||
- Treat a non-null run with empty arrays as checked empty data, not missing
|
|
||||||
data.
|
|
||||||
- Treat `data: null`, HTTP errors, and malformed payloads as optional-source
|
|
||||||
missing or malformed conditions using the configured missing-source policy.
|
|
||||||
|
|
||||||
## Target Internal Shape
|
|
||||||
|
|
||||||
Add normalized collected facts to `internal/weatherdata`:
|
|
||||||
|
|
||||||
- `ConvectiveOutlookRun`
|
|
||||||
- `ConvectiveOutlook`
|
|
||||||
- `ConvectiveOutlookDiscussion`
|
|
||||||
|
|
||||||
The run should include upstream run metadata, ordered outlooks, ordered
|
|
||||||
discussions, and enough raw/provenance data for inspection. The bundle should
|
|
||||||
gain a field similar to:
|
|
||||||
|
|
||||||
```go
|
|
||||||
SPCConvectiveOutlooks *weatherdata.ConvectiveOutlookRun
|
|
||||||
```
|
|
||||||
|
|
||||||
`facts.CollectedFacts` should expose the same collected source. Derived facts
|
|
||||||
should provide report-period-filtered outlooks and discussions, or a small
|
|
||||||
forecast/facts helper should perform that filtering before module builders
|
|
||||||
shape prompt output. The filtering rule should use overlap with the resolved
|
|
||||||
report valid period, not server-current active status.
|
|
||||||
|
|
||||||
## Prompt-Facing Shape
|
|
||||||
|
|
||||||
The risk-product module should be concise and location-oriented:
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
briefing:
|
|
||||||
applicable_risk_products:
|
|
||||||
spc_convective_outlooks:
|
|
||||||
checked: true
|
|
||||||
as_of: "2026-06-12 at 7:00 AM"
|
|
||||||
issued_at: "2026-06-12 at 6:00 AM"
|
|
||||||
outlooks:
|
|
||||||
- day: 1
|
|
||||||
outlook_type: categorical
|
|
||||||
label: SLGT
|
|
||||||
label_text: Slight Risk
|
|
||||||
period_begins: "2026-06-12 at 8:00 AM"
|
|
||||||
period_ends: "2026-06-13 at 7:00 AM"
|
|
||||||
contains_location: true
|
|
||||||
image_url: "https://..."
|
|
||||||
```
|
|
||||||
|
|
||||||
The discussion module should be separate narrative context:
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
briefing:
|
|
||||||
narrative_products:
|
|
||||||
area_forecast_discussion: {}
|
|
||||||
spc_convective_discussion:
|
|
||||||
included_because: "categorical severity_rank >= 3"
|
|
||||||
discussions:
|
|
||||||
- day: 1
|
|
||||||
period_begins: "2026-06-12 at 8:00 AM"
|
|
||||||
period_ends: "2026-06-13 at 7:00 AM"
|
|
||||||
headline: "Severe storms possible"
|
|
||||||
summary: "Scattered severe storms are possible."
|
|
||||||
discussion: "SPC discussion text."
|
|
||||||
updated_at: "2026-06-12 at 6:30 AM"
|
|
||||||
```
|
|
||||||
|
|
||||||
If outlook data is checked successfully and no report-period outlooks apply,
|
|
||||||
the risk-product stanza should make that explicit with `checked: true` and an
|
|
||||||
empty outlook count or empty list. The discussion stanza should be omitted when
|
|
||||||
the severity threshold is not met or no relevant discussion is available.
|
|
||||||
|
|
||||||
## Implementation Plan
|
|
||||||
|
|
||||||
The staged implementation plan for this feature lives in
|
|
||||||
`docs/roadmap/implementation.md`. This document remains the feature roadmap:
|
|
||||||
it defines the target state, user intent, and policy decisions that future
|
|
||||||
implementation work should preserve.
|
|
||||||
|
|
||||||
## Test Plan
|
|
||||||
|
|
||||||
Add focused coverage for:
|
|
||||||
|
|
||||||
- Weather API decode of run metadata, outlooks, discussions, and geometry;
|
|
||||||
- endpoint path/query construction using the endpoint constant;
|
|
||||||
- `data: null` optional-source policy behavior;
|
|
||||||
- non-null empty `outlooks`/`discussions` as checked empty data;
|
|
||||||
- source provenance and data hash recording;
|
|
||||||
- valid-period overlap filtering for today, tomorrow, 3-day, weekend, and
|
|
||||||
storm windows;
|
|
||||||
- risk-product module output, omitted geometry, checked-empty behavior, and
|
|
||||||
prompt category;
|
|
||||||
- discussion module severity threshold behavior and prompt category;
|
|
||||||
- report default composition validation;
|
|
||||||
- app or prompt-input workflow proving generated YAML contains the new stanzas
|
|
||||||
when fixture data warrants them.
|
|
||||||
|
|
||||||
Run at minimum:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
go test ./internal/adapters/weatherapi ./internal/weatherdata ./internal/facts ./internal/briefing ./internal/module ./internal/report ./internal/app ./internal/promptinput
|
|
||||||
go test ./...
|
|
||||||
go run ./cmd/weatherreporter --help
|
|
||||||
git diff --check
|
|
||||||
```
|
|
||||||
|
|
||||||
## Deferred Work
|
|
||||||
|
|
||||||
Do not include these in the first implementation unless a separate roadmap
|
|
||||||
expands the scope:
|
|
||||||
|
|
||||||
- calling `/outlooks/convective/active` or `/outlooks/convective/location`;
|
|
||||||
- user-configurable SPC discussion severity threshold;
|
|
||||||
- prompt-facing GeoJSON geometry;
|
|
||||||
- polygon distance, area, or map-rendered risk summaries;
|
|
||||||
- Mesoscale Discussions, watches, WPC outlooks, radar, QPF, or other risk
|
|
||||||
products;
|
|
||||||
- module-owned upstream fetching;
|
|
||||||
- custom per-report Weather API query filters such as `day` or `outlookType`.
|
|
||||||
|
|
||||||
## Open Questions
|
|
||||||
|
|
||||||
No open question blocks implementation. The recommended defaults above should
|
|
||||||
be used for the first pass. If fixture testing shows that the base latest-run
|
|
||||||
route includes too much irrelevant data, the viable alternative is to add
|
|
||||||
report-aware adapter query filters later, but that should be driven by observed
|
|
||||||
payload size or prompt quality rather than by the initial design.
|
|
||||||
Reference in New Issue
Block a user