7.5 KiB
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/convectiveGET /outlooks/convective/activeGET /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: nullmeans no latest outlook run exists.- a non-null
dataobject with emptyoutlooksanddiscussionsarrays means the endpoint was checked successfully and no matching outlooks were present. precisionand unknown query parameters are rejected by the upstream outlook routes.- outlook GeoJSON coordinates use longitude, latitude order.
format,units, andtzare 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
/activeor/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_outlooksandspc_convective_discussion. - Place
spc_convective_outlooksunderapplicable_risk_products. - Place
spc_convective_discussionundernarrative_products, immediately afterarea_forecast_discussionin 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:
ConvectiveOutlookRunConvectiveOutlookConvectiveOutlookDiscussion
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:
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:
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:
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: nulloptional-source policy behavior;- non-null empty
outlooks/discussionsas 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:
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/activeor/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
dayoroutlookType.
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.