Add roadmap and staged plan for implementing SPC convective outlook support
This commit is contained in:
190
docs/roadmap/outlook.md
Normal file
190
docs/roadmap/outlook.md
Normal file
@@ -0,0 +1,190 @@
|
||||
# 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 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
|
||||
severity_rank: 3
|
||||
valid_start: "2026-06-12 at 8:00 AM"
|
||||
valid_end: "2026-06-13 at 7:00 AM"
|
||||
contains_location: true
|
||||
source_url: "https://..."
|
||||
image_url: "https://..."
|
||||
```
|
||||
|
||||
The discussion module should be separate narrative context:
|
||||
|
||||
```yaml
|
||||
briefing:
|
||||
narrative_products:
|
||||
area_forecast_discussion: {}
|
||||
spc_convective_discussion:
|
||||
included_because: "severity_rank >= 3"
|
||||
discussions:
|
||||
- day: 1
|
||||
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