Files
weatherreporter/docs/roadmap/implementation.md

22 KiB

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:

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:

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:

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:

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:

go test ./internal/config ./internal/cli ./internal/app
go test ./...
go run ./cmd/weatherreporter --help
git diff --check

Manual checks:

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:

go test ./...
go run ./cmd/weatherreporter --help
git diff --check

Run focused package checks:

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:

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.