Add a roadmap and implementation plan for rolling near-term forecast reports

This commit is contained in:
2026-06-12 12:24:07 -05:00
parent e6a4bb2d16
commit 0b1423c90d
3 changed files with 771 additions and 692 deletions

View File

@@ -1,519 +1,442 @@
# SPC Convective Outlook Implementation Roadmap
# Near-Term Report Implementation Roadmap
## Purpose
This roadmap defines the concrete implementation sequence for
`docs/roadmap/outlook.md`. It is written for an LLM coding agent that will
implement the stages in order.
`docs/roadmap/near-term.md`. It is written for an LLM coding agent that will
implement each stage in order.
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
Use `docs/roadmap/outlook.md` as the authoritative feature roadmap for intent,
target shape, and policy decisions. This implementation roadmap is the
step-by-step work plan. If the two documents conflict, update the feature
roadmap first when the target behavior changes, then update this implementation
plan.
Use `docs/roadmap/near-term.md` as the authoritative feature roadmap for user
intent, target behavior, and policy choices. This document is the implementation
plan. If the target behavior changes, update `near-term.md` first, then update
this roadmap.
## Locked Implementation Decisions
- Preserve existing CLI syntax, generated artifact paths, report IDs, prompt
IDs, distributor behavior, and Scriptorium invocation.
- Use source key and module IDs based on `spc_convective_outlooks`.
- Add two modules:
- `spc_convective_outlooks` in `applicable_risk_products`;
- `spc_convective_discussion` in `narrative_products`.
- Fetch the upstream Weather API source once in
`internal/adapters/weatherapi`; module builders must not fetch upstream data.
- Use a Weather API adapter constant for the path:
`/outlooks/convective`.
- Initial query parameters for the outlook endpoint are `format=json` and the
configured `tz`; do not send `precision`.
- Treat the source as optional under existing missing-source policy.
- Treat `data: null` as no latest run and therefore missing/unavailable
optional source data.
- Treat a non-null run with empty `outlooks` and `discussions` arrays as
checked, non-missing empty data.
- Preserve GeoJSON geometry in collected facts and persisted bundle artifacts,
but omit geometry from prompt-facing module output.
- Filter outlooks by overlap with the resolved report valid period in Go.
- Do not depend on `/outlooks/convective/active` or
`/outlooks/convective/location` for initial behavior.
- Include SPC discussion text only when at least one retained report-period
categorical outlook has `severity_rank >= 3`.
- Define the discussion threshold as an internal constant, initially `3`, not a
config field.
- Add report ID `near_term` and Go constant `report.NearTerm`.
- Add CLI command `weatherreporter generate near-term`.
- Do not add `--date`, `--start`, `--end`, or duration flags for this report.
- Do not add `near_term` to morning or evening batches in the first
implementation.
- Define the valid-period duration with a package-owned constant, initially
`6` hours.
- Resolve the valid period as `[generation_time, generation_time + 6h)` in the
effective report timezone.
- Use prompt ID `weather.near_term_report`.
- Use artifact group `near-term` and batch output name `near-term.md`.
- Add comparison strategy constant `CompareRollingWindow = "rolling_window"`.
- Declare `CompatiblePriorIDs: []report.ID{report.NearTerm}`, but do not emit
Recent Changes for `near_term` in the first implementation.
- Keep state prior lookup returning `nil` for rolling-window reports until a
future comparison algorithm is designed.
- Default module order is:
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`
- 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
output yet.
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.
Goal: add the `near_term` report definition, constants, registry entry, and
rolling six-hour valid-period resolver.
Files to inspect:
- `internal/report/definition.go`
- `internal/report/registry.go`
- `internal/report/period.go`
- `internal/report/daily_report.go`
- `internal/report/three_day_report.go`
- `internal/report/weekend_report.go`
- `internal/report/storm_report.go`
- `internal/config/reports.go`
- `internal/config/config_test.go`
- `examples/config.yml`
- `internal/report/period_test.go`
- `internal/state/filesystem.go`
Implementation:
- Add `spc_convective_outlooks` to default report module lists for:
- Daily Today;
- Daily Tomorrow;
- 3-Day;
- 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.
- Add `NearTerm ID = "near_term"` in `internal/report/definition.go`.
- Add `CompareRollingWindow ComparisonStrategy = "rolling_window"`.
- Add `internal/report/near_term_report.go`.
- Define a package-local duration constant in that file, for example:
Acceptance criteria:
```go
const nearTermHours = 6
```
- All default report module compositions validate.
- Example config loads successfully.
- Config override tests can include both new module IDs.
- Existing report IDs, prompt IDs, output names, and valid-period behavior are
unchanged.
- Add `nearTermDefinition()` returning:
- `ID: NearTerm`
- `Name: "Near-Term Report"`
- `PromptID: "weather.near_term_report"`
- `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
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.
## 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
the intended categories when fixture data warrants them.
Goal: make existing modules compatible with `near_term` where appropriate and
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:
- `internal/app/app_test.go`
- `internal/promptinput/package_test.go`
- Weather API test server fixtures in `internal/adapters/weatherapi`
- `internal/state` artifact path behavior if tests inspect saved files
- `internal/state`
- `internal/promptinput`
- Weather API fixture server helpers in app tests
- distributor notification tests if report paths are asserted
Implementation:
- Extend app-level Weather API fixtures to serve convective outlook data.
- Add or update workflow tests so saved YAML contains:
- `briefing.applicable_risk_products.spc_convective_outlooks`;
- `briefing.narrative_products.spc_convective_discussion` when severity rank
is at least `3`;
- no geometry in prompt-facing YAML.
- Add a workflow case where outlook data is checked but empty and the
risk-product stanza remains explicit.
- Add a workflow case where severity rank is below `3` and the discussion
stanza is omitted.
- Keep existing Recent Changes behavior unchanged unless a later roadmap adds
comparisons for SPC outlooks.
- Add an app-level test that generates `ReportNearTerm` with fixture weather
data and fake Scriptorium.
- Assert:
- `ReportResult.Metadata.ReportID == report.NearTerm`;
- prompt ID is `weather.near_term_report`;
- managed report path uses artifact group `near-term`;
- data package path uses artifact group `near-term`;
- module snapshot contains the near-term module list in order;
- data package categories are unchanged;
- `hourly_forecast` contains only periods overlapping the six-hour window;
- `precip_timing` reflects only the six-hour window;
- `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.
- Prompt category grouping preserves module order within categories.
- No Scriptorium or distributor behavior changes are required.
- Add stale-key checks if generated YAML is inspected:
- module intervals use `period_begins` / `period_ends`;
- 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
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.
## 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:
- `docs/integrations/weatherapi.md`
- `docs/internal/weather-data.md`
- `docs/internal/facts.md`
- `docs/cli.md`
- `docs/config.md`
- `docs/operations.md`
- `docs/internal/report-registry.md`
- `docs/internal/module.md`
- `docs/internal/facts.md`
- `docs/internal/briefing.md`
- `docs/internal/prompt-input.md`
- `docs/config.md` if example module override behavior changes
- `examples/config.yml` if not already updated in Stage 7
- `examples/config.yml`, only if changed in Stage 6
Documentation requirements:
- Describe only the implemented SPC behavior outside `docs/roadmap/`.
- In the Weather API integration doc, include only the route, query, response
fields, and missing/empty semantics used by weatherreporter.
- In internal docs, distinguish:
- collected source facts and geometry/provenance;
- derived report-period filtering;
- prompt-facing module output that omits geometry.
- In prompt-input docs, list:
- `spc_convective_outlooks` under `applicable_risk_products`;
- `spc_convective_discussion` under `narrative_products`.
- Keep deferred route choices, geometry presentation, and user-configurable
threshold ideas under roadmap docs only.
- Describe `generate near-term` in CLI docs after implementation.
- Document that the first version is explicit generation only and is not part
of scheduled batches.
- Document the six-hour rolling valid period and that the duration is an
internal constant, not a config field.
- Document near-term report identity, artifact group, batch output name, prompt
ID, and module composition in internal docs.
- Document near-term config override keys only if Stage 6 implements them.
- Keep deferred items under roadmap docs only:
- configurable duration;
- batch membership;
- dedicated `derived_near_term_summary`;
- near-term Recent Changes comparison output.
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.
- Documentation links and module ID lists are consistent with code.
Suggested validation:
Validation:
```bash
go test ./internal/config
@@ -522,71 +445,54 @@ git diff --check
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
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 run ./cmd/weatherreporter --help
git diff --check
```
Manual review:
Manual checks:
- Confirm `/outlooks/convective` is referenced through an adapter constant.
- Confirm Weather API outlook requests do not send `precision`.
- Confirm no module builder performs Weather API calls.
- Confirm prompt YAML omits GeoJSON geometry.
- Confirm checked-empty outlook data is not represented as missing data.
- Confirm SPC discussion text appears only for categorical outlooks at severity
rank `3` or higher.
- Confirm public CLI syntax, output paths, distributor upload behavior, and
Scriptorium argv remain unchanged.
- `weatherreporter --help` lists `generate near-term`.
- No existing command syntax changed.
- `near_term` is absent from morning and evening batch membership.
- No non-roadmap docs describe unimplemented deferred near-term work.
- No config examples include secrets or invalid module IDs.
- Distributor bundle path rendering remains template-driven and does not need
report-specific branching.
This stage is small enough for one implementation prompt.
## Deferred Work
Out of scope for the initial implementation:
Do not include these in the first implementation:
- use of `/outlooks/convective/active`;
- use of `/outlooks/convective/location`;
- per-report Weather API filters such as `day` or `outlookType`;
- user-configurable SPC discussion severity threshold;
- prompt-facing GeoJSON geometry;
- polygon distance, area, map summaries, or rendered images;
- Mesoscale Discussions, watches, WPC outlooks, radar, QPF, or other risk
products;
- 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.
- user-configurable near-term duration;
- scheduled near-term batch membership or a new high-frequency batch command;
- dedicated `derived_near_term_summary`;
- narrative forecast periods in the default near-term module list;
- separate AFD section modules;
- rolling-window Recent Changes comparison output;
- custom CLI duration flags;
- distributor-specific behavior for near-term reports.
## Open Questions
No question blocks implementation.
None block implementation.
The recommended approach is to implement the locked decisions exactly as
described above. The main viable alternative is to query
`/outlooks/convective/active` or `/outlooks/convective/location`, but that
would make tomorrow, multi-day, weekend, and event reports depend on
server-current active filtering rather than report valid periods. That
alternative should be deferred unless fixture payloads from the base latest-run
route prove too large or too irrelevant for prompt use.
Recommendation: keep the first version explicit and narrow: `generate
near-term`, six-hour constant, no batch membership, no Recent Changes output.
This fits the existing registry/module architecture and lets prompt quality be
tested before adding scheduler or comparison complexity.
Viable alternative: implement rolling-window Recent Changes immediately by
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.