# Daily Report Implementation Roadmap ## Purpose This roadmap defines the staged implementation plan for `docs/roadmap/daily.md`. It is written for an LLM coding agent that will implement the new generated-text-template `daily` report in order. The target feature is a clean replacement of the existing active Daily path: `weatherreporter generate daily` should run a new independent `daily` report that requires `--date YYYY-MM-DD`, uses generated structured text plus a Markdown template, and initially renders the same report shape as `tomorrow`. This file is planning-only. Non-roadmap documentation should be updated only in the implementation stage after the behavior exists. ## Source Roadmap Use `docs/roadmap/daily.md` as the feature roadmap and source of user intent. That document defines the target state, report identity, command behavior, module composition, generated-text contract, and replacement policy for the legacy `daily_today` report. ## Locked Decisions - `daily` is a separate report ID, not an alias for `today`, `tomorrow`, or legacy `daily_today`. - `weatherreporter generate daily` remains the public command, but it must run the new generated-text-template Daily report. - `weatherreporter generate daily` requires `--date YYYY-MM-DD`. - The supplied date is interpreted as a local civil date in the effective report timezone after config and `--tz` overrides. - The valid period is `[00:00, next 00:00)` for the selected local civil day. - Daily is manually targeted and is not added to morning or evening batches. - Daily uses prompt ID `weather.daily_generated_text`. - Daily uses template ID `daily` and generated-text schema ID `daily`. - Daily uses artifact group `daily` and batch output name `daily.md`. - Daily has its own generated-text type, schema, prompt asset, Markdown template, render-context type, and planning module. - Daily may share private helpers with Tomorrow when mechanics are identical, but it must not expose Tomorrow-specific public types or stanzas. - Remove active `daily_today` report definition and `reports.daily_today` config-key support. - Historical workspace artifacts with `daily_today` metadata are not migrated. ## Implementation Principles - Preserve public CLI syntax except for the intentional breaking change that `generate daily` now requires `--date`. - Keep report identity, prompt IDs, generation mode, module order, artifact groups, batch membership, and compatibility policy centralized in `internal/report`. - Keep CLI parsing in `internal/cli`; enforce the missing-date policy in both CLI parsing and the report resolver so non-CLI callers cannot accidentally generate an undated Daily report. - Keep generated-text schema validation in `internal/generatedtext`. - Keep embedded schemas, prompts, and templates as separate files under `internal/reporttemplate`. - Keep deterministic module output in `internal/briefing` and module IDs/options in `internal/module`. - Do not add compatibility aliases for `daily_today`. - Update implemented documentation only after the code behavior exists. ## Stage 1: Daily Planning Module ### Goal Add the Daily-specific planning module before activating the new report. This keeps module and report work separable and makes the eventual Daily default composition explicit. ### Files To Inspect - `internal/module/module.go` - `internal/briefing/tomorrow_planning_module.go` - `internal/briefing/today_planning_module.go` - `internal/briefing/modules.go` - `internal/briefing/derived_modules_test.go` - `docs/roadmap/daily.md` ### Implementation - Add `module.DailyPlanning` with value `daily_planning`. - Add `module.DailyPlanningOptions struct{}`. - Add `internal/briefing/daily_planning_module.go`. - Add `DailyPlanningModule` with the initial fields: - `morning_readiness` - `commute_school_workday_concerns` - `overnight_change_watch` - Build the Daily planning module from the same underlying summary inputs as Tomorrow planning, but do not reuse the public `TomorrowPlanningModule` type. - Share private helper functions with Tomorrow only when the helper expresses report-neutral mechanics. - Register `daily_planning` in the default module registry with: - stanza name `daily_planning` - default options `module.DailyPlanningOptions{}` - supported reports `[]report.ID{report.Daily}` after `report.Daily` exists in Stage 3; if Stage 1 is implemented before `report.Daily`, add the module ID/options now and wire report support in Stage 3. ### Acceptance Criteria - `daily_planning` is a distinct module ID and stanza. - No Daily path emits `tomorrow_planning`. - No public Daily type aliases or embeds `TomorrowPlanningModule`. - Unsupported reports receive an actionable unsupported-module error. ### Tests - Add or update module tests for: - output shape; - missing-summary fallback behavior; - supported Daily report; - unsupported Today, Tomorrow, Hourly, Three-Day, Weekend, and Storm reports. - Suggested focused command after this stage: ```sh go test ./internal/module ./internal/briefing ``` ### Prompt Size Small enough for one implementation prompt. ## Stage 2: Daily GeneratedText Contract And Assets ### Goal Add Daily generated-text validation and embedded assets while keeping the active report registry unchanged until Stage 3. ### Files To Inspect - `internal/generatedtext/tomorrow.go` - `internal/generatedtext/today.go` - `internal/generatedtext/catalog.go` - `internal/generatedtext/*_test.go` - `internal/reporttemplate/reporttemplate.go` - `internal/reporttemplate/templates/tomorrow.md.tmpl` - `internal/reporttemplate/prompts/tomorrow.generated_text.md` - `internal/reporttemplate/schemas/tomorrow.generated_text.schema.json` - `internal/reporttemplate/reporttemplate_test.go` ### Implementation - Add `internal/generatedtext/daily.go`. - Add `type Daily struct` with the same public JSON shape as Tomorrow: - `summary` required string - `forecast_discussion` required `[]string` - `precipitation_timing` optional string - `confidence` optional string - Add `ValidateDaily`. - Match Tomorrow validation semantics: - reject malformed JSON; - reject unknown fields; - reject trailing values; - trim string fields; - trim each forecast-discussion paragraph; - drop blank forecast-discussion paragraphs; - require nonblank summary; - require at least one nonblank forecast-discussion paragraph; - return canonical normalized JSON. - Add `internal/reporttemplate/schemas/daily.generated_text.schema.json`. - Add `internal/reporttemplate/prompts/daily.generated_text.md`. - Add `internal/reporttemplate/templates/daily.md.tmpl`, initially matching the Tomorrow rendered Markdown structure while referencing Daily render-context fields. - Register Daily in the generated-text catalog with schema ID `daily`. - Register the Daily schema and template asset lookup in `internal/reporttemplate`. ### Acceptance Criteria - Daily generated text validates independently from Tomorrow. - The Daily schema and template can be looked up by ID `daily`. - The Daily prompt asset exists as maintained source material for out-of-band Scriptorium registration. - No report registry behavior changes yet unless Stage 2 and Stage 3 are intentionally implemented together. ### Tests - Add generated-text tests for: - valid Daily JSON; - missing `summary`; - missing or blank `forecast_discussion`; - unknown fields; - trailing JSON; - trimming and canonical output. - Add catalog tests proving schema ID `daily` maps to `ValidateDaily`. - Add reporttemplate tests for Daily schema lookup, template lookup, and basic render behavior. - Suggested focused command: ```sh go test ./internal/generatedtext ./internal/reporttemplate ``` ### Prompt Size Small enough for one implementation prompt. ## Stage 3: Daily Render Context ### Goal Add a dedicated Daily template context that mirrors Tomorrow mechanics without exposing Tomorrow public types. ### Files To Inspect - `internal/generatedtext/render_context.go` - `internal/generatedtext/render_context_test.go` - `internal/briefing/*_module.go` - `internal/module/module.go` - `internal/facts` - `internal/reporttemplate/templates/tomorrow.md.tmpl` ### Implementation - Add: ```go type DailyRenderContext struct { Report DailyReportContext GeneratedText Daily Modules DailyTemplateModules Collected facts.CollectedFacts Derived facts.DerivedFacts } ``` - Add `DailyReportContext` with: - `Title` - `ForecastDate` - `ForecastDateLabel` - `ForecastDayName` - `GeneratedAt` - `GeneratedAtLabel` - `ValidPeriod` - `Timezone` - Add `DailyTemplateModules` with the Daily template surfaces defined in `docs/roadmap/daily.md`, including `DailyPlanning`. - Add a Daily daypart context if the Tomorrow daypart context is not already generic enough to use privately. - Add `BuildDailyRenderContext`. - Generate `Title` as `'s Weather`, for example `Monday's Weather`. - Build `ForecastDateLabel` from the valid-period start in the effective timezone. - Reuse private module snapshot lookup and ordered-daypart helpers where useful, but keep Daily's exported types distinct. ### Acceptance Criteria - Daily render context is independent and template-friendly. - Optional modules are nil when omitted by missing-data policy. - Ordered dayparts use the same ordering and omission behavior as Tomorrow. - Daily planning is available as `.Modules.DailyPlanning`. - The render context exposes `Collected` and `Derived` for advanced template use, consistent with current generated-text-template reports. ### Tests - Add render-context tests for: - title and forecast date label; - generated-at label; - valid period and timezone; - Daily planning extraction; - omitted optional modules; - daypart ordering; - collected and derived facts propagation. - Suggested focused command: ```sh go test ./internal/generatedtext ./internal/reporttemplate ``` ### Prompt Size Small enough for one implementation prompt. ## Stage 4: Report Registry Cutover ### Goal Replace the active legacy `daily_today` report definition with the new generated text-template `daily` report. ### Files To Inspect - `internal/report/definition.go` - `internal/report/daily_report.go` - `internal/report/today_report.go` - `internal/report/tomorrow_report.go` - `internal/report/names.go` - `internal/report/period.go` - `internal/report/period_test.go` - `internal/briefing/modules.go` - `internal/app/app.go` - `internal/changes` ### Implementation - Add or change the active Daily report ID to: ```go Daily ID = "daily" ``` - Remove active use of `DailyToday`. - Prefer removing the exported `DailyToday` constant entirely if no active code needs it. - If temporary compile sequencing requires keeping it during this stage, remove it before the stage is complete. - Replace `dailyTodayDefinition` with `dailyDefinition`. - Daily definition values: - ID `Daily` - name `Daily Report` - prompt ID `weather.daily_generated_text` - generation mode `GenerationModeGeneratedTextTemplate` - template ID `daily` - generated-text schema ID `daily` - artifact group `daily` - batch output name `daily.md` - generated `true` - compatible prior IDs `[]ID{Daily}` - comparison strategy same valid local date - no morning batch membership - no evening batch membership - Add `dailyModules()` with the module order from `docs/roadmap/daily.md`: - `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` - `daily_planning` - `hourly_forecast` - Add `resolveDaily` requiring `ResolveRequest.Date`. - Return an actionable error when `Date` is zero. - Resolve the date as a local civil day in `ResolveRequest.Location`. - Update report name/config resolution: - `IDForCommandName("daily")` returns `Daily`. - `IDForConfigKey("daily")` returns `Daily`. - `IDForConfigKey("daily_today")` returns an unknown config-key error. - Update module registry supported-report lists: - replace `report.DailyToday` with `report.Daily` for shared Daily-compatible modules; - add `report.Daily` to Daily planning support. - Update Recent Changes dispatch where it currently groups daily-style reports, replacing `DailyToday` with `Daily`. - Ensure `BatchReports` continues to include Today in the morning batch and Tomorrow in the evening batch, with no Daily membership. ### Acceptance Criteria - `report.DefaultRegistry()` includes `daily` and not `daily_today`. - `weatherreporter generate daily` resolves to report ID `daily`. - `reports.daily` config overrides apply to Daily. - `reports.daily_today` is rejected. - Daily uses generated-text-template mode. - Daily metadata, RunID, artifact paths, distributor variables, and report output all use report ID/artifact group `daily`. - Scheduled batches do not include Daily. ### Tests - Update report tests for: - registry membership; - command-name resolution; - config-key resolution; - generated-text-template metadata; - explicit-date valid period; - missing-date resolver error; - batch membership. - Update module registry tests to prove every default Daily module is buildable. - Update Recent Changes tests to use Daily snapshots instead of legacy `daily_today` snapshots where applicable. - Suggested focused command: ```sh go test ./internal/report ./internal/briefing ./internal/changes ``` ### Prompt Size This is the first larger cutover stage. It is still suitable for one implementation prompt if the agent works package by package and keeps tests focused. If compile errors spread widely, split into: 1. report ID/name/config resolution; 2. module registry and Recent Changes updates; 3. report tests and stale-symbol cleanup. ## Stage 5: CLI And App Workflow Integration ### Goal Make the public command `weatherreporter generate daily --date YYYY-MM-DD` execute the full generated-text-template workflow and reject missing dates. ### Files To Inspect - `internal/cli/root.go` - `internal/cli/root_test.go` - `internal/app/app.go` - `internal/app/app_test.go` - `internal/state` - `internal/adapters/scriptorium` - `internal/adapters/distributor` - `examples/config.yml` ### Implementation - Update CLI date policy: - `generate daily` requires `--date YYYY-MM-DD`; - missing `--date` returns a concise actionable error; - malformed `--date` remains an error; - `generate today` keeps its current behavior; - `generate tomorrow` remains date-free. - Ensure `--config`, `--units`, `--tz`, `--out`, and distributor notification behavior continue to work as they do for other generated-text-template reports. - Update app generated-text dispatch so Daily uses: - prompt ID `weather.daily_generated_text`; - schema ID `daily`; - template ID `daily`; - `BuildDailyRenderContext`. - Ensure persisted artifacts include: - raw generated text JSON; - generated-text run result; - normalized generated text; - render context; - rendered Markdown; - metadata; - optional output copy. - Ensure distributor upload, when enabled, uses the managed Daily Markdown report path and Daily template variables. ### Acceptance Criteria - `weatherreporter generate daily --date YYYY-MM-DD` runs through the generated-text-template path. - `weatherreporter generate daily` without `--date` fails before generation. - `weatherreporter generate daily --date bad-date` fails before generation. - Existing Today, Tomorrow, Hourly, Three-Day, Weekend, and Storm commands keep their current syntax. - Daily app workflow saves the same classes of artifacts as Today/Tomorrow. - Daily optional `--out` copy behavior remains separate from the managed report path. ### Tests - Update CLI parser tests for: - required Daily date; - malformed Daily date; - valid Daily date; - Today and Tomorrow date behavior unchanged. - Add or update app workflow tests for: - Daily generated-text Scriptorium request; - Daily schema validation; - Daily render context persistence; - Daily rendered report path; - Daily optional output copy; - Daily distributor request values when notification is enabled. - Suggested focused command: ```sh go test ./internal/cli ./internal/app ./internal/state ``` ### Prompt Size Medium. Suitable for one implementation prompt after Stages 1-4 compile. ## Stage 6: Legacy DailyToday Sweep ### Goal Remove stale active-code references to the legacy `daily_today` report after the new Daily path is working. ### Files To Inspect - `internal/report` - `internal/briefing` - `internal/config` - `internal/app` - `internal/changes` - `internal/promptinput` - `internal/state` - `internal/generatedtext` - `internal/reporttemplate` - package tests ### Implementation - Remove remaining production-code references to: - `DailyToday`; - `daily_today`; - `weather.daily_report` on the active Daily path. - Update test fixtures and helper names that represent the active Daily report. - Keep references only when intentionally describing historical artifacts in roadmap text. - Do not add migration logic for old workspace files. - Do not keep `daily_today` as a hidden alias. ### Acceptance Criteria - `rg -n "DailyToday|daily_today|weather.daily_report" internal examples docs` has no production-code or implemented-doc matches, except future/historical roadmap references if intentionally retained. - All report and module defaults refer to active report IDs. - Tests no longer assume `daily` resolves to `daily_today`. ### Tests - Run: ```sh rg -n "DailyToday|daily_today|weather.daily_report" internal examples docs go test ./internal/... ``` ### Prompt Size Small to medium. This can be combined with Stage 5 only if the agent has enough context and compile errors are already localized. ## Stage 7: Documentation And Examples ### Goal Update implemented documentation and maintained examples after the Daily feature exists. Keep planned or deferred behavior only under `docs/roadmap/`. ### Files To Inspect - `README.md` - `docs/cli.md` - `docs/config.md` - `docs/operations.md` - `docs/templates.md` - `docs/troubleshooting.md` - `docs/internal/report-registry.md` - `docs/internal/generatedtext.md` - `docs/internal/reporttemplate.md` - `docs/internal/module.md` - `docs/internal/app-orchestration.md` - `examples/config.yml` - tests that load examples ### Implementation - Update `docs/cli.md`: - document `weatherreporter generate daily --date YYYY-MM-DD`; - describe missing date as an error; - keep Today and Tomorrow documented as separate commands. - Update `docs/config.md`: - document `reports.daily` module overrides; - remove active `reports.daily_today` references. - Update `docs/operations.md`: - document Daily as manually targeted by date; - document Daily artifact paths and managed-report behavior. - Update `docs/templates.md`: - document Daily template variables and generated-text fields. - Update internal docs: - report registry Daily identity; - generated-text Daily validation; - reporttemplate Daily assets; - `daily_planning` module; - app orchestration only where Daily differs from generic generated-text-template flow. - Update `examples/config.yml` only if it includes report module override examples that should reference `daily`. - Do not describe scheduled Daily behavior, old artifact migration, or future template divergence as implemented behavior. ### Acceptance Criteria - Non-roadmap docs describe only implemented Daily behavior. - No implemented docs describe `daily_today` as an active report. - Examples use implemented report config keys only. - README remains concise and links to canonical docs instead of duplicating reference material. ### Tests And Checks ```sh go test ./internal/config ./internal/cli git diff --check rg -n "daily_today|weather.daily_report|DailyToday" README.md docs examples ``` Review any remaining matches manually. Roadmap references may remain only when they are clearly historical or future planning context. ### Prompt Size Medium. Suitable for one implementation prompt after code behavior exists. ## Stage 8: Final Validation ### Goal Verify the complete Daily cutover and catch stale references or documentation drift. ### Required Commands ```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 ./... go run ./cmd/weatherreporter --help git diff --check ``` ### Stale Reference Checks ```sh rg -n "DailyToday|daily_today|weather.daily_report" internal examples README.md docs rg -n "reports\\.daily_today|weatherreporter generate daily(?!.*--date)" docs examples ``` The second command uses a regex feature that may not be supported by every `rg` build. If it fails, use simpler searches: ```sh rg -n "reports\\.daily_today|weatherreporter generate daily" docs examples ``` Review remaining matches manually. ### Manual Review Items - Confirm `weatherreporter --help` lists `daily`, `today`, and `tomorrow` distinctly. - Confirm `generate daily` help or parser behavior makes `--date` required. - Confirm morning and evening batch definitions did not change except where tests explicitly prove intended behavior. - Confirm distributor template variables for Daily use report ID/artifact group `daily`. - Confirm old workspace artifacts are not migrated or rewritten. ## Deferred Work Do not include these in the Daily replacement implementation: - scheduling Daily in morning or evening batches; - migration of old `daily_today` workspace artifacts; - compatibility alias support for `daily_today`; - changing Today or Tomorrow semantics; - adding a generic report-template inheritance system; - consolidating Daily and Tomorrow public generated-text types; - making Daily default to today or tomorrow when `--date` is omitted; - changing Scriptorium prompt registration behavior, which remains out of band. ## Open Questions No open questions block implementation. The roadmap intentionally chooses the clean-break option for all previously ambiguous areas: `daily_today` is removed from active behavior, `--date` is required, Daily is not scheduled, and Daily owns separate template/schema/prompt and planning-module surfaces even when they initially match Tomorrow. ## Global Validation Checklist - `go test ./...` passes. - `go run ./cmd/weatherreporter --help` is accurate. - `git diff --check` passes. - `weatherreporter generate daily --date YYYY-MM-DD` is the only valid Daily generation form. - `weatherreporter generate daily` without `--date` fails. - `reports.daily` is accepted. - `reports.daily_today` is rejected. - No active code path uses `daily_today`. - Daily generated reports use `weather.daily_generated_text`. - Daily rendered Markdown uses the Daily template. - Daily generated-text JSON uses the Daily schema. - Daily data packages include `daily_planning`, not `tomorrow_planning`. - Daily is absent from morning and evening scheduled batches. - Non-roadmap documentation describes only implemented behavior.