From 0b1423c90d552554c8765402f15c7519a64eb716 Mon Sep 17 00:00:00 2001 From: Eric Rakestraw Date: Fri, 12 Jun 2026 12:24:07 -0500 Subject: [PATCH] Add a roadmap and implementation plan for rolling near-term forecast reports --- docs/roadmap/implementation.md | 910 +++++++++++++++------------------ docs/roadmap/near-term.md | 363 +++++++++++++ docs/roadmap/outlook.md | 190 ------- 3 files changed, 771 insertions(+), 692 deletions(-) create mode 100644 docs/roadmap/near-term.md delete mode 100644 docs/roadmap/outlook.md diff --git a/docs/roadmap/implementation.md b/docs/roadmap/implementation.md index 2c074b1..51c7420 100644 --- a/docs/roadmap/implementation.md +++ b/docs/roadmap/implementation.md @@ -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=` -- 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. diff --git a/docs/roadmap/near-term.md b/docs/roadmap/near-term.md new file mode 100644 index 0000000..1fecbe0 --- /dev/null +++ b/docs/roadmap/near-term.md @@ -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. diff --git a/docs/roadmap/outlook.md b/docs/roadmap/outlook.md deleted file mode 100644 index 7510a98..0000000 --- a/docs/roadmap/outlook.md +++ /dev/null @@ -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.