191 lines
7.5 KiB
Markdown
191 lines
7.5 KiB
Markdown
# 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.
|