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
todayis a new independent generated-text-template report.todaydoes not replace the existingdaily_todayreport in this roadmap.weatherreporter generate todayis a new public command.weatherreporter generate dailyremains the existing daily command and must not alias to Today.reports.todayis a new independent config override key.reports.dailyandreports.daily_todayremain mapped to the existing daily report while that report exists.- Morning batch includes
todayin the position currently occupied bydaily_today. - Morning batch should not include both
todayanddaily_today. - Evening batch remains
tomorrow. - New Today runtime identity uses report ID
today, artifact grouptoday, batch outputtoday.md, template IDtoday, generated-text schema IDtoday, and prompt IDweather.today_generated_text. - Historical
daily_todayworkspace artifacts are not migrated. - Today uses its own deterministic
today_planningmodule. - 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/moduleand module builders ininternal/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
dailyandtodaycompatibility 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, addToday ID = "today". - Do not remove or alter
DailyTodayin this stage. - In
internal/module/module.go, add:TodayPlanning ID = "today_planning";TodayPlanningOptions struct{}.
- Add
internal/briefing/today_planning_module.go. - Add
TodayPlanningModulewith 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
TomorrowPlanningModuleor thetomorrow_planningstanza. - Register
TodayPlanningininternal/briefing/modules.gowith:- stanza name
today_planning; - supported reports
[]report.ID{report.Today}; - required derived facts matching the daily-summary/daypart facts it uses;
MissingDataEmptyunless implementation discovers a stricter requirement is needed.
- stanza name
- Add
report.Todayto 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.DailyTodaysupport unchanged.
Files To Inspect
internal/report/definition.gointernal/module/module.gointernal/briefing/modules.gointernal/briefing/tomorrow_planning_module.gointernal/briefing/summary_helpers.gointernal/briefing/derived_modules_test.gointernal/briefing/modules_test.go
Acceptance Criteria
module.TodayPlanningis registered and buildable forreport.Today.today_planningis 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.Todaywith fields:Summary string;ForecastDiscussion []string;PrecipitationTiming string;Confidence string.
- Add
ValidateTodaywith 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_discussionparagraph; - 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, andBuildTodayRenderContextininternal/generatedtext. - Today report context should include:
TitleexactlyToday'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
todayand template IDtoday. - Add Today schema/template entries to
internal/reporttemplateif 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.gointernal/generatedtext/render_context.gointernal/generatedtext/catalog.gointernal/generatedtext/*_test.gointernal/reporttemplate/reporttemplate.gointernal/reporttemplate/templates/tomorrow.md.tmplinternal/reporttemplate/schemas/tomorrow.generated_text.schema.jsoninternal/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:
ValidateTodaysuccess, 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.gofile. - 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 bothtodayanddailyas distinct commands.
- add
- 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.gointernal/report/daily_report.gointernal/report/registry.gointernal/report/period.gointernal/report/names.gointernal/report/period_test.gointernal/briefing/modules.gointernal/briefing/package.gointernal/app/app.gointernal/config/reports.gointernal/config/config_test.gointernal/changes
Acceptance Criteria
report.DefaultRegistry()has active definitions for bothtodayand the existing daily report.- Morning batch report IDs are
today,three_day,weekendwhen Weekend is included. - Sunday morning batch report IDs are
today,three_day. dailyandtodaycommand names resolve to different report IDs.reports.todayandreports.daily/reports.daily_todayresolve 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; IDForCommandNamefortodayanddaily;CommandNames()stable order;IDForConfigKeyfortoday,daily, anddaily_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.ReportTodayif app keeps report-kind constants. - Keep
app.ReportDailymapped to the existing daily command/report. - Update CLI help text to show
generate todayas a distinct command. - Keep
generate dailyhelp text separate. - Parse
--date YYYY-MM-DDfor Today.- If omitted, use current local date in the configured timezone.
- Keep existing
daily --datebehavior unchanged.
- Ensure app generation resolves:
todaytoreport.Today;dailyto 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.gointernal/app/app_test.gointernal/cli/root.gointernal/cli/root_test.gointernal/stateinternal/adapters/scriptoriuminternal/adapters/distributorinternal/configexamples/config.yml
Acceptance Criteria
weatherreporter generate todaysucceeds in tests through the generated-text template path.weatherreporter generate dailystill succeeds through the existing daily path and does not produce Today identity artifacts.weatherreporter generate today --date YYYY-MM-DDresolves that local civil day.- Morning batch uses Today artifacts and
today.mdoutput copy names. - Notification debug artifacts record Today report ID, pipeline ID, bundle ID, and bundle paths.
- Optional
--outand batch--out-dirbehavior 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 dailyremains separate. - CLI
--datesupport 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 dailydocumented as a separate command; - document
--datefor Today generation; - keep Daily docs accurate for the existing command.
- add
docs/config.md:- document
reports.today; - keep
reports.daily/reports.daily_todaydocumentation separate while the existing daily report remains implemented.
- document
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.
- document
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
todayanddailyare aliases. - Maintained examples load successfully.
- CLI examples match actual
--helpoutput 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 todayis listed in help and docs.generate dailyremains listed separately.generate dailydoes not resolve to report IDtoday.reports.dailyandreports.todayare 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
dailycommand/report in Today/Tomorrow style. - Removing or retiring the existing
daily_todayreport. - Making
dailyan alias fortoday. - Making
todayan alias fordaily. - Migrating historical
daily_todayworkspace 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.