From d998791e20024759ec87d0661736927cab4d3b41 Mon Sep 17 00:00:00 2001 From: Eric Rakestraw Date: Fri, 29 May 2026 10:11:34 -0500 Subject: [PATCH] Add policy documentation, AGENTS.md, and update .gitignore --- .gitignore | 6 +- AGENTS.md | 4 + docs/policy/architecture.md | 11 + docs/policy/development.md | 629 ++++++++++++++++++++++++++ docs/roadmap/initial.md | 880 ++++++++++++++++++++++++++++++++++++ 5 files changed, 1529 insertions(+), 1 deletion(-) create mode 100644 AGENTS.md create mode 100644 docs/policy/development.md create mode 100644 docs/roadmap/initial.md diff --git a/.gitignore b/.gitignore index 3dea460..db90548 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,6 @@ +# Compiled application binary +weatherreporter + # ---> Go # If you prefer the allow list template instead of the deny list, see community template: # https://github.com/github/gitignore/blob/main/community/Golang/Go.AllowList.gitignore @@ -47,7 +50,8 @@ go.work.sum .LSOverride # Icon must end with two \r -Icon +Icon + # Thumbnails ._* diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..ec69996 --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,4 @@ +Please carefully review the documents in `docs/policy` before making any changes to this repository. + - `architecture.md` provides the canonical high-level architecture policy for this repository. + - `development.md` provides more granular development policy for this repository. + - `documentation.md` provides the canonical documentation policy for this repository. \ No newline at end of file diff --git a/docs/policy/architecture.md b/docs/policy/architecture.md index d8006d8..697e925 100644 --- a/docs/policy/architecture.md +++ b/docs/policy/architecture.md @@ -2,6 +2,17 @@ This document defines the development principles for this Go project. It is inward-facing: developers and LLM coding agents should use it to preserve the project’s shape, boundaries, and invariants as the code evolves. +## weatherreporter +`weatherreporter` is a deterministic weather briefing and report-preparation application. It consumes normalized weather data from the internal weatherfeeder-backed API, derives report-specific briefing packages, compares those packages against prior snapshots, and invokes an external prompt runner to produce human-facing reports. + +The application should keep meteorological data selection, daypart grouping, threshold detection, forecast-period resolution, and recent-change comparison inside Go domain packages. LLM prompts should receive curated briefing packages rather than raw unbounded source payloads wherever practical. + +Report types must be defined through a registry or equivalent mechanism. Each report definition should declare its report ID, prompt ID, valid-period resolver, briefing builder, comparison strategy, and output naming behavior. Avoid scattering report-type conditionals across CLI and orchestration code. + +Generated reports must be associated with explicit metadata, including report type, location, generation time, valid period, source product timestamps or hashes, briefing snapshot path, and output path. Recent Changes must be based on structured snapshot comparison rather than comparison of rendered Markdown report text. + +`scriptorium` is an external adapter, not domain logic. Subprocess execution must be isolated under `internal/adapters/scriptorium`, use context-aware execution, avoid shell interpolation, capture actionable stderr, and keep scriptorium-specific flags from leaking into domain packages. + ## Project Shape Default to a small, explicit, dependency-light Go application. Keep the design modular enough to test and change safely, but do not add abstraction unless it protects a real boundary or enables a real extension point. diff --git a/docs/policy/development.md b/docs/policy/development.md new file mode 100644 index 0000000..980e68f --- /dev/null +++ b/docs/policy/development.md @@ -0,0 +1,629 @@ +# Weatherreporter Package Layout + +This document defines the proposed package layout for `weatherreporter`, a Go application that prepares human-facing weather reports from normalized weather data collected by `weatherfeeder` and rendered through `scriptorium`. + +The application should remain a small, explicit, dependency-light Go program. Domain logic should live outside CLI, transport, and external-adapter packages. External systems should be isolated behind narrow adapters. Report-specific behavior should be selected through a registry or equivalent mechanism rather than scattered conditionals. + +## Architectural Summary + +`weatherreporter` is a deterministic weather briefing and report-preparation application. It should: + +1. Fetch normalized weather data from an internal weather API backed by `weatherfeeder`. +2. Derive report-specific briefing packages from the normalized forecast bundle. +3. Compare current briefing snapshots against prior comparable snapshots to produce optional Recent Changes. +4. Build structured prompt variables for a specific report type. +5. Invoke `scriptorium` as an external prompt runner. +6. Persist the rendered Markdown report, briefing snapshot, and generation metadata. + +The preferred data flow is: + +```text +weatherfeeder-backed internal API + -> weather API adapter + -> forecast bundle + -> report-specific briefing builder + -> recent-change comparison + -> prompt variable package + -> scriptorium subprocess adapter + -> Markdown report + metadata + stored snapshot +``` + +The application should not treat the LLM as the source of weather facts. The Go code should select the relevant data, compute daypart and period summaries, attach alerts and NWS context, identify meaningful changes, and send the LLM a curated briefing package. The LLM should synthesize and phrase the report for humans. + +## Proposed Directory Layout + +```text +cmd/weatherreporter/ + main.go + +internal/app/ + generate.go + scheduled.go + storm.go + +internal/cli/ + root.go + generate.go + run.go + inspect.go + +internal/config/ + config.go + defaults.go + load.go + validate.go + +internal/adapters/weatherapi/ + client.go + types.go + +internal/adapters/scriptorium/ + runner.go + types.go + +internal/forecast/ + bundle.go + dayparts.go + derive.go + select.go + thresholds.go + +internal/report/ + definition.go + registry.go + period.go + daily.go + tomorrow.go + three_day.go + weekend.go + storm.go + +internal/briefing/ + package.go + daily.go + tomorrow.go + three_day.go + weekend.go + storm.go + +internal/changes/ + compare.go + thresholds.go + summary.go + +internal/state/ + store.go + filesystem.go + metadata.go + +internal/promptvars/ + build.go + schema.go + +internal/timeutil/ + clock.go + periods.go +``` + +This layout can be simplified during early prototyping if a package has only one file, but the package boundaries should remain conceptually stable. + +## Dependency Direction + +The intended dependency direction is: + +```text +cmd/weatherreporter + -> internal/cli + -> internal/app + -> internal/config + -> internal/report + -> internal/briefing + -> internal/forecast + -> internal/changes + -> internal/state + -> internal/adapters/* +``` + +Rules: + +- `cmd/weatherreporter` should only bootstrap the CLI. +- `internal/cli` should parse commands and flags, then call `internal/app`. +- `internal/app` should orchestrate workflows but avoid embedding detailed forecast logic. +- `internal/adapters/*` should not contain domain policy. +- `internal/forecast`, `internal/report`, `internal/briefing`, and `internal/changes` should be testable without real external services. +- `internal/state` should expose a storage interface so filesystem state can later be replaced or supplemented. +- `scriptorium` details should not leak outside `internal/adapters/scriptorium`. + +## Package Responsibilities + +### `cmd/weatherreporter` + +Entry point for the compiled binary. + +Responsibilities: + +- Construct the root command from `internal/cli`. +- Execute the command. +- Handle final process exit behavior. + +Non-responsibilities: + +- No configuration loading details. +- No forecast logic. +- No direct calls to weather APIs, state stores, or `scriptorium`. + +### `internal/cli` + +Defines the user-facing command tree, flags, arguments, and command wiring. + +Responsibilities: + +- Define commands such as: + - `weatherreporter generate daily` + - `weatherreporter generate tomorrow` + - `weatherreporter generate three-day` + - `weatherreporter generate weekend` + - `weatherreporter generate storm` + - `weatherreporter run morning` + - `weatherreporter run evening` + - `weatherreporter inspect snapshot` +- Parse CLI flags and convert them into app-layer request structs. +- Load configuration through `internal/config`. +- Present concise user-facing errors. + +Non-responsibilities: + +- No report-building logic. +- No direct subprocess execution. +- No direct weather API calls. +- No state comparison logic. + +Suggested command shape: + +```text +weatherreporter generate daily --location home --date today --out ./daily.md +weatherreporter generate tomorrow --location home --out ./tomorrow.md +weatherreporter generate three-day --location home --out ./three_day.md +weatherreporter generate weekend --location home --out ./weekend.md +weatherreporter generate storm --location home --out ./storm.md +weatherreporter run morning --location home +weatherreporter run evening --location home +``` + +### `internal/config` + +Owns configuration structures, defaults, loading, precedence, and validation. + +Responsibilities: + +- Define application configuration structs. +- Provide built-in defaults in `defaults.go`. +- Load YAML configuration from `/usr/local/etc/weatherreporter/config.yml` or a CLI-supplied path. +- Apply precedence rules. +- Validate required settings. +- Normalize paths, durations, report settings, locations, and daypart definitions. + +Suggested configuration areas: + +- Weather API base URL, timeout, and location endpoints. +- Locations and time zones. +- `scriptorium` binary, profile, timeout, and optional extra arguments. +- Workspace and output directories. +- Report enablement and output naming. +- Daypart definitions. +- Recent-change thresholds. + +Non-responsibilities: + +- No command execution. +- No HTTP calls. +- No report-building logic. + +### `internal/app` + +Application orchestration and top-level use cases. + +Responsibilities: + +- Implement use cases such as: + - Generate one report. + - Run the morning batch. + - Run the evening batch. + - Generate a manual storm report. +- Coordinate config, weather API adapter, report registry, briefing builders, state store, change comparison, prompt variable builder, and `scriptorium` runner. +- Enforce workflow order. +- Ensure each generation run persists enough artifacts for inspection and future comparison. + +The core generation workflow should be approximately: + +```text +resolve report definition +resolve location and valid period +fetch current weather bundle +build current briefing package +load prior comparable briefing snapshot +compute recent changes +build prompt variables +write vars file +invoke scriptorium +persist report metadata and briefing snapshot +``` + +Non-responsibilities: + +- No detailed daypart calculations. +- No direct parsing of NWS text unless delegated to domain packages. +- No direct shell command construction outside the `scriptorium` adapter. + +### `internal/adapters/weatherapi` + +HTTP adapter for the internal weather API backed by `weatherfeeder`. + +Responsibilities: + +- Fetch normalized weather data for a configured location. +- Decode API responses into adapter-owned DTOs or directly into stable internal types if those types are intentionally owned by `weatherreporter`. +- Apply request timeouts and context cancellation. +- Return actionable errors containing endpoint and operation context. + +Expected data categories: + +- Hourly forecast data. +- Daily forecast data. +- NWS narrative forecast periods. +- NWS alerts. +- NWS forecast discussion. +- NWS weather story. + +Non-responsibilities: + +- No daypart grouping. +- No Recent Changes comparison. +- No prompt variable construction. +- No `scriptorium` calls. + +### `internal/adapters/scriptorium` + +Subprocess adapter for invoking `scriptorium`. + +Responsibilities: + +- Provide a narrow runner interface, such as: + +```go +type Runner interface { + Run(ctx context.Context, req RunRequest) (*RunResult, error) +} +``` + +- Execute `scriptorium run` with `exec.CommandContext`. +- Pass arguments as an argv slice, not through a shell. +- Prefer a vars file path over large inline JSON. +- Capture stdout/stderr with reasonable size limits. +- Treat nonzero exits as actionable errors. +- Keep all `scriptorium`-specific flag details inside the adapter. + +Suggested command form: + +```text +scriptorium run \ + --prompt weather.daily_report \ + --vars-file ./workspace/daily.vars.json \ + --out ./workspace/daily_report.md +``` + +Non-responsibilities: + +- No weather logic. +- No report registry logic. +- No decision about which prompt to run. + +Future note: + +- A native LLM client can later replace or supplement this adapter behind a similar interface. + +### `internal/forecast` + +Core forecast-domain processing. + +Responsibilities: + +- Define the normalized `Bundle` consumed by report builders. +- Group hourly forecast data into configured dayparts. +- Compute derived facts, including: + - Temperature ranges. + - Apparent-temperature ranges, if available. + - Max precipitation probability. + - Peak wind and wind gusts. + - Precipitation windows. + - Thunder mentions. + - Snow/ice/freezing risk indicators. + - Alert overlap with relevant periods. +- Select forecast elements relevant to a report period. +- Provide threshold helpers for impact detection. + +Non-responsibilities: + +- No CLI behavior. +- No external API calls. +- No rendered prose. +- No direct `scriptorium` calls. + +### `internal/report` + +Report definitions, registry, period resolution, and report-level contracts. + +Responsibilities: + +- Define report IDs and report definition contracts. +- Register report types and variants. +- Resolve valid periods for each report. +- Associate report types with prompt IDs. +- Define comparison strategies and output naming behavior. + +Suggested report definitions: + +```text +daily_today -> prompt weather.daily_report +daily_tomorrow -> prompt weather.daily_report or weather.tomorrow_report +three_day -> prompt weather.three_day_outlook +weekend -> prompt weather.weekend_outlook +storm -> prompt weather.storm_report +``` + +A report definition should describe: + +- Report ID. +- Human-readable name. +- Prompt ID. +- Valid-period resolver. +- Briefing builder ID or function. +- Recent-change comparison strategy. +- Default output naming pattern. +- Whether the report participates in morning or evening scheduled batches. + +Non-responsibilities: + +- No detailed forecast computation. +- No state storage. +- No subprocess execution. + +### `internal/briefing` + +Builds report-specific briefing packages from forecast bundles and report definitions. + +Responsibilities: + +- Convert a forecast bundle into a report-specific structured briefing package. +- Keep each report’s briefing shape explicit and testable. +- Attach relevant NWS narrative periods, alerts, forecast discussion context, and weather story context. +- Provide inputs suitable for LLM prompt variables. + +Report-specific builders should exist for: + +- Daily Report. +- Tomorrow Planning Brief. +- 3-Day Outlook. +- Weekend Outlook. +- Storm Report. + +Non-responsibilities: + +- No external API fetching. +- No final prose rendering. +- No state persistence, except through app orchestration. + +Design note: + +- This package is the architectural center of the application. A clean briefing package makes `scriptorium` a renderer rather than a source of weather reasoning. + +### `internal/changes` + +Structured comparison of current and prior briefing snapshots. + +Responsibilities: + +- Compare current briefing packages against prior comparable snapshots. +- Apply meaningful-change thresholds. +- Produce compact structured change summaries for prompt variables. +- Avoid comparison of rendered Markdown report text. + +Meaningful changes may include: + +- Temperature changes crossing configured thresholds. +- Precipitation probability changes by category. +- Precipitation timing shifts. +- New, canceled, extended, upgraded, or expanded alerts. +- Wind gust threshold crossings. +- Snow/ice/freezing risk changes. +- Severe-weather wording or risk changes. +- Confidence or uncertainty changes, if represented in structured briefing data. + +Non-responsibilities: + +- No fetching prior state directly unless mediated through app/state contracts. +- No final report prose. +- No external calls. + +### `internal/state` + +Durable state store for reports, snapshots, metadata, and comparison lookup. + +Responsibilities: + +- Persist generated report metadata. +- Persist briefing snapshots. +- Persist prompt variable files when useful for inspection. +- Locate prior comparable snapshots for Recent Changes. +- Use atomic writes where practical. +- Keep filesystem layout narrow and predictable. + +Initial backend: + +- Filesystem state. + +Potential future backend: + +- SQLite or another state database, behind the same store interface. + +Suggested state layout: + +```text +workspace/ + locations/ + home/ + snapshots/ + daily/ + 2026-05-30/ + 2026-05-29T050000-0500.briefing.json + 2026-05-29T050000-0500.metadata.json + three-day/ + weekend/ + storm/ + reports/ + daily/ + 2026-05-30.md + three-day/ + weekend/ + storm/ + vars/ + daily/ + 2026-05-29T050000-0500.vars.json +``` + +Non-responsibilities: + +- No weather derivation. +- No report prose generation. +- No CLI formatting decisions. + +### `internal/promptvars` + +Builds the final variable payload passed to `scriptorium`. + +Responsibilities: + +- Combine report metadata, briefing package, Recent Changes, and selected source context into a prompt variable document. +- Validate required prompt variables before invoking `scriptorium`. +- Keep prompt variable schemas explicit enough to test. +- Write vars files to the workspace when requested by the app layer. + +Non-responsibilities: + +- No weather API calls. +- No forecast derivation. +- No subprocess execution. + +### `internal/timeutil` + +Time, clock, and period helpers. + +Responsibilities: + +- Provide an injectable clock for deterministic tests. +- Resolve local dates using the configured location time zone. +- Handle daypart spans, including overnight windows. +- Normalize valid periods. +- Provide helpers for recurring scheduled batches. + +Non-responsibilities: + +- No report-specific forecast logic unless delegated by `internal/report`. +- No external calls. + +## Report Types and Valid-Period Identity + +Each generated report must be associated with explicit metadata: + +- Report type. +- Report variant, if applicable. +- Location ID. +- Generation time. +- Valid period start. +- Valid period end. +- Source product timestamps and/or hashes. +- Briefing snapshot path. +- Prompt variable path. +- Rendered report path. + +The valid period should identify what weather period the report covers, independent of when the report was generated. + +Examples: + +- A 5 PM Tomorrow Planning Brief for Saturday and a 5 AM Saturday Daily Report both cover the same valid date. +- A Saturday Weekend Outlook covers the remaining weekend, while a Friday Weekend Outlook may cover Friday evening through Sunday night. +- A Storm Report covers a forecast event window, not a fixed calendar day. + +This identity is required for reliable Recent Changes behavior. + +## Scheduled Batch Semantics + +The app should support scheduled batches but should not need to be a daemon in the initial version. + +Suggested batches: + +```text +morning: + - daily_today + - three_day + - weekend, except Sunday + +evening: + - daily_tomorrow +``` + +External scheduling should be handled by systemd timers, cron, or another orchestrator. `weatherreporter` should simply provide deterministic commands that can be scheduled. + +## Storm Report Direction + +The initial version should support manual Storm Report generation: + +```text +weatherreporter generate storm --location home +``` + +Future storm monitoring should use a staged design: + +```text +incoming weather data + -> deterministic candidate detector + -> LLM event evaluator + -> storm lifecycle state + -> storm report generation or skip decision +``` + +Potential storm lifecycle states: + +```text +none -> monitoring -> active_report -> escalated -> deescalating -> resolved +``` + +This future behavior should not be built before the core scheduled reports are stable, but the package layout should leave room for it. + +## Testing Expectations + +Core tests should not require real external services. + +Priority test areas: + +- Configuration loading and validation. +- Daypart grouping, especially overnight periods. +- Valid-period resolution for each report type. +- Briefing package construction from fixtures. +- Recent Changes threshold behavior. +- Prior snapshot lookup. +- `scriptorium` adapter behavior using a fake executable or command runner. +- CLI command parsing for major workflows. + +## Design Invariants + +Preserve these invariants as the project evolves: + +- Weather facts come from normalized source data, not from the LLM. +- The LLM receives curated briefing packages, not unbounded raw weather payloads. +- Recent Changes are based on structured snapshot comparison, not Markdown diffing. +- Report types are registered or otherwise centrally defined. +- External integrations are thin adapters. +- CLI code wires workflows but does not own domain logic. +- The first durable state backend is filesystem-based and inspectable. +- `scriptorium` is an adapter boundary, not an application dependency that leaks across packages. diff --git a/docs/roadmap/initial.md b/docs/roadmap/initial.md new file mode 100644 index 0000000..1b84a43 --- /dev/null +++ b/docs/roadmap/initial.md @@ -0,0 +1,880 @@ +# Weatherreporter Implementation Roadmap + +This roadmap defines a staged implementation plan for `weatherreporter`, a Go application that prepares human-facing weather reports from normalized weather data collected by `weatherfeeder` and rendered through `scriptorium`. + +The goal is to build the application in stable layers. Each stage should leave the repository in a working, testable state. Early stages should prioritize inspectable intermediate artifacts over complete automation. + +## Guiding Implementation Principles + +- Build deterministic data preparation before LLM rendering. +- Keep CLI, adapters, domain logic, state, and prompt-variable construction separate. +- Use fixture-driven tests for forecast processing and briefing builders. +- Persist intermediate artifacts so failed or low-quality reports can be inspected. +- Add one report type fully before generalizing to all report types. +- Treat `scriptorium` as an external adapter during the prototype. +- Do not build the storm-monitoring agent until scheduled report generation is reliable. + +## Stage 0: Repository Skeleton and Architecture Baseline + +### Goal + +Create the project skeleton, commit the architecture documents, and establish the package boundaries before adding substantial logic. + +### Packages Introduced + +- `cmd/weatherreporter` +- `internal/cli` +- `internal/app` +- `internal/config` + +### Work Items + +1. Initialize the Go module. +2. Add the architecture policy document. +3. Add the package layout document. +4. Add this implementation roadmap. +5. Create minimal package directories and placeholder files where useful. +6. Add basic build/test tooling. +7. Add a minimal `weatherreporter --help` command. + +### Deliverables + +- Go module builds successfully. +- `go test ./...` passes. +- Basic CLI entrypoint exists. +- Documentation is present under the appropriate docs directory. + +### Done Criteria + +- The repository has a clear skeleton matching the intended architecture. +- The binary can be built. +- The CLI can display help without loading external services. + +## Stage 1: Configuration and CLI Foundation + +### Goal + +Implement configuration loading and a stable command shape before integrating external systems. + +### Packages Introduced or Expanded + +- `internal/config` +- `internal/cli` +- `internal/app` +- `internal/timeutil` + +### Work Items + +1. Define configuration structs for: + - Weather API settings. + - Locations. + - Location time zones. + - `scriptorium` settings. + - Workspace paths. + - Report output paths. + - Daypart definitions. + - Recent-change thresholds. +2. Implement built-in defaults in `internal/config/defaults.go`. +3. Implement YAML config loading from: + - `/usr/local/etc/weatherreporter/config.yml` + - CLI override via `--config` +4. Implement config validation. +5. Implement basic command structure: + - `generate daily` + - `generate tomorrow` + - `generate three-day` + - `generate weekend` + - `generate storm` + - `run morning` + - `run evening` +6. Commands may initially return “not implemented” after config and request resolution. +7. Add time-zone and clock helpers. + +### Deliverables + +- Config can be loaded, validated, and inspected in tests. +- CLI commands parse expected flags. +- App-layer request structs exist for report generation and scheduled batches. + +### Tests + +- Config defaults load successfully. +- Example config file load test. +- Invalid config produces actionable errors. +- CLI parser tests for major commands. +- Time-zone resolution tests. + +### Done Criteria + +- CLI request handling is stable enough that later stages can attach real behavior without reshaping commands. +- Configuration can represent at least one location and the default daypart set. + +## Stage 2: Weather API Adapter and Forecast Bundle + +### Goal + +Fetch normalized weather data from the internal weather API and represent it as a stable forecast bundle inside the application. + +### Packages Introduced or Expanded + +- `internal/adapters/weatherapi` +- `internal/forecast` +- `internal/app` + +### Work Items + +1. Define the internal `forecast.Bundle` type. +2. Define source substructures for: + - Hourly forecast data. + - Daily forecast data. + - NWS narrative forecast periods. + - NWS alerts. + - NWS forecast discussion. + - NWS weather story. +3. Implement the weather API client. +4. Add context-aware HTTP calls and timeouts. +5. Add actionable errors for failed API calls and decode failures. +6. Add fixture support for tests. +7. Optionally add a debug command or app method to fetch and save the raw normalized bundle. + +### Deliverables + +- Weather API adapter can fetch a bundle for a configured location. +- Forecast bundle type is available to downstream packages. +- Tests can use fixtures without real API calls. + +### Tests + +- Decode representative API fixture into `forecast.Bundle`. +- HTTP error handling. +- Timeout/cancellation behavior. +- Missing or malformed source sections. + +### Done Criteria + +- The app can fetch current weather data and save or log a concise confirmation. +- No report generation is required yet. + +## Stage 3: Forecast Derivation and Daypart Processing + +### Goal + +Implement deterministic forecast processing needed by the Daily Report. + +### Packages Introduced or Expanded + +- `internal/forecast` +- `internal/timeutil` + +### Work Items + +1. Implement configurable daypart definitions. +2. Implement daypart overlap logic, including overnight periods. +3. Group hourly forecast records into dayparts. +4. Compute daypart summaries: + - Temperature range. + - Apparent-temperature range, if available. + - Max precipitation probability. + - Peak wind speed. + - Peak wind gust. + - Dominant or notable conditions. + - Thunder, snow, ice, fog, heat, cold, or wind indicators where supported by source data. +5. Implement alert overlap with report periods and dayparts. +6. Implement basic threshold helpers. +7. Implement source selection helpers for NWS narrative periods and broader context. + +### Deliverables + +- A deterministic Daily Report forecast summary can be built from a fixture bundle. +- Daypart outputs are inspectable as JSON. + +### Tests + +- Morning/midday/afternoon/evening grouping. +- Overnight grouping across midnight. +- Missing hourly data behavior. +- Boundary timestamps at daypart edges. +- Max/min and threshold calculations. +- Alert-period overlap behavior. + +### Done Criteria + +- Forecast derivation is reliable enough to support a first report briefing package. +- No LLM or `scriptorium` integration is required yet. + +## Stage 4: Report Registry and Valid-Period Resolution + +### Goal + +Centralize report definitions and valid-period behavior before building report-specific briefings. + +### Packages Introduced or Expanded + +- `internal/report` +- `internal/timeutil` +- `internal/app` + +### Work Items + +1. Define report IDs and variants: + - `daily_today` + - `daily_tomorrow` + - `three_day` + - `weekend` + - `storm` +2. Define report metadata structures. +3. Define a report `Definition` contract. +4. Implement a registry. +5. Implement valid-period resolvers: + - Today Daily Report. + - Tomorrow Planning Brief. + - 3-Day Outlook. + - Weekend Outlook. + - Storm Report placeholder. +6. Define default prompt IDs: + - `weather.daily_report` + - `weather.tomorrow_report`, or reuse `weather.daily_report` if preferred. + - `weather.three_day_outlook` + - `weather.weekend_outlook` + - `weather.storm_report` +7. Implement batch membership rules: + - Morning batch: Daily Today, 3-Day Outlook, Weekend Outlook except Sunday. + - Evening batch: Daily Tomorrow. + +### Deliverables + +- App layer can resolve which reports should run for a command. +- Each report has a valid period independent of generation time. +- Report definitions map to prompt IDs. + +### Tests + +- Daily valid period for different generation times. +- Tomorrow valid period from evening generation. +- 3-day period calculation. +- Weekend period calculation on Monday, Friday, Saturday, and Sunday. +- Morning batch skips Weekend Outlook on Sunday. +- Registry lookup errors are actionable. + +### Done Criteria + +- Report identity and period behavior are stable. +- Later stages can add builders without changing CLI semantics. + +## Stage 5: Daily Briefing Builder + +### Goal + +Build the first complete report-specific briefing package without invoking the LLM. + +### Packages Introduced or Expanded + +- `internal/briefing` +- `internal/report` +- `internal/forecast` +- `internal/app` + +### Work Items + +1. Define common briefing metadata: + - Report type. + - Variant. + - Location. + - Generation time. + - Valid start/end. + - Source timestamps or hashes. +2. Define the Daily Report briefing schema. +3. Build Daily Report briefing content: + - Bottom-line inputs. + - Daypart summaries. + - Active or relevant alerts. + - Best/worst outdoor window inputs, if derivable. + - NWS narrative periods relevant to the day. + - Forecast discussion summary or selected text from the API data. + - Weather story summary or selected text from the API data. +4. Add JSON output for the briefing package. +5. Add an app workflow that can generate the Daily briefing and write it to disk for inspection. + +### Deliverables + +- `weatherreporter generate daily` can produce a Daily briefing JSON artifact without calling `scriptorium`. +- Fixture-based output is stable enough for review. + +### Tests + +- Daily briefing from representative fixture. +- Alerts included/excluded correctly. +- Source context selection. +- Empty or quiet-weather behavior. +- Snapshot metadata completeness. + +### Done Criteria + +- The Daily briefing package is useful as prompt input. +- The app can produce the briefing artifact from real or fixture data. + +## Stage 6: Prompt Variable Builder + +### Goal + +Convert a briefing package into the structured variable payload expected by `scriptorium` prompts. + +### Packages Introduced or Expanded + +- `internal/promptvars` +- `internal/briefing` +- `internal/report` +- `internal/app` + +### Work Items + +1. Define prompt variable schema structures. +2. Build variables from report metadata and briefing content. +3. Include placeholders for Recent Changes, initially empty. +4. Validate required fields before rendering. +5. Write vars JSON to the workspace. +6. Ensure vars output is stable and inspectable. + +### Deliverables + +- Daily Report prompt vars can be generated and written to a file. +- The vars file is suitable for `scriptorium run --vars-file`. + +### Tests + +- Prompt vars generated from Daily briefing fixture. +- Missing required fields fail validation. +- JSON output is deterministic where practical. + +### Done Criteria + +- The app can prepare a complete vars file for a Daily Report. +- LLM rendering is the only missing step for the first end-to-end report. + +## Stage 7: Scriptorium Adapter and First End-to-End Daily Report + +### Goal + +Invoke `scriptorium` as a subprocess and produce the first rendered Markdown report. + +### Packages Introduced or Expanded + +- `internal/adapters/scriptorium` +- `internal/app` +- `internal/state`, minimally if needed for output paths + +### Work Items + +1. Define `scriptorium.Runner` interface and request/result types. +2. Implement subprocess execution with `exec.CommandContext`. +3. Pass arguments without shell interpolation. +4. Prefer `--vars-file` for prompt variables. +5. Capture stderr and stdout with reasonable limits. +6. Apply timeout and cancellation. +7. Return actionable errors for nonzero exits. +8. Wire Daily Report generation end-to-end: + - Fetch bundle. + - Build briefing. + - Build vars. + - Run `scriptorium`. + - Write Markdown report. + +### Deliverables + +- `weatherreporter generate daily --location home --out ./daily.md` produces a Markdown report. +- Failures include useful context. + +### Tests + +- Adapter command construction using a fake command runner or fake executable. +- Nonzero exit handling. +- Timeout behavior. +- App workflow test using fake weather client and fake `scriptorium` runner. + +### Done Criteria + +- The first report can be generated end-to-end. +- `scriptorium` is isolated behind the adapter package. + +## Stage 8: Filesystem State Store and Metadata Persistence + +### Goal + +Persist report artifacts and metadata in a durable, inspectable structure. + +### Packages Introduced or Expanded + +- `internal/state` +- `internal/app` + +### Work Items + +1. Define state store interface. +2. Implement filesystem-backed store. +3. Persist briefing snapshots. +4. Persist prompt variable files. +5. Persist report metadata. +6. Persist rendered Markdown reports when output path is managed by the app. +7. Use atomic writes where practical. +8. Implement lookup for prior comparable snapshots. + +### Deliverables + +- Each generated report has associated metadata and briefing snapshot. +- Prior comparable snapshot lookup works for Daily Reports. + +### Tests + +- Atomic write behavior where feasible. +- Metadata round-trip. +- Snapshot path generation. +- Prior snapshot lookup. +- Narrow-path safety behavior. + +### Done Criteria + +- Daily reports leave enough state for inspection and future Recent Changes. +- State layout is predictable and documented. + +## Stage 9: Recent Changes for Daily Reports + +### Goal + +Add structured comparison of Daily Report briefing snapshots and include meaningful changes in prompt variables. + +### Packages Introduced or Expanded + +- `internal/changes` +- `internal/state` +- `internal/promptvars` +- `internal/app` + +### Work Items + +1. Define change summary structures. +2. Define threshold configuration. +3. Implement Daily briefing comparison. +4. Compare current snapshot to prior comparable snapshot. +5. Detect meaningful changes, such as: + - Temperature shifts. + - Precipitation timing shifts. + - Precipitation probability category changes. + - Alert changes. + - Wind gust changes. + - Snow/ice/thunder risk changes. +6. Add Recent Changes to prompt vars. +7. Omit or minimize Recent Changes when no meaningful changes exist. + +### Deliverables + +- Daily Report vars include Recent Changes when appropriate. +- Daily Report generation persists current snapshot after comparison. + +### Tests + +- No prior snapshot behavior. +- No meaningful changes behavior. +- Temperature threshold crossing. +- Precipitation timing shift. +- Alert added/removed behavior. +- Comparison uses valid period, not just generation time. + +### Done Criteria + +- The Daily Report can say what changed relative to the prior report covering the same forecast period. +- Markdown report text is not used as the comparison source. + +## Stage 10: Tomorrow Planning Brief + +### Goal + +Add the evening Tomorrow Planning Brief using the Daily Report machinery where practical. + +### Packages Introduced or Expanded + +- `internal/report` +- `internal/briefing` +- `internal/changes` +- `internal/app` + +### Work Items + +1. Implement the `daily_tomorrow` report definition fully. +2. Reuse or specialize the Daily briefing builder for tomorrow’s valid date. +3. Add any tomorrow-specific planning fields, such as: + - Morning readiness note inputs. + - Commute/school/workday concerns. + - What may change overnight. +4. Ensure comparison can find a prior report covering the same valid day where appropriate. +5. Implement `run evening` as Daily Tomorrow. + +### Deliverables + +- `weatherreporter generate tomorrow --location home` works end-to-end. +- `weatherreporter run evening --location home` works. + +### Tests + +- Tomorrow valid-period calculation. +- Tomorrow briefing uses the correct date. +- Recent Changes can compare against prior 3-Day or prior Tomorrow snapshot if configured. +- Evening batch includes only the expected report. + +### Done Criteria + +- Evening look-ahead generation is reliable. +- Daily Today and Daily Tomorrow share logic without muddling their identities. + +## Stage 11: 3-Day Outlook + +### Goal + +Add the 3-Day Outlook report using the same architecture. + +### Packages Introduced or Expanded + +- `internal/report` +- `internal/briefing` +- `internal/forecast` +- `internal/changes` +- `internal/app` + +### Work Items + +1. Implement 3-Day valid-period resolution. +2. Build a 3-Day briefing package. +3. Summarize each day: + - Overall character. + - Temperature range. + - Precipitation/storm/winter/heat/wind risks. + - Best/worst windows if derivable. + - Relevant alerts. +4. Attach broader NWS context, especially forecast discussion and weather story inputs. +5. Implement 3-Day Recent Changes strategy. +6. Add end-to-end generation. + +### Deliverables + +- `weatherreporter generate three-day --location home` produces Markdown. +- Morning batch can include the 3-Day Outlook. + +### Tests + +- Three-day valid period. +- Daily aggregation across three days. +- Alert overlap across multi-day period. +- Recent Changes across multi-day snapshots. +- Quiet-weather behavior. + +### Done Criteria + +- The 3-Day Outlook is generated using the same registry, briefing, vars, state, and rendering pipeline as the Daily Report. + +## Stage 12: Weekend Outlook + +### Goal + +Add the Weekend Outlook with day-of-week-sensitive period behavior. + +### Packages Introduced or Expanded + +- `internal/report` +- `internal/briefing` +- `internal/forecast` +- `internal/changes` +- `internal/app` + +### Work Items + +1. Implement Weekend valid-period resolution: + - Monday through Thursday: upcoming Saturday/Sunday, optionally Friday evening if configured. + - Friday: Friday evening through Sunday night. + - Saturday: remaining weekend. + - Sunday: normally not generated by the scheduled morning batch. +2. Build Weekend briefing package. +3. Emphasize planning fields: + - Best outdoor windows. + - Worst weather windows. + - Rain/storm timing. + - Heat/cold/wind comfort. + - Confidence and uncertainty inputs. +4. Implement Weekend Recent Changes strategy. +5. Add morning batch inclusion except Sunday. + +### Deliverables + +- `weatherreporter generate weekend --location home` produces Markdown. +- `weatherreporter run morning --location home` includes Weekend Outlook except Sunday. + +### Tests + +- Weekend period on each day of the week. +- Saturday remaining-weekend behavior. +- Sunday skip behavior in morning batch. +- Recent Changes across narrowed valid periods. +- Outdoor-window derivation behavior. + +### Done Criteria + +- Weekend Outlook works end-to-end and follows expected scheduling behavior. + +## Stage 13: Morning and Evening Batch Hardening + +### Goal + +Make scheduled workflows reliable enough for unattended execution by cron, systemd timers, or another orchestrator. + +### Packages Introduced or Expanded + +- `internal/app` +- `internal/cli` +- `internal/state` +- `internal/config` + +### Work Items + +1. Finalize `run morning` workflow. +2. Finalize `run evening` workflow. +3. Decide failure behavior: + - Continue remaining reports after one report fails, or fail fast. + - Return aggregate status. +4. Add structured run summaries. +5. Ensure each report run records enough metadata for troubleshooting. +6. Add CLI flags for output directory, location, and optional dry-run/vars-only mode if desired. +7. Add logging suitable for scheduled execution. + +### Deliverables + +- Morning batch can generate Daily, 3-Day, and Weekend reports. +- Evening batch can generate Tomorrow Planning Brief. +- Failures are understandable from logs and metadata. + +### Tests + +- Morning batch report selection. +- Evening batch report selection. +- Partial failure behavior. +- Output path behavior. +- Dry-run or vars-only behavior, if implemented. + +### Done Criteria + +- The scheduled report system is ready for real daily use. + +## Stage 14: Manual Storm Report + +### Goal + +Add manual Storm Report generation without building the automatic monitoring agent yet. + +### Packages Introduced or Expanded + +- `internal/report` +- `internal/briefing` +- `internal/forecast` +- `internal/app` +- `internal/changes`, if needed + +### Work Items + +1. Implement Storm Report definition. +2. Define storm valid-period behavior. +3. Build storm briefing package from: + - Active alerts. + - Forecast discussion. + - Weather story. + - Relevant hourly/daily periods. + - NWS narrative periods. +4. Include storm-specific fields: + - Event headline inputs. + - Timing window. + - Hazards. + - Most likely scenario inputs. + - Reasonable worst-case inputs, if supported by source context. + - Confidence and uncertainty inputs. + - What to watch next. +5. Add manual command: + - `weatherreporter generate storm --location home` + +### Deliverables + +- Manual Storm Report generation works end-to-end. +- No automatic agent behavior is required. + +### Tests + +- Storm briefing with active alerts. +- Storm briefing with forecast discussion but no active alert. +- Quiet/no-storm behavior. +- Relevant source selection. + +### Done Criteria + +- A user can manually generate a focused Storm Report when desired. +- The implementation reuses the same pipeline rather than creating a separate flow. + +## Stage 15: Inspection and Debugging Tools + +### Goal + +Make generated artifacts easy to inspect and debug. + +### Packages Introduced or Expanded + +- `internal/cli` +- `internal/app` +- `internal/state` + +### Work Items + +1. Add inspection commands as needed: + - List recent reports. + - Show metadata for a report. + - Show prior comparable snapshot chosen for Recent Changes. + - Emit briefing JSON without rendering. + - Emit prompt vars without rendering. +2. Add clear paths to generated artifacts in command output. +3. Ensure logs do not dump large weather payloads by default. + +### Deliverables + +- Developers can inspect why a report was generated a certain way. +- Recent Changes comparison inputs are discoverable. + +### Tests + +- Inspection command behavior with fixture state. +- Missing artifact errors. +- Metadata lookup behavior. + +### Done Criteria + +- Debugging a bad report does not require stepping through the whole workflow manually. + +## Stage 16: Future Storm Monitoring Agent + +### Goal + +Add automatic storm-event evaluation only after scheduled reports and manual storm reports are stable. + +### Packages Introduced or Expanded + +Potentially: + +- `internal/storm` or expanded `internal/report`/`internal/briefing` +- `internal/adapters/scriptorium` or a separate evaluator prompt adapter +- `internal/state` +- `internal/app` + +### Work Items + +1. Implement deterministic candidate detection. +2. Define storm candidate structures. +3. Use source signals such as: + - Active alerts. + - Forecast discussion hazard wording. + - Weather Story emphasis. + - Hourly/daily threshold crossings. + - Material forecast changes toward higher impact. +4. Add an LLM event evaluator through `scriptorium` or a future native LLM adapter. +5. Track storm lifecycle state: + - `none` + - `monitoring` + - `active_report` + - `escalated` + - `deescalating` + - `resolved` +6. Generate or update Storm Reports only when warranted. +7. Avoid noisy report generation for ordinary low-impact thunder chances. + +### Deliverables + +- `weatherreporter evaluate storm --location home` can decide whether a Storm Report is warranted. +- Event lifecycle state is persisted. +- Storm Report updates are generated only for meaningful changes. + +### Tests + +- Candidate detection thresholds. +- Noisy/non-event suppression. +- Alert-triggered escalation. +- Lifecycle transitions. +- Evaluator failure behavior. + +### Done Criteria + +- Automatic storm monitoring is useful and not spammy. +- Manual Storm Report generation remains available. + +## Suggested First Implementation Milestone + +The first meaningful milestone should be: + +```text +weatherreporter generate daily --location home --out ./daily.md +``` + +This command should: + +1. Load config. +2. Fetch weather data. +3. Build a Daily briefing package. +4. Build prompt variables. +5. Invoke `scriptorium`. +6. Write Markdown output. +7. Persist metadata and snapshots. + +Do not implement all report types before this milestone. One complete vertical slice will reveal schema, state, prompt-variable, and adapter issues earlier than a broad but shallow implementation. + +## Suggested Second Implementation Milestone + +The second milestone should be: + +```text +weatherreporter run morning --location home +weatherreporter run evening --location home +``` + +At this point, the app should support: + +- Daily Today. +- Daily Tomorrow. +- 3-Day Outlook. +- Weekend Outlook. +- Recent Changes for all scheduled report types. +- Filesystem state and metadata. +- Reliable scheduled execution. + +## Suggested Third Implementation Milestone + +The third milestone should be: + +```text +weatherreporter generate storm --location home +``` + +At this point, Storm Reports can be generated manually using the same pipeline. Automatic storm monitoring should remain future work until manual Storm Reports are useful and stable. + +## Non-Goals for the Initial Prototype + +Do not implement these in the initial prototype unless required by real use: + +- Native LLM client inside `weatherreporter`. +- Daemon mode. +- Automatic storm-monitoring agent. +- Database-backed state. +- Multi-user authorization. +- Public HTTP API. +- Complex plugin system. +- Markdown diffing for Recent Changes. +- Raw unbounded weather payloads sent directly to prompts. + +## Final Implementation Notes + +The most important early design decision is to make the briefing package the central artifact. Once the briefing package is stable, every report follows the same basic path: + +```text +report definition -> valid period -> forecast selection -> briefing package -> Recent Changes -> prompt vars -> scriptorium -> report metadata +``` + +This keeps the application modular, testable, and easy to extend with future report types.