Split Tomorrow report identity

This commit is contained in:
2026-06-14 23:22:31 +00:00
parent afe6803a63
commit 386263784c
20 changed files with 75 additions and 73 deletions

View File

@@ -47,7 +47,7 @@ embedded hourly template.
`--end`. `--end`.
`run morning` generates Daily Today and the 3-Day Outlook, plus Weekend Outlook `run morning` generates Daily Today and the 3-Day Outlook, plus Weekend Outlook
except on Sunday. `run evening` generates the Tomorrow Planning Brief. Batch except on Sunday. `run evening` generates the Tomorrow Report. Batch
runs continue independent reports after a failure, print a JSON summary to runs continue independent reports after a failure, print a JSON summary to
stdout, write compact status lines to stderr, and return nonzero when any report stdout, write compact status lines to stderr, and return nonzero when any report
failed. `--out-dir` writes extra Markdown copies for the operator; distributor failed. `--out-dir` writes extra Markdown copies for the operator; distributor

View File

@@ -182,8 +182,8 @@ snapshot exists and a threshold is crossed.
report definitions. Omit a report entry to use its default module order. report definitions. Omit a report entry to use its default module order.
Supported report keys are `daily`, `tomorrow`, `hourly`, `three_day`, Supported report keys are `daily`, `tomorrow`, `hourly`, `three_day`,
`weekend`, and `storm`. Canonical report IDs such as `daily_today` and `weekend`, and `storm`. Canonical report IDs such as `daily_today` are also
`daily_tomorrow` are also accepted. accepted.
Each report entry supports: Each report entry supports:

View File

@@ -135,7 +135,7 @@ failure context.
## Batch Workflow ## Batch Workflow
`run morning` resolves Daily Today, 3-Day Outlook, and Weekend Outlook except `run morning` resolves Daily Today, 3-Day Outlook, and Weekend Outlook except
on Sunday. `run evening` resolves Daily Tomorrow. Batch output copy names come on Sunday. `run evening` resolves Tomorrow Report. Batch output copy names come
from report definitions. Batch generation continues independent reports after a from report definitions. Batch generation continues independent reports after a
failure, records each result, writes compact status lines to stderr, emits a failure, records each result, writes compact status lines to stderr, emits a
JSON summary to stdout, and returns an aggregate error when any report failed. JSON summary to stdout, and returns an aggregate error when any report failed.

View File

@@ -36,8 +36,8 @@ the embedded hourly template.
| Report | ID | Prompt | Generation mode | Artifact group | Batch copy | Prior compatibility | | Report | ID | Prompt | Generation mode | Artifact group | Batch copy | Prior compatibility |
| --- | --- | --- | --- | --- | --- | --- | | --- | --- | --- | --- | --- | --- | --- |
| Daily Today | `daily_today` | `weather.daily_report` | `scriptorium_markdown` | `daily` | `daily.md` | Daily Today, Daily Tomorrow | | Daily Today | `daily_today` | `weather.daily_report` | `scriptorium_markdown` | `daily` | `daily.md` | Daily Today |
| Daily Tomorrow | `daily_tomorrow` | `weather.daily_report` | `scriptorium_markdown` | `daily` | `tomorrow.md` | Daily Today, Daily Tomorrow | | Tomorrow Report | `tomorrow` | `weather.daily_report` | `scriptorium_markdown` | `tomorrow` | `tomorrow.md` | Tomorrow Report |
| Hourly Report | `hourly` | `weather.hourly_generated_text` | `generated_text_template` | `hourly` | `hourly.md` | Hourly Report | | Hourly Report | `hourly` | `weather.hourly_generated_text` | `generated_text_template` | `hourly` | `hourly.md` | Hourly Report |
| 3-Day Outlook | `three_day` | `weather.three_day_outlook` | `scriptorium_markdown` | `three-day` | `three-day.md` | 3-Day Outlook | | 3-Day Outlook | `three_day` | `weather.three_day_outlook` | `scriptorium_markdown` | `three-day` | `three-day.md` | 3-Day Outlook |
| Weekend Outlook | `weekend` | `weather.weekend_outlook` | `scriptorium_markdown` | `weekend` | `weekend.md` | Weekend Outlook | | Weekend Outlook | `weekend` | `weather.weekend_outlook` | `scriptorium_markdown` | `weekend` | `weekend.md` | Weekend Outlook |
@@ -49,7 +49,7 @@ All report definitions are eligible for generation.
- Daily Today covers the selected local civil day, or the current local civil - Daily Today covers the selected local civil day, or the current local civil
day when no date override is supplied. day when no date override is supplied.
- Daily Tomorrow covers the next local civil day from generation time. - Tomorrow Report covers the next local civil day from generation time.
- Hourly Report covers the half-open six-hour period from generation time in - Hourly Report covers the half-open six-hour period from generation time in
the effective report timezone. The duration is an internal report constant, the effective report timezone. The duration is an internal report constant,
not a configuration field. not a configuration field.
@@ -81,7 +81,7 @@ module overrides can use the `hourly` key for Hourly Report.
## Batch Membership ## Batch Membership
Morning batches include Daily Today, 3-Day Outlook, and Weekend Outlook except Morning batches include Daily Today, 3-Day Outlook, and Weekend Outlook except
on Sunday. Evening batches include Daily Tomorrow. Hourly Report is not part on Sunday. Evening batches include Tomorrow Report. Hourly Report is not part
of a scheduled batch. of a scheduled batch.
## State And App Usage ## State And App Usage
@@ -110,7 +110,7 @@ Inspect:
## Invariants ## Invariants
- Report selection goes through the registry. - Report selection goes through the registry.
- Daily Today and Daily Tomorrow both use `weather.daily_report`. - Daily Today and Tomorrow Report both use `weather.daily_report`.
- Valid periods are half-open intervals independent of rendered report text. - Valid periods are half-open intervals independent of rendered report text.
- Artifact grouping, batch output filenames, generated-report eligibility, - Artifact grouping, batch output filenames, generated-report eligibility,
default module composition, comparison compatibility, and comparison strategy default module composition, comparison compatibility, and comparison strategy

View File

@@ -91,7 +91,9 @@ Prior snapshot lookup reads stored metadata through the shared lookup path and
selects the latest earlier snapshot whose report ID is compatible with the selects the latest earlier snapshot whose report ID is compatible with the
current report definition. current report definition.
- Daily Today and Daily Tomorrow are compatible with each other for the same - Daily Today compares with prior Daily Today snapshots for the same valid
local date.
- Tomorrow Report compares with prior Tomorrow Report snapshots for the same
valid local date. valid local date.
- 3-Day Outlook compares with prior 3-Day snapshots for the same valid local - 3-Day Outlook compares with prior 3-Day snapshots for the same valid local
date. date.

View File

@@ -43,7 +43,7 @@ weatherreporter run evening
``` ```
`run morning` generates Daily Today and the 3-Day Outlook, plus Weekend Outlook `run morning` generates Daily Today and the 3-Day Outlook, plus Weekend Outlook
except on Sunday. `run evening` generates the Tomorrow Planning Brief. Batch except on Sunday. `run evening` generates the Tomorrow Report. Batch
commands print a JSON summary to stdout, write compact per-report status lines commands print a JSON summary to stdout, write compact per-report status lines
to stderr, continue independent reports after one report fails, and return to stderr, continue independent reports after one report fails, and return
nonzero when any report failed. When notification is configured, the summary and nonzero when any report failed. When notification is configured, the summary and
@@ -246,7 +246,8 @@ source provenance and warnings without dumping full weather payloads.
Recent Changes are computed from structured module snapshots, not rendered Recent Changes are computed from structured module snapshots, not rendered
Markdown or YAML text. Markdown or YAML text.
Daily Today and Daily Tomorrow can compare with each other when they cover the Daily Today compares with prior Daily Today snapshots for the same valid local
date. Tomorrow Report compares with prior Tomorrow Report snapshots for the
same valid local date. 3-Day Outlook compares with prior compatible 3-Day same valid local date. 3-Day Outlook compares with prior compatible 3-Day
snapshots for the same valid local date. Weekend Outlook compares with prior snapshots for the same valid local date. Weekend Outlook compares with prior
compatible Weekend snapshots for the same weekend window. Hourly Report and compatible Weekend snapshots for the same weekend window. Hourly Report and

View File

@@ -394,7 +394,7 @@ func reportIDForCommand(kind ReportKind) (report.ID, error) {
case ReportDaily: case ReportDaily:
return report.DailyToday, nil return report.DailyToday, nil
case ReportTomorrow: case ReportTomorrow:
return report.DailyTomorrow, nil return report.Tomorrow, nil
case ReportHourly: case ReportHourly:
return report.Hourly, nil return report.Hourly, nil
case ReportThreeDay: case ReportThreeDay:
@@ -1085,7 +1085,7 @@ func recentChanges(ctx context.Context, store state.Store, priorSnapshot *state.
PrecipTimingShiftMinutes: cfg.PrecipTimingShiftMinutes, PrecipTimingShiftMinutes: cfg.PrecipTimingShiftMinutes,
} }
switch reportID { switch reportID {
case report.DailyToday, report.DailyTomorrow: case report.DailyToday, report.Tomorrow:
return changes.CompareDaily(previous, current, thresholds) return changes.CompareDaily(previous, current, thresholds)
case report.ThreeDay: case report.ThreeDay:
return changes.CompareThreeDay(previous, current, thresholds) return changes.CompareThreeDay(previous, current, thresholds)

View File

@@ -1240,7 +1240,7 @@ func TestGenerateTomorrowReportUsesTomorrowBriefingDate(t *testing.T) {
t.Fatalf("GenerateReport() error = %v", err) t.Fatalf("GenerateReport() error = %v", err)
} }
if result.Metadata.ReportID != report.DailyTomorrow || result.Metadata.Variant != "tomorrow" { if result.Metadata.ReportID != report.Tomorrow || result.Metadata.Variant != "tomorrow" {
t.Fatalf("metadata report/variant = %q/%q, want tomorrow", result.Metadata.ReportID, result.Metadata.Variant) t.Fatalf("metadata report/variant = %q/%q, want tomorrow", result.Metadata.ReportID, result.Metadata.Variant)
} }
dailySummary, ok, err := module.StanzaValue[map[string]any](result.ModuleSnapshot, "derived_daily_summary") dailySummary, ok, err := module.StanzaValue[map[string]any](result.ModuleSnapshot, "derived_daily_summary")
@@ -1253,12 +1253,12 @@ func TestGenerateTomorrowReportUsesTomorrowBriefingDate(t *testing.T) {
if _, ok := result.ModuleSnapshot.LookupStanza("tomorrow_planning"); !ok { if _, ok := result.ModuleSnapshot.LookupStanza("tomorrow_planning"); !ok {
t.Fatal("tomorrow_planning stanza missing") t.Fatal("tomorrow_planning stanza missing")
} }
if !strings.Contains(filepath.Base(result.ReportPath), "daily_tomorrow") { if !strings.Contains(filepath.Base(result.ReportPath), "tomorrow") {
t.Fatalf("ReportPath = %q, want managed tomorrow report path", result.ReportPath) t.Fatalf("ReportPath = %q, want managed tomorrow report path", result.ReportPath)
} }
} }
func TestTomorrowReportCanCompareAgainstPriorDailySnapshot(t *testing.T) { func TestTomorrowReportCanCompareAgainstPriorTomorrowSnapshot(t *testing.T) {
server := dailyBundleServer(t) server := dailyBundleServer(t)
cfg := config.Defaults() cfg := config.Defaults()
cfg.WeatherAPI.BaseURL = server.URL + "/" cfg.WeatherAPI.BaseURL = server.URL + "/"
@@ -1270,8 +1270,7 @@ func TestTomorrowReportCanCompareAgainstPriorDailySnapshot(t *testing.T) {
} }
priorResolved, err := ResolveGenerate(GenerateRequest{ priorResolved, err := ResolveGenerate(GenerateRequest{
Config: cfg, Config: cfg,
Report: ReportDaily, Report: ReportTomorrow,
Date: mustParse("2026-05-30T12:00:00-05:00"),
}, mustParse("2026-05-29T17:00:00-05:00")) }, mustParse("2026-05-29T17:00:00-05:00"))
if err != nil { if err != nil {
t.Fatalf("ResolveGenerate(prior) error = %v", err) t.Fatalf("ResolveGenerate(prior) error = %v", err)
@@ -1301,10 +1300,10 @@ func TestTomorrowReportCanCompareAgainstPriorDailySnapshot(t *testing.T) {
t.Fatalf("GenerateReport() error = %v", err) t.Fatalf("GenerateReport() error = %v", err)
} }
if result.PriorSnapshot == nil { if result.PriorSnapshot == nil {
t.Fatal("PriorSnapshot = nil, want compatible prior daily snapshot") t.Fatal("PriorSnapshot = nil, want compatible prior tomorrow snapshot")
} }
if len(result.RecentChanges) == 0 { if len(result.RecentChanges) == 0 {
t.Fatal("RecentChanges length = 0, want changes from compatible prior daily snapshot") t.Fatal("RecentChanges length = 0, want changes from compatible prior tomorrow snapshot")
} }
} }
@@ -1622,7 +1621,7 @@ func TestResolveGenerateMapsCommandToReportDefinition(t *testing.T) {
{ {
name: "tomorrow", name: "tomorrow",
kind: ReportTomorrow, kind: ReportTomorrow,
wantID: report.DailyTomorrow, wantID: report.Tomorrow,
wantPrompt: "weather.daily_report", wantPrompt: "weather.daily_report",
wantStart: "2026-05-30T00:00:00-05:00", wantStart: "2026-05-30T00:00:00-05:00",
wantEnd: "2026-05-31T00:00:00-05:00", wantEnd: "2026-05-31T00:00:00-05:00",

View File

@@ -197,7 +197,7 @@ func TestDerivedDaypartSummariesExposeConfiguredKeysAndHazards(t *testing.T) {
func TestOutdoorWindowsAndTomorrowPlanningModulesPreserveDailyContent(t *testing.T) { func TestOutdoorWindowsAndTomorrowPlanningModulesPreserveDailyContent(t *testing.T) {
registry := MustDefaultModuleRegistry() registry := MustDefaultModuleRegistry()
ctx := derivedModuleContext(report.DailyTomorrow) ctx := derivedModuleContext(report.Tomorrow)
outdoorOutput, err := registry.BuildModule(ctx, module.ConfigItem{ID: module.OutdoorWindows}) outdoorOutput, err := registry.BuildModule(ctx, module.ConfigItem{ID: module.OutdoorWindows})
if err != nil { if err != nil {

View File

@@ -249,8 +249,8 @@ func (d ModuleDefinition) ValidateOptions(options any) error {
} }
func defaultModuleDefinitions() []ModuleDefinition { func defaultModuleDefinitions() []ModuleDefinition {
allReports := []report.ID{report.DailyToday, report.DailyTomorrow, report.Hourly, report.ThreeDay, report.Weekend, report.Storm} allReports := []report.ID{report.DailyToday, report.Tomorrow, report.Hourly, report.ThreeDay, report.Weekend, report.Storm}
daypartReports := []report.ID{report.DailyToday, report.DailyTomorrow, report.ThreeDay, report.Weekend} daypartReports := []report.ID{report.DailyToday, report.Tomorrow, report.ThreeDay, report.Weekend}
return []ModuleDefinition{ return []ModuleDefinition{
{ {
ID: module.Metadata, ID: module.Metadata,
@@ -276,7 +276,7 @@ func defaultModuleDefinitions() []ModuleDefinition {
DefaultOptions: module.NarrativeForecastOptions{}, DefaultOptions: module.NarrativeForecastOptions{},
RequiredCollected: []module.FactRequirement{module.CollectedNarrativeForecast}, RequiredCollected: []module.FactRequirement{module.CollectedNarrativeForecast},
RequiredDerived: []module.FactRequirement{module.RequiresDerivedNarrativePeriods}, RequiredDerived: []module.FactRequirement{module.RequiresDerivedNarrativePeriods},
SupportedReports: []report.ID{report.DailyToday, report.DailyTomorrow}, SupportedReports: []report.ID{report.DailyToday, report.Tomorrow},
MissingData: module.MissingDataOmit, MissingData: module.MissingDataOmit,
Builder: buildNarrativeForecastModule, Builder: buildNarrativeForecastModule,
}, },
@@ -286,7 +286,7 @@ func defaultModuleDefinitions() []ModuleDefinition {
DefaultOptions: module.HourlyForecastOptions{}, DefaultOptions: module.HourlyForecastOptions{},
RequiredCollected: []module.FactRequirement{module.CollectedHourlyForecast}, RequiredCollected: []module.FactRequirement{module.CollectedHourlyForecast},
RequiredDerived: []module.FactRequirement{module.RequiresDerivedHourlyPeriods}, RequiredDerived: []module.FactRequirement{module.RequiresDerivedHourlyPeriods},
SupportedReports: []report.ID{report.DailyToday, report.DailyTomorrow, report.Hourly}, SupportedReports: []report.ID{report.DailyToday, report.Tomorrow, report.Hourly},
MissingData: module.MissingDataOmit, MissingData: module.MissingDataOmit,
Builder: buildHourlyForecastModule, Builder: buildHourlyForecastModule,
}, },
@@ -295,7 +295,7 @@ func defaultModuleDefinitions() []ModuleDefinition {
StanzaName: "derived_daily_summary", StanzaName: "derived_daily_summary",
DefaultOptions: module.DerivedDailySummaryOptions{}, DefaultOptions: module.DerivedDailySummaryOptions{},
RequiredDerived: []module.FactRequirement{module.RequiresDerivedDailySummaries, module.RequiresDerivedPrecipTiming}, RequiredDerived: []module.FactRequirement{module.RequiresDerivedDailySummaries, module.RequiresDerivedPrecipTiming},
SupportedReports: []report.ID{report.DailyToday, report.DailyTomorrow}, SupportedReports: []report.ID{report.DailyToday, report.Tomorrow},
MissingData: module.MissingDataError, MissingData: module.MissingDataError,
Builder: buildDerivedDailySummaryModule, Builder: buildDerivedDailySummaryModule,
}, },
@@ -379,7 +379,7 @@ func defaultModuleDefinitions() []ModuleDefinition {
StanzaName: "tomorrow_planning", StanzaName: "tomorrow_planning",
DefaultOptions: module.TomorrowPlanningOptions{}, DefaultOptions: module.TomorrowPlanningOptions{},
RequiredDerived: []module.FactRequirement{module.RequiresDerivedDailySummaries}, RequiredDerived: []module.FactRequirement{module.RequiresDerivedDailySummaries},
SupportedReports: []report.ID{report.DailyTomorrow}, SupportedReports: []report.ID{report.Tomorrow},
MissingData: module.MissingDataEmpty, MissingData: module.MissingDataEmpty,
Builder: buildTomorrowPlanningModule, Builder: buildTomorrowPlanningModule,
}, },

View File

@@ -187,7 +187,7 @@ func variantForReport(id report.ID) string {
switch id { switch id {
case report.DailyToday: case report.DailyToday:
return "today" return "today"
case report.DailyTomorrow: case report.Tomorrow:
return "tomorrow" return "tomorrow"
default: default:
return "" return ""

View File

@@ -133,19 +133,19 @@ func TestRunGenerateTomorrowWritesMarkdownReport(t *testing.T) {
if !strings.Contains(string(report), "# Daily Report") { if !strings.Contains(string(report), "# Daily Report") {
t.Fatalf("report output missing markdown:\n%s", string(report)) t.Fatalf("report output missing markdown:\n%s", string(report))
} }
dataPackagePath := oneArtifact(t, workspaceRoot, "data-packages", "daily", "2026-05-30", "*.data_package.yaml") dataPackagePath := oneArtifact(t, workspaceRoot, "data-packages", "tomorrow", "2026-05-30", "*.data_package.yaml")
data, err := os.ReadFile(dataPackagePath) data, err := os.ReadFile(dataPackagePath)
if err != nil { if err != nil {
t.Fatalf("read managed data package: %v", err) t.Fatalf("read managed data package: %v", err)
} }
if !strings.Contains(string(data), "id: daily_tomorrow") || !strings.Contains(string(data), "tomorrow_planning:") { if !strings.Contains(string(data), "id: tomorrow") || !strings.Contains(string(data), "tomorrow_planning:") {
t.Fatalf("data package output missing tomorrow content:\n%s", string(data)) t.Fatalf("data package output missing tomorrow content:\n%s", string(data))
} }
reportMatches, err := filepath.Glob(filepath.Join(workspaceRoot, "reports", "daily", "*.md")) reportMatches, err := filepath.Glob(filepath.Join(workspaceRoot, "reports", "tomorrow", "*.md"))
if err != nil { if err != nil {
t.Fatalf("glob managed report: %v", err) t.Fatalf("glob managed report: %v", err)
} }
if len(reportMatches) != 1 || !strings.Contains(filepath.Base(reportMatches[0]), "daily_tomorrow") { if len(reportMatches) != 1 || !strings.Contains(filepath.Base(reportMatches[0]), "tomorrow") {
t.Fatalf("managed reports = %#v, want tomorrow report", reportMatches) t.Fatalf("managed reports = %#v, want tomorrow report", reportMatches)
} }
} }
@@ -171,18 +171,18 @@ func TestRunEveningGeneratesTomorrowReport(t *testing.T) {
if err != nil { if err != nil {
t.Fatalf("Run() error = %v", err) t.Fatalf("Run() error = %v", err)
} }
dataPackageMatches, err := filepath.Glob(filepath.Join(workspaceRoot, "data-packages", "daily", "2026-05-30", "*.data_package.yaml")) dataPackageMatches, err := filepath.Glob(filepath.Join(workspaceRoot, "data-packages", "tomorrow", "2026-05-30", "*.data_package.yaml"))
if err != nil { if err != nil {
t.Fatalf("glob data package: %v", err) t.Fatalf("glob data package: %v", err)
} }
if len(dataPackageMatches) != 1 { if len(dataPackageMatches) != 1 {
t.Fatalf("data package files = %#v, want one", dataPackageMatches) t.Fatalf("data package files = %#v, want one", dataPackageMatches)
} }
reportMatches, err := filepath.Glob(filepath.Join(workspaceRoot, "reports", "daily", "*.md")) reportMatches, err := filepath.Glob(filepath.Join(workspaceRoot, "reports", "tomorrow", "*.md"))
if err != nil { if err != nil {
t.Fatalf("glob managed report: %v", err) t.Fatalf("glob managed report: %v", err)
} }
if len(reportMatches) != 1 || !strings.Contains(filepath.Base(reportMatches[0]), "daily_tomorrow") { if len(reportMatches) != 1 || !strings.Contains(filepath.Base(reportMatches[0]), "tomorrow") {
t.Fatalf("managed reports = %#v, want only tomorrow report", reportMatches) t.Fatalf("managed reports = %#v, want only tomorrow report", reportMatches)
} }
} }
@@ -482,10 +482,10 @@ func TestRunEveningReportsNotificationSuccess(t *testing.T) {
distributorServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { distributorServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.URL.Path == "/runs/distributor-run-1" { if r.URL.Path == "/runs/distributor-run-1" {
w.Header().Set("Content-Type", "application/json") w.Header().Set("Content-Type", "application/json")
_, _ = w.Write([]byte(`{"run_id":"distributor-run-1","pipeline_id":"weatherreporter.daily","status":"succeeded","report":{"actions":[{"action":"replace_older"}]}}`)) _, _ = w.Write([]byte(`{"run_id":"distributor-run-1","pipeline_id":"weatherreporter.tomorrow","status":"succeeded","report":{"actions":[{"action":"replace_older"}]}}`))
return return
} }
if r.URL.Path != "/v1/pipelines/weatherreporter.daily/upload" { if r.URL.Path != "/v1/pipelines/weatherreporter.tomorrow/upload" {
http.NotFound(w, r) http.NotFound(w, r)
return return
} }
@@ -521,7 +521,7 @@ func TestRunEveningReportsNotificationSuccess(t *testing.T) {
if len(summary.Reports) != 1 { if len(summary.Reports) != 1 {
t.Fatalf("reports = %#v, want one report", summary.Reports) t.Fatalf("reports = %#v, want one report", summary.Reports)
} }
if summary.Reports[0].NotificationStatus != "succeeded" || summary.Reports[0].NotificationRunID != "distributor-run-1" || summary.Reports[0].NotificationPipelineID != "weatherreporter.daily" { if summary.Reports[0].NotificationStatus != "succeeded" || summary.Reports[0].NotificationRunID != "distributor-run-1" || summary.Reports[0].NotificationPipelineID != "weatherreporter.tomorrow" {
t.Fatalf("notification fields = %#v", summary.Reports[0]) t.Fatalf("notification fields = %#v", summary.Reports[0])
} }
if !strings.Contains(stderr.String(), `notificationStatus="succeeded"`) || !strings.Contains(stderr.String(), `notificationRunId="distributor-run-1"`) { if !strings.Contains(stderr.String(), `notificationStatus="succeeded"`) || !strings.Contains(stderr.String(), `notificationRunId="distributor-run-1"`) {

View File

@@ -111,8 +111,8 @@ func reportIDForConfigKey(key string) (report.ID, error) {
switch normalized { switch normalized {
case "daily", "daily_today": case "daily", "daily_today":
return report.DailyToday, nil return report.DailyToday, nil
case "tomorrow", "daily_tomorrow": case "tomorrow":
return report.DailyTomorrow, nil return report.Tomorrow, nil
case "hourly": case "hourly":
return report.Hourly, nil return report.Hourly, nil
case "three_day", "three_day_outlook": case "three_day", "three_day_outlook":

View File

@@ -115,7 +115,7 @@ func BuildDerived(req BuildDerivedRequest) (DerivedFacts, error) {
switch req.Resolved.Definition.ID { switch req.Resolved.Definition.ID {
case report.Hourly: case report.Hourly:
case report.DailyToday, report.DailyTomorrow: case report.DailyToday, report.Tomorrow:
summary, err := forecast.BuildDailySummary(bundle, period.Start, location, req.Dayparts) summary, err := forecast.BuildDailySummary(bundle, period.Start, location, req.Dayparts)
if err != nil { if err != nil {
return DerivedFacts{}, err return DerivedFacts{}, err

View File

@@ -135,7 +135,7 @@ func TestBuildDerivedOutlookBuildsPartialDaySummariesWithMissingOptionalSources(
func TestBuildDerivedWeekendAndTomorrow(t *testing.T) { func TestBuildDerivedWeekendAndTomorrow(t *testing.T) {
location := testLocation() location := testLocation()
for _, id := range []report.ID{report.DailyTomorrow, report.Weekend} { for _, id := range []report.ID{report.Tomorrow, report.Weekend} {
resolved := resolveForTest(t, id, mustParse("2026-05-29T08:00:00-05:00"), location) resolved := resolveForTest(t, id, mustParse("2026-05-29T08:00:00-05:00"), location)
derived, err := BuildDerived(BuildDerivedRequest{ derived, err := BuildDerived(BuildDerivedRequest{
Resolved: resolved, Resolved: resolved,
@@ -259,7 +259,7 @@ func TestBuildDerivedSelectsSPCConvectiveOutlooksByValidPeriod(t *testing.T) {
}, },
{ {
name: "daily tomorrow", name: "daily tomorrow",
resolved: resolveForTest(t, report.DailyTomorrow, now, location), resolved: resolveForTest(t, report.Tomorrow, now, location),
wantOutlookIDs: []string{"sat-enhanced"}, wantOutlookIDs: []string{"sat-enhanced"},
wantDiscussion: []string{"day2"}, wantDiscussion: []string{"day2"},
}, },

View File

@@ -15,27 +15,27 @@ func dailyTodayDefinition() Definition {
ArtifactGroup: "daily", ArtifactGroup: "daily",
BatchOutputName: "daily.md", BatchOutputName: "daily.md",
Generated: true, Generated: true,
CompatiblePriorIDs: []ID{DailyToday, DailyTomorrow}, CompatiblePriorIDs: []ID{DailyToday},
Modules: dailyTodayModules(), Modules: dailyTodayModules(),
Morning: true, Morning: true,
resolve: resolveDailyToday, resolve: resolveDailyToday,
} }
} }
func dailyTomorrowDefinition() Definition { func tomorrowDefinition() Definition {
return Definition{ return Definition{
ID: DailyTomorrow, ID: Tomorrow,
Name: "Tomorrow Planning Brief", Name: "Tomorrow Report",
PromptID: "weather.daily_report", PromptID: "weather.daily_report",
GenerationMode: GenerationModeScriptoriumMarkdown, GenerationMode: GenerationModeScriptoriumMarkdown,
ComparisonStrategy: CompareSameValidDate, ComparisonStrategy: CompareSameValidDate,
ArtifactGroup: "daily", ArtifactGroup: "tomorrow",
BatchOutputName: "tomorrow.md", BatchOutputName: "tomorrow.md",
Generated: true, Generated: true,
CompatiblePriorIDs: []ID{DailyToday, DailyTomorrow}, CompatiblePriorIDs: []ID{Tomorrow},
Modules: dailyTomorrowModules(), Modules: tomorrowModules(),
Evening: true, Evening: true,
resolve: resolveDailyTomorrow, resolve: resolveTomorrow,
} }
} }
@@ -57,7 +57,7 @@ func dailyTodayModules() []module.ConfigItem {
) )
} }
func dailyTomorrowModules() []module.ConfigItem { func tomorrowModules() []module.ConfigItem {
return moduleItems( return moduleItems(
module.Metadata, module.Metadata,
module.CurrentConditions, module.CurrentConditions,
@@ -83,6 +83,6 @@ func resolveDailyToday(req ResolveRequest) (timeutil.Period, error) {
return timeutil.CivilDay(req.Now, req.Location), nil return timeutil.CivilDay(req.Now, req.Location), nil
} }
func resolveDailyTomorrow(req ResolveRequest) (timeutil.Period, error) { func resolveTomorrow(req ResolveRequest) (timeutil.Period, error) {
return timeutil.CivilDay(req.Now.In(req.Location).AddDate(0, 0, 1), req.Location), nil return timeutil.CivilDay(req.Now.In(req.Location).AddDate(0, 0, 1), req.Location), nil
} }

View File

@@ -13,7 +13,7 @@ type ID string
const ( const (
DailyToday ID = "daily_today" DailyToday ID = "daily_today"
DailyTomorrow ID = "daily_tomorrow" Tomorrow ID = "tomorrow"
Hourly ID = "hourly" Hourly ID = "hourly"
ThreeDay ID = "three_day" ThreeDay ID = "three_day"
Weekend ID = "weekend" Weekend ID = "weekend"

View File

@@ -32,7 +32,7 @@ func (r Registry) BatchReports(batch Batch, req ResolveRequest) ([]Resolved, err
} }
return r.resolveIDs(ids, req) return r.resolveIDs(ids, req)
case Evening: case Evening:
return r.resolveIDs([]ID{DailyTomorrow}, req) return r.resolveIDs([]ID{Tomorrow}, req)
default: default:
return nil, fmt.Errorf("unknown batch %q", batch) return nil, fmt.Errorf("unknown batch %q", batch)
} }

View File

@@ -37,7 +37,7 @@ func TestTomorrowValidPeriodFromEveningGeneration(t *testing.T) {
location := mustLoadLocation(t) location := mustLoadLocation(t)
now := mustParse("2026-05-29T20:00:00-05:00") now := mustParse("2026-05-29T20:00:00-05:00")
resolved, err := Resolve(DailyTomorrow, ResolveRequest{Now: now, Location: location}) resolved, err := Resolve(Tomorrow, ResolveRequest{Now: now, Location: location})
if err != nil { if err != nil {
t.Fatalf("Resolve() error = %v", err) t.Fatalf("Resolve() error = %v", err)
} }
@@ -207,8 +207,8 @@ func TestEveningBatchIncludesTomorrow(t *testing.T) {
t.Fatalf("BatchReports() error = %v", err) t.Fatalf("BatchReports() error = %v", err)
} }
ids := resolvedIDs(resolved) ids := resolvedIDs(resolved)
if strings.Join(ids, ",") != "daily_tomorrow" { if strings.Join(ids, ",") != "tomorrow" {
t.Fatalf("ids = %v, want daily_tomorrow", ids) t.Fatalf("ids = %v, want tomorrow", ids)
} }
} }
@@ -243,7 +243,7 @@ func TestRegistryLookupErrorIsActionable(t *testing.T) {
func TestRegistryAllIncludesHourlyInStableOrder(t *testing.T) { func TestRegistryAllIncludesHourlyInStableOrder(t *testing.T) {
ids := resolvedDefinitionIDs(DefaultRegistry().All()) ids := resolvedDefinitionIDs(DefaultRegistry().All())
want := []string{"daily_today", "daily_tomorrow", "hourly", "three_day", "weekend", "storm"} want := []string{"daily_today", "tomorrow", "hourly", "three_day", "weekend", "storm"}
if strings.Join(ids, ",") != strings.Join(want, ",") { if strings.Join(ids, ",") != strings.Join(want, ",") {
t.Fatalf("All() ids = %#v, want %#v", ids, want) t.Fatalf("All() ids = %#v, want %#v", ids, want)
} }
@@ -306,15 +306,15 @@ func TestRegistryDefinitionsDeclarePathAndCompatibilityPolicy(t *testing.T) {
artifactGroup: "daily", artifactGroup: "daily",
batchOutputName: "daily.md", batchOutputName: "daily.md",
generated: true, generated: true,
compatiblePriorIDs: []ID{DailyToday, DailyTomorrow}, compatiblePriorIDs: []ID{DailyToday},
comparisonStrategy: CompareSameValidDate, comparisonStrategy: CompareSameValidDate,
}, },
{ {
id: DailyTomorrow, id: Tomorrow,
artifactGroup: "daily", artifactGroup: "tomorrow",
batchOutputName: "tomorrow.md", batchOutputName: "tomorrow.md",
generated: true, generated: true,
compatiblePriorIDs: []ID{DailyToday, DailyTomorrow}, compatiblePriorIDs: []ID{Tomorrow},
comparisonStrategy: CompareSameValidDate, comparisonStrategy: CompareSameValidDate,
}, },
{ {
@@ -406,7 +406,7 @@ func TestRegistryDefinitionsDeclareDefaultModules(t *testing.T) {
}, },
}, },
{ {
id: DailyTomorrow, id: Tomorrow,
want: []module.ID{ want: []module.ID{
module.Metadata, module.Metadata,
module.CurrentConditions, module.CurrentConditions,

View File

@@ -13,7 +13,7 @@ type Registry struct {
func DefaultRegistry() Registry { func DefaultRegistry() Registry {
definitions := []Definition{ definitions := []Definition{
dailyTodayDefinition(), dailyTodayDefinition(),
dailyTomorrowDefinition(), tomorrowDefinition(),
hourlyDefinition(), hourlyDefinition(),
threeDayDefinition(), threeDayDefinition(),
weekendDefinition(), weekendDefinition(),
@@ -74,7 +74,7 @@ func (r Registry) MustLookup(id ID) Definition {
} }
func (r Registry) All() []Definition { func (r Registry) All() []Definition {
ids := []ID{DailyToday, DailyTomorrow, Hourly, ThreeDay, Weekend, Storm} ids := []ID{DailyToday, Tomorrow, Hourly, ThreeDay, Weekend, Storm}
out := make([]Definition, 0, len(ids)) out := make([]Definition, 0, len(ids))
for _, id := range ids { for _, id := range ids {
if definition, ok := r.definitions[id]; ok { if definition, ok := r.definitions[id]; ok {