Files
weatherreporter/docs/roadmap/implementation.md

602 lines
22 KiB
Markdown

# Today Report Implementation Roadmap
## Purpose
This roadmap defines the staged implementation plan for
`docs/roadmap/today.md`. It is written for an LLM coding agent that will
implement each stage in order.
This is a planning document only. It may describe unimplemented behavior because
it lives under `docs/roadmap/`.
## Source Roadmap
`docs/roadmap/today.md` is the feature roadmap and target-state authority for
the Today report. This implementation plan provides the concrete sequence for
reaching that target.
If this implementation plan and `today.md` conflict, update the feature roadmap
first so it remains the conceptual source of truth, then update this file.
## Locked Decisions
- `today` is a new independent generated-text-template report.
- `today` does not replace the existing `daily_today` report in this roadmap.
- `weatherreporter generate today` is a new public command.
- `weatherreporter generate daily` remains the existing daily command and must
not alias to Today.
- `reports.today` is a new independent config override key.
- `reports.daily` and `reports.daily_today` remain mapped to the existing daily
report while that report exists.
- Morning batch includes `today` in the position currently occupied by
`daily_today`.
- Morning batch should not include both `today` and `daily_today`.
- Evening batch remains `tomorrow`.
- New Today runtime identity uses report ID `today`, artifact group `today`,
batch output `today.md`, template ID `today`, generated-text schema ID
`today`, and prompt ID `weather.today_generated_text`.
- Historical `daily_today` workspace artifacts are not migrated.
- Today uses its own deterministic `today_planning` module.
- Today does not reuse Tomorrow's prompt, template, schema, report ID, artifact
group, or public planning stanza.
- Scriptorium registration remains out of band. Weatherreporter invokes
Scriptorium by prompt ID and passes the data package as it does for existing
generated-text reports.
## Implementation Principles
- Keep report identity and public name resolution in `internal/report`.
- Keep CLI parsing in `internal/cli`; do not move domain policy into CLI.
- Keep generated-text validation and render-context construction in
`internal/generatedtext`.
- Keep embedded schema/template assets in `internal/reporttemplate`.
- Keep module IDs in `internal/module` and module builders in
`internal/briefing`.
- Keep the app orchestration path shared with other generated-text-template
reports.
- Do not introduce a new workflow engine, CLI framework, plugin system, or
compatibility migration layer for old workspace artifacts.
- Do not make `daily` and `today` compatibility aliases.
- Preserve existing managed artifact layout conventions.
## Stage 1: Today ID Scaffold And Planning Module
Goal: add the internal Today report ID scaffold and Today planning module
without making Today public or changing batch behavior yet.
This stage should compile and pass focused module/report tests while the
existing active `daily_today` report remains unchanged.
### Implementation
- In `internal/report/definition.go`, add `Today ID = "today"`.
- Do not remove or alter `DailyToday` in this stage.
- In `internal/module/module.go`, add:
- `TodayPlanning ID = "today_planning"`;
- `TodayPlanningOptions struct{}`.
- Add `internal/briefing/today_planning_module.go`.
- Add `TodayPlanningModule` with initial prompt-facing fields:
- `morning_readiness`;
- `commute_school_workday_concerns`;
- `outdoor_planning`;
- `late_day_change_watch`.
- Add a private deterministic helper, such as `buildTodayPlanning`, near the
existing planning summary helpers.
- Reuse private helper functions from Tomorrow planning only when the
underlying logic is identical. Do not expose or reuse `TomorrowPlanningModule`
or the `tomorrow_planning` stanza.
- Register `TodayPlanning` in `internal/briefing/modules.go` with:
- stanza name `today_planning`;
- supported reports `[]report.ID{report.Today}`;
- required derived facts matching the daily-summary/daypart facts it uses;
- `MissingDataEmpty` unless implementation discovers a stricter requirement
is needed.
- Add `report.Today` to module support lists where Today should be eligible:
- all-report modules;
- daypart modules;
- narrative forecast;
- hourly forecast;
- derived daily summary;
- derived daypart summaries.
- Keep existing `report.DailyToday` support unchanged.
### Files To Inspect
- `internal/report/definition.go`
- `internal/module/module.go`
- `internal/briefing/modules.go`
- `internal/briefing/tomorrow_planning_module.go`
- `internal/briefing/summary_helpers.go`
- `internal/briefing/derived_modules_test.go`
- `internal/briefing/modules_test.go`
### Acceptance Criteria
- `module.TodayPlanning` is registered and buildable for `report.Today`.
- `today_planning` is rejected for unsupported reports.
- The new module emits deterministic snake_case JSON fields.
- Existing Tomorrow planning behavior is unchanged.
- Existing Daily Today behavior is unchanged.
### Tests
Add or update tests for:
- Today planning output shape.
- Today planning supported report validation.
- Unsupported report validation, including Tomorrow and Daily Today.
- Empty/fallback behavior when useful planning facts are missing.
Run:
```sh
go test ./internal/briefing ./internal/module ./internal/report
go test ./...
git diff --check
```
Stage size: suitable for one implementation prompt.
## Stage 2: Today GeneratedText, Schema, Prompt, Template, And Render Context
Goal: add Today generated-text assets and render-context support while keeping
the report inactive until Stage 3.
### Implementation
- Add `internal/generatedtext/today.go`.
- Add `generatedtext.Today` with fields:
- `Summary string`;
- `ForecastDiscussion []string`;
- `PrecipitationTiming string`;
- `Confidence string`.
- Add `ValidateToday` with Tomorrow-equivalent semantics:
- strict JSON;
- reject unknown fields;
- reject trailing JSON values;
- trim string fields;
- trim and drop blank discussion paragraphs;
- require nonblank `summary`;
- require at least one nonblank `forecast_discussion` paragraph;
- return canonical normalized JSON using the same public field names.
- Add `internal/reporttemplate/schemas/today.generated_text.schema.json`.
- Add `internal/reporttemplate/templates/today.md.tmpl`.
- Add `internal/reporttemplate/prompts/today.generated_text.md`.
- Add `TodayRenderContext`, `TodayReportContext`, `TodayTemplateModules`, and
`BuildTodayRenderContext` in `internal/generatedtext`.
- Today report context should include:
- `Title` exactly `Today's Weather`;
- `ForecastDate`;
- `ForecastDateLabel`;
- `ForecastDayName`;
- `GeneratedAt`;
- `GeneratedAtLabel`;
- `ValidPeriod`;
- `Timezone`.
- Today template modules should include at least:
- `Metadata`;
- `CurrentConditions`;
- `HourlyForecast`;
- `DerivedDailySummary`;
- `DerivedDaypartSummaries`;
- ordered daypart rows;
- `PrecipTiming`;
- `AlertDigest`;
- `SPCConvectiveOutlooks`;
- `AreaForecastDiscussion`;
- `SPCConvectiveDiscussion`;
- `WeatherStory`;
- `TodayPlanning`.
- Share private render-context helper mechanics with Tomorrow where appropriate,
such as snapshot lookup and daypart row ordering.
- Do not expose Tomorrow-specific types through the Today context.
- Register Today in the generated-text catalog with schema ID `today` and
template ID `today`.
- Add Today schema/template entries to `internal/reporttemplate` if explicit
asset maps are still used.
- The Today prompt asset should instruct the LLM to produce only the structured
JSON fields expected by the Today schema, using the data package as the only
weather source.
### Files To Inspect
- `internal/generatedtext/tomorrow.go`
- `internal/generatedtext/render_context.go`
- `internal/generatedtext/catalog.go`
- `internal/generatedtext/*_test.go`
- `internal/reporttemplate/reporttemplate.go`
- `internal/reporttemplate/templates/tomorrow.md.tmpl`
- `internal/reporttemplate/schemas/tomorrow.generated_text.schema.json`
- `internal/reporttemplate/prompts/tomorrow.generated_text.md`
### Acceptance Criteria
- Today generated-text validation mirrors Tomorrow behavior.
- Today schema accepts exactly the intended public fields.
- Today template renders from structured module data and generated text.
- Today render context uses Today-specific public types.
- Today generated-text catalog lookup works once a report definition references
schema/template ID `today`.
- Existing Hourly and Tomorrow generated-text behavior is unchanged.
### Tests
Add or update tests for:
- `ValidateToday` success, missing required fields, unknown fields, trailing
JSON, malformed JSON, blank strings, paragraph trimming, and canonical JSON.
- Today JSON schema lookup.
- Today template lookup.
- Today render context labels, daypart ordering, populated modules, and nil
optional modules.
- Generated-text catalog completeness support for Today once the report
definition is active. If the report is not active until Stage 3, add this
assertion in Stage 3.
Run:
```sh
go test ./internal/generatedtext ./internal/reporttemplate ./internal/briefing
go test ./...
git diff --check
```
Stage size: suitable for one implementation prompt.
## Stage 3: Today Report Definition And Batch Integration
Goal: add Today as an active report and switch morning batch to Today without
aliasing or replacing the existing `daily` command/report.
### Implementation
- Add a Today definition, preferably in a new `internal/report/today_report.go`
file.
- The Today definition must use:
- `ID: report.Today`;
- `Name: "Today Report"` or equivalent user-facing report name;
- `PromptID: "weather.today_generated_text"`;
- `GenerationMode: report.GenerationModeGeneratedTextTemplate`;
- `TemplateID: "today"`;
- `GeneratedTextSchemaID: "today"`;
- `ComparisonStrategy: report.CompareSameValidDate`;
- `ArtifactGroup: "today"`;
- `BatchOutputName: "today.md"`;
- `Generated: true`;
- `CompatiblePriorIDs: []report.ID{report.Today}`;
- `Morning: true`;
- current-local-civil-day resolver.
- Keep the existing Daily Today definition and report ID unchanged.
- Today default modules must include, in this order unless implementation tests
justify a more useful order:
- `metadata`;
- `current_conditions`;
- `narrative_forecast`;
- `derived_daily_summary`;
- `derived_daypart_summaries`;
- `precip_timing`;
- `alert_digest`;
- `spc_convective_outlooks`;
- `area_forecast_discussion`;
- `spc_convective_discussion`;
- `weather_story`;
- `outdoor_windows`;
- `hourly_forecast`;
- `today_planning`.
- Update `internal/report/registry.go`:
- default registry includes both Daily Today and Today;
- `All()` includes both reports in a stable order, with Today near the other
current-day report.
- Update `internal/report/period.go`:
- morning batch resolves Today first;
- morning batch does not include Daily Today;
- morning Sunday skip behavior for Weekend is unchanged;
- evening batch remains Tomorrow.
- Update report name helpers in `internal/report/names.go`:
- add `CommandNameToday = "today"`;
- `IDForCommandName("today") == report.Today`;
- `IDForCommandName("daily")` remains the existing Daily Today report;
- `CommandNames()` includes both `today` and `daily` as distinct commands.
- Update config-key resolution:
- `IDForConfigKey("today") == report.Today`;
- `IDForConfigKey("daily")` remains the existing Daily Today report;
- `IDForConfigKey("daily_today")` remains the existing Daily Today report.
- Update structured Recent Changes dispatch so Today uses the daily comparison
logic currently used by Daily Today and Tomorrow.
- Update briefing/package variant logic so Today produces variant `today`.
### Files To Inspect
- `internal/report/definition.go`
- `internal/report/daily_report.go`
- `internal/report/registry.go`
- `internal/report/period.go`
- `internal/report/names.go`
- `internal/report/period_test.go`
- `internal/briefing/modules.go`
- `internal/briefing/package.go`
- `internal/app/app.go`
- `internal/config/reports.go`
- `internal/config/config_test.go`
- `internal/changes`
### Acceptance Criteria
- `report.DefaultRegistry()` has active definitions for both `today` and the
existing daily report.
- Morning batch report IDs are `today,three_day,weekend` when Weekend is
included.
- Sunday morning batch report IDs are `today,three_day`.
- `daily` and `today` command names resolve to different report IDs.
- `reports.today` and `reports.daily` / `reports.daily_today` resolve to
different report IDs.
- Today report metadata, RunID, artifact group, batch output name, data-package
path, report path, and distributor template values use `today`.
- Existing manual Daily, Tomorrow, Hourly, Three-Day, Weekend, and Storm
behavior is unchanged.
### Tests
Add or update tests for:
- report registry membership and `All()` order;
- `IDForCommandName` for `today` and `daily`;
- `CommandNames()` stable order;
- `IDForConfigKey` for `today`, `daily`, and `daily_today`;
- proof that Today and Daily resolve to different report IDs;
- Today valid period with default current local date;
- Today valid period with explicit `--date` / request date;
- morning and Sunday morning batch order;
- Recent Changes dispatch for Today;
- module registry support for Today modules.
Run:
```sh
go test ./internal/report ./internal/config ./internal/briefing ./internal/changes
go test ./internal/app ./internal/cli
go test ./...
go run ./cmd/weatherreporter --help
git diff --check
```
Stage size: this is a broad but coherent report/batch integration stage. It is
suitable for one implementation prompt if the agent keeps the scope to report
identity and tests.
## Stage 4: App, CLI, Config, And Workflow Integration
Goal: make `generate today`, batches, artifacts, and distributor notification
work end-to-end through the existing app flow while keeping `generate daily`
separate.
### Implementation
- Add `app.ReportToday` if app keeps report-kind constants.
- Keep `app.ReportDaily` mapped to the existing daily command/report.
- Update CLI help text to show `generate today` as a distinct command.
- Keep `generate daily` help text separate.
- Parse `--date YYYY-MM-DD` for Today.
- If omitted, use current local date in the configured timezone.
- Keep existing `daily --date` behavior unchanged.
- Ensure app generation resolves:
- `today` to `report.Today`;
- `daily` to the existing daily report.
- Ensure Today generated-text-template flow persists:
- raw generated text;
- structured Scriptorium run result;
- normalized generated text;
- render context;
- rendered Markdown report;
- metadata;
- optional output copy.
- Ensure Today prompt invocation uses `weather.today_generated_text`.
- Ensure Scriptorium output path for Today generated text is a JSON artifact,
matching existing generated-text-template reports.
- Ensure state and metadata inspection work for Today run IDs.
- Ensure distributor notification uses the managed Today Markdown report path,
not optional output copies.
- Ensure distributor template variables resolve with:
- `report_id=today`;
- `artifact_group=today`;
- `batch_output_name=today.md`;
- valid-period variables based on the Today valid period.
- Update fake Scriptorium scripts in app/CLI tests to return valid Today JSON
when prompt ID is `weather.today_generated_text`.
### Files To Inspect
- `internal/app/app.go`
- `internal/app/app_test.go`
- `internal/cli/root.go`
- `internal/cli/root_test.go`
- `internal/state`
- `internal/adapters/scriptorium`
- `internal/adapters/distributor`
- `internal/config`
- `examples/config.yml`
### Acceptance Criteria
- `weatherreporter generate today` succeeds in tests through the generated-text
template path.
- `weatherreporter generate daily` still succeeds through the existing daily
path and does not produce Today identity artifacts.
- `weatherreporter generate today --date YYYY-MM-DD` resolves that local civil
day.
- Morning batch uses Today artifacts and `today.md` output copy names.
- Notification debug artifacts record Today report ID, pipeline ID, bundle ID,
and bundle paths.
- Optional `--out` and batch `--out-dir` behavior remains unchanged except for
the new Today output name in morning batch.
- No secret values appear in CLI output, metadata, or notification artifacts.
### Tests
Add or update tests for:
- CLI parser support for `generate today`.
- CLI parser proof that `generate daily` remains separate.
- CLI `--date` support for Today.
- App workflow artifacts for Today.
- App output copy behavior for Today.
- App workflow proof that Daily still produces existing daily identity.
- Batch output directory using `today.md`.
- Distributor request values for Today.
- Inspect commands loading Today run metadata/modules/data package/sources.
Run:
```sh
go test ./internal/app ./internal/cli ./internal/state
go test ./internal/adapters/scriptorium ./internal/adapters/distributor
go test ./...
go run ./cmd/weatherreporter --help
git diff --check
```
Stage size: suitable for one implementation prompt.
## Stage 5: Documentation And Examples
Goal: update implemented docs and maintained examples after the code exists.
### Implementation
Update non-roadmap docs to describe implemented behavior only:
- `docs/cli.md`:
- add `generate today`;
- keep `generate daily` documented as a separate command;
- document `--date` for Today generation;
- keep Daily docs accurate for the existing command.
- `docs/config.md`:
- document `reports.today`;
- keep `reports.daily` / `reports.daily_today` documentation separate while
the existing daily report remains implemented.
- `docs/operations.md`:
- update morning batch behavior;
- document Today artifact paths;
- update distributor identity examples if present.
- `docs/templates.md`:
- document Today template variables.
- `docs/internal/report-registry.md`:
- document Today report identity and its independence from Daily Today.
- `docs/internal/generatedtext.md`:
- document Today generated-text schema and validation.
- `docs/internal/reporttemplate.md`:
- document Today template/schema/prompt assets.
- `docs/internal/module.md`:
- document `today_planning`.
- `docs/internal/app-orchestration.md`:
- document Today only where the generic generated-text-template workflow does
not already cover it.
- `examples/config.yml`:
- add Today module override examples only if useful;
- keep existing daily examples accurate if they remain;
- keep no raw secrets.
Roadmap docs:
- Keep future Daily rewrite work only under `docs/roadmap/`.
- Do not document the future Daily rewrite outside roadmap docs.
### Acceptance Criteria
- Non-roadmap docs describe Today as implemented and distinct from Daily.
- Non-roadmap docs do not imply `today` and `daily` are aliases.
- Maintained examples load successfully.
- CLI examples match actual `--help` output and parser behavior.
- Documentation follows `docs/policy/documentation.md`.
### Tests And Checks
Run:
```sh
go test ./internal/config ./internal/cli ./internal/app
go test ./...
go run ./cmd/weatherreporter --help
git diff --check
```
Manual checks:
```sh
rg -n "today.*alias|daily.*alias|daily_today.*retired|replace.*daily_today" README.md docs examples internal
```
Expected remaining matches should be limited to roadmap discussion that clearly
states there is no aliasing in this feature.
Stage size: suitable for one implementation prompt.
## Stage 6: Final Validation And Stale-Symbol Sweep
Goal: confirm Today is fully implemented as a separate report and no accidental
Daily/Today aliasing remains.
### Required Validation
Run:
```sh
go test ./...
go run ./cmd/weatherreporter --help
git diff --check
```
Run focused package checks:
```sh
go test ./internal/report ./internal/generatedtext ./internal/reporttemplate
go test ./internal/briefing ./internal/module
go test ./internal/app ./internal/cli ./internal/config
go test ./internal/state ./internal/promptinput ./internal/changes
go test ./internal/adapters/distributor ./internal/adapters/scriptorium ./internal/adapters/weatherapi
```
Run stale/alias checks:
```sh
rg -n "IDForCommandName\\(\"daily\"\\).*Today|IDForConfigKey\\(\"daily\"\\).*Today" internal
rg -n "weather.today_generated_text|today.md|report.Today" internal docs examples
```
Manual review:
- `generate today` is listed in help and docs.
- `generate daily` remains listed separately.
- `generate daily` does not resolve to report ID `today`.
- `reports.daily` and `reports.today` are distinct config override keys.
- Morning batch output includes `today.md`.
- Today data package and metadata use report ID `today`.
- Distributor request values use `today`.
- Existing manual Daily behavior remains available.
- Existing Tomorrow behavior is unchanged.
Stage size: suitable for one implementation prompt.
## Deferred Work
Do not include these in the Today implementation:
- Rewriting the `daily` command/report in Today/Tomorrow style.
- Removing or retiring the existing `daily_today` report.
- Making `daily` an alias for `today`.
- Making `today` an alias for `daily`.
- Migrating historical `daily_today` workspace artifacts.
- Adding an inspection compatibility layer that rewrites old metadata IDs.
- Changing Tomorrow template/schema behavior except where shared helpers require
tests to be adjusted.
- Changing Distributor adapter behavior beyond Today template values.
## Open Questions
No open questions block implementation.
The future relationship between `daily`, `daily_today`, and `today` is
explicitly deferred to the next Daily rewrite roadmap. For this implementation,
the required behavior is that Today and Daily remain separate commands and
separate report IDs.