From 98cab70b53c532b01b3eae8f7443e8e378be14ad Mon Sep 17 00:00:00 2001 From: Eric Rakestraw Date: Sun, 14 Jun 2026 23:53:31 +0000 Subject: [PATCH] Document Tomorrow generated-text workflow --- docs/cli.md | 8 ++--- docs/integrations/scriptorium.md | 9 +++--- docs/internal/briefing.md | 4 +++ docs/internal/generatedtext.md | 30 +++++++++++++++--- docs/internal/module.md | 22 +++++++++++++ docs/internal/prompt-input.md | 9 ++++-- docs/internal/report-registry.md | 17 +++++----- docs/internal/reporttemplate.md | 31 +++++++++++------- docs/operations.md | 54 ++++++++++++++++++++++---------- docs/templates.md | 47 ++++++++++++++++++++++++--- docs/troubleshooting.md | 46 +++++++++++++++++++++++++++ 11 files changed, 221 insertions(+), 56 deletions(-) diff --git a/docs/cli.md b/docs/cli.md index 9e7083b..32fa3ce 100644 --- a/docs/cli.md +++ b/docs/cli.md @@ -39,10 +39,10 @@ Implemented `generate` commands write a JSON module snapshot, YAML data package, preflight artifact, managed Markdown report, and metadata under the configured workspace. `--out` writes an extra Markdown copy for the operator; distributor notification uses the managed report path, not the extra copy. `generate -hourly` covers the next six hours in the effective report timezone, does not -accept date or event window flags, writes managed generated-text artifacts, -validates the structured text, and renders the managed Markdown report from the -embedded hourly template. +tomorrow` and `generate hourly` write managed generated-text artifacts, +validate structured text from Scriptorium, and render the managed Markdown +report from embedded templates. `generate hourly` covers the next six hours in +the effective report timezone and does not accept date or event window flags. `generate storm` requires explicit event-window bounds with `--start` and `--end`. diff --git a/docs/integrations/scriptorium.md b/docs/integrations/scriptorium.md index fbea7f8..12aba57 100644 --- a/docs/integrations/scriptorium.md +++ b/docs/integrations/scriptorium.md @@ -88,10 +88,11 @@ recording, with the output path pointing at the raw generated-text JSON artifact. `weatherreporter` persists render preflight JSON when orchestration reaches the -preflight save point. Markdown report artifacts are written by Scriptorium to -the `--out` path. Generated-text raw JSON artifacts are also written by -Scriptorium to the `--out` path; later weatherreporter workflow steps validate -and render those bytes. +preflight save point. For direct Markdown reports, Scriptorium writes the +managed Markdown artifact to the `--out` path. For generated-text-template +reports, Scriptorium writes raw JSON to the `--out` path; later +weatherreporter workflow steps validate those bytes and render Markdown from an +embedded template. ## Failure Behavior diff --git a/docs/internal/briefing.md b/docs/internal/briefing.md index 337b1ed..3b51d64 100644 --- a/docs/internal/briefing.md +++ b/docs/internal/briefing.md @@ -38,6 +38,10 @@ Outputs: Every registered composition entry has a builder. Unknown or unimplemented module IDs fail validation instead of being skipped. +Tomorrow Report supports the Daily-style civil-day modules plus +`tomorrow_planning` and `hourly_forecast`; those outputs feed the Tomorrow +GeneratedText prompt package and embedded Markdown template. + Hourly Report supports source and valid-period modules that operate over its rolling six-hour period: `metadata`, `current_conditions`, `hourly_forecast`, `precip_timing`, `alert_digest`, `spc_convective_outlooks`, diff --git a/docs/internal/generatedtext.md b/docs/internal/generatedtext.md index 54b7f3c..ea806b6 100644 --- a/docs/internal/generatedtext.md +++ b/docs/internal/generatedtext.md @@ -7,21 +7,23 @@ This document describes structured generated-text handling in `internal/generatedtext` validates structured text returned for generated-text-template reports and builds curated render contexts for -templates. The first implemented contract is the Hourly Report. +templates. The implemented contracts are Tomorrow Report and Hourly Report. ## Inputs And Outputs Inputs: -- raw Hourly Report GeneratedText JSON +- raw GeneratedText JSON for Tomorrow Report or Hourly Report - report metadata from `internal/briefing` - a module snapshot from `internal/module` -- validated hourly generated text +- validated generated text Outputs: +- typed `Tomorrow` generated text - typed `Hourly` generated text -- normalized stable JSON for validated hourly generated text +- normalized stable JSON for validated generated text +- typed `TomorrowRenderContext` values for `internal/reporttemplate` - typed `HourlyRenderContext` values for `internal/reporttemplate` The hourly generated text JSON accepts: @@ -39,6 +41,22 @@ The hourly generated text JSON accepts: `precipitation_timing` and `confidence` are optional and omitted from normalized JSON when blank. +The Tomorrow generated text JSON accepts: + +```json +{ + "summary": "string", + "forecast_discussion": ["string"], + "precipitation_timing": "string", + "confidence": "string" +} +``` + +`summary` is required after trimming whitespace. `forecast_discussion` must +contain at least one nonblank paragraph after trimming blank items. +`precipitation_timing` and `confidence` are optional and omitted from normalized +JSON when blank. + ## Boundaries - This package owns typed generated-text validation and render-context shaping. @@ -54,7 +72,8 @@ JSON when blank. - Malformed generated-text JSON fails with decode context. - Unknown generated-text JSON fields fail during decoding. -- Empty required hourly fields fail after trimming whitespace. +- Empty required fields fail after trimming whitespace. +- Tomorrow forecast discussion fails when no nonblank paragraphs remain. - Missing optional render-context stanzas become nil module pointers. - Invalid render metadata, including missing timezone, missing generated time, or invalid valid period, fails before template rendering. @@ -64,6 +83,7 @@ JSON when blank. Inspect: - `internal/generatedtext/hourly_test.go` +- `internal/generatedtext/tomorrow_test.go` - `internal/generatedtext/render_context_test.go` ## Invariants diff --git a/docs/internal/module.md b/docs/internal/module.md index 0eef985..a1a1ece 100644 --- a/docs/internal/module.md +++ b/docs/internal/module.md @@ -47,6 +47,28 @@ The registry recognizes these IDs: Every registered module has a builder. Report composition entries that refer to unknown or unimplemented module IDs fail validation instead of being skipped. +## Tomorrow Composition + +The default Tomorrow Report module order is: + +1. `metadata` +2. `current_conditions` +3. `narrative_forecast` +4. `derived_daily_summary` +5. `derived_daypart_summaries` +6. `precip_timing` +7. `alert_digest` +8. `spc_convective_outlooks` +9. `area_forecast_discussion` +10. `spc_convective_discussion` +11. `weather_story` +12. `outdoor_windows` +13. `tomorrow_planning` +14. `hourly_forecast` + +The embedded Tomorrow template uses selected deterministic fields from these +module outputs after GeneratedText validation. + ## Hourly Composition The default Hourly Report module order is: diff --git a/docs/internal/prompt-input.md b/docs/internal/prompt-input.md index eec4562..2631576 100644 --- a/docs/internal/prompt-input.md +++ b/docs/internal/prompt-input.md @@ -68,8 +68,9 @@ Prompt-facing module intervals use local `period_begins` and `period_ends` labels; canonical report metadata and source timestamps remain structured timestamps where applicable. -Hourly Report module snapshots use the same package schema and categories when -converted into prompt input. The default hourly module list places +Tomorrow Report and Hourly Report module snapshots use the same package schema +and categories when converted into prompt input. The default hourly module list +places `precip_timing` under `derived_summaries`, alert and SPC outlooks under `applicable_risk_products`, AFD/SPC discussion/weather story under `narrative_products`, and current/hourly data under `raw_data`. It does not @@ -77,6 +78,10 @@ include civil-day summary stanzas. Generated-text and render context artifacts are produced later in app orchestration and are not part of the YAML data package. +The default Tomorrow module list includes civil-day summary stanzas, +`tomorrow_planning`, and `hourly_forecast` in the data package before +structured GeneratedText is requested from Scriptorium. + Current categories are: - `applicable_risk_products`: location-applicable alerts, warnings, outlooks, diff --git a/docs/internal/report-registry.md b/docs/internal/report-registry.md index 1ffd3ca..0905df6 100644 --- a/docs/internal/report-registry.md +++ b/docs/internal/report-registry.md @@ -27,17 +27,17 @@ Each report definition declares: - default ordered module composition Markdown report definitions use the `scriptorium_markdown` generation mode. -Their template and structured-text schema identifiers are empty. Hourly Report -declares `generated_text_template` with template ID `hourly` and schema ID -`hourly`; the app uses those identifiers to validate generated text and render -the embedded hourly template. +Their template and structured-text schema identifiers are empty. Tomorrow +Report and Hourly Report declare `generated_text_template`; the app uses their +template and schema identifiers to validate generated text and render embedded +Markdown templates. ## Reports | 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 | -| Tomorrow Report | `tomorrow` | `weather.daily_report` | `scriptorium_markdown` | `tomorrow` | `tomorrow.md` | Tomorrow Report | +| Tomorrow Report | `tomorrow` | `weather.tomorrow_generated_text` | `generated_text_template` | `tomorrow` | `tomorrow.md` | Tomorrow 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 | | Weekend Outlook | `weekend` | `weather.weekend_outlook` | `scriptorium_markdown` | `weekend` | `weekend.md` | Weekend Outlook | @@ -76,7 +76,8 @@ IDs, then uses the registry for report policy. The app supplies `weather_api.timezone` as a loaded `time.Location`. Batch output path copying uses batch output names from report definitions. Report -module overrides can use the `hourly` key for Hourly Report. +module overrides can use short keys such as `tomorrow` and `hourly`, or +canonical report IDs such as `daily_today`. ## Batch Membership @@ -110,7 +111,9 @@ Inspect: ## Invariants - Report selection goes through the registry. -- Daily Today and Tomorrow Report both use `weather.daily_report`. +- Direct Markdown reports have empty template and generated-text schema IDs. +- Generated-text-template reports declare prompt, template, and schema IDs in + their report definition. - Valid periods are half-open intervals independent of rendered report text. - Artifact grouping, batch output filenames, generated-report eligibility, default module composition, comparison compatibility, and comparison strategy diff --git a/docs/internal/reporttemplate.md b/docs/internal/reporttemplate.md index b4826fc..974753c 100644 --- a/docs/internal/reporttemplate.md +++ b/docs/internal/reporttemplate.md @@ -6,8 +6,8 @@ in `internal/reporttemplate`. ## Purpose `internal/reporttemplate` owns repository-native report templates and companion -GeneratedText JSON schemas. The first implemented template contract is the -Hourly Report. +GeneratedText JSON schemas. The implemented template contracts are Tomorrow +Report and Hourly Report. The package embeds assets from: @@ -27,8 +27,9 @@ Outputs: - GeneratedText schema bytes for prompt/schema configuration - rendered Markdown bytes for app orchestration to persist -The implemented template ID is `hourly`. The implemented schema ID is also -`hourly`, backed by `hourly.generated_text.schema.json`. +The implemented template IDs are `tomorrow` and `hourly`. The implemented +schema IDs are also `tomorrow` and `hourly`, backed by +`tomorrow.generated_text.schema.json` and `hourly.generated_text.schema.json`. ## Boundaries @@ -41,31 +42,37 @@ GeneratedText validation is owned by `internal/generatedtext`. App orchestration decides which template and schema IDs apply to a report through `internal/report` definitions. -## Template Contract +## Template Contracts -Hourly rendering uses a typed render context with: +Tomorrow and Hourly rendering use typed render contexts with: - report metadata labels such as title, location, valid period, and generation time -- validated hourly GeneratedText prose slots +- validated GeneratedText prose slots - deterministic labels derived from module outputs, including current conditions, hourly forecast rows, precipitation timing, alerts, SPC outlooks, forecast discussion, SPC discussion, and weather story +Tomorrow additionally exposes forecast-date labels, ordered daypart forecast +rows, daily/daypart summaries, tomorrow planning facts, and a multi-paragraph +forecast discussion generated-text slot. The ordered daypart slice is built in +Go so templates do not range over maps. + Templates use `text/template` with `missingkey=error`, so missing context fields fail rendering instead of producing incomplete Markdown. ## Schema Contract -The hourly GeneratedText schema describes the structured prose Scriptorium is -expected to write for the prompt. It requires: +The GeneratedText schemas describe the structured prose Scriptorium is expected +to write for each generated-text prompt. Hourly requires: - `summary` - `forecast_discussion` -It allows optional `precipitation_timing` and `confidence`, and rejects -additional properties. Weather truth remains in module outputs; GeneratedText is -limited to prose slots consumed by the template. +Tomorrow requires `summary` and a nonempty `forecast_discussion` array. Both +schemas allow optional `precipitation_timing` and `confidence`, and reject +additional properties. Weather truth remains in module outputs; GeneratedText +is limited to prose slots consumed by the template. ## Failure Behavior diff --git a/docs/operations.md b/docs/operations.md index 8adbfe9..d1c106d 100644 --- a/docs/operations.md +++ b/docs/operations.md @@ -17,23 +17,23 @@ weatherreporter generate weekend weatherreporter generate storm --start 2026-05-29T18:00 --end 2026-05-30T06:00 ``` -Markdown-path generation commands resolve a report period, fetch a Weather API -bundle, build a JSON module snapshot, build a YAML prompt input data package, -run `scriptorium render`, run `scriptorium run`, and write managed artifacts -under the configured workspace. When distributor notification is enabled, -weatherreporter uploads the managed Markdown report after report rendering -succeeds and final metadata is saved. `--out PATH` writes an extra Markdown -copy for generated reports; it is not used as the distributor upload source. +Generation commands resolve a report period, fetch a Weather API bundle, build +a JSON module snapshot, build a YAML prompt input data package, run +`scriptorium render`, and write managed artifacts under the configured +workspace. Markdown-path reports then run `scriptorium run` directly to the +managed Markdown report path. -`generate hourly` covers the six-hour rolling period from generation time in -the effective report timezone and is not part of scheduled morning or evening -batches. It builds the same module snapshot and data package, runs -`scriptorium render` as preflight, runs structured `scriptorium run` to raw -GeneratedText JSON, validates the structured text, saves a render context, and -renders the managed Markdown report from the embedded hourly template. When -distributor notification is enabled, hourly uploads the managed Markdown report -after final metadata is saved. `--out PATH` writes an extra Markdown copy and -is not used as the distributor upload source. +`generate tomorrow` and `generate hourly` use the generated-text-template +workflow. They run structured `scriptorium run` to raw GeneratedText JSON, +validate the structured text, save a render context, and render the managed +Markdown report from embedded templates. `generate hourly` covers the six-hour +rolling period from generation time in the effective report timezone and is not +part of scheduled morning or evening batches. + +When distributor notification is enabled, weatherreporter uploads the managed +Markdown report after report rendering succeeds and final metadata is saved. +`--out PATH` writes an extra Markdown copy for generated reports; it is not used +as the distributor upload source. Batch commands: @@ -80,6 +80,14 @@ workspace/ .generated_text.run.json .generated_text.json .render_context.json + tomorrow/ + YYYY-MM-DD/ + .modules.json + .metadata.json + .generated_text.raw.json + .generated_text.run.json + .generated_text.json + .render_context.json storm/ YYYY-MM-DD/ .modules.json @@ -97,6 +105,9 @@ workspace/ hourly/ YYYY-MM-DD/ .data_package.yaml + tomorrow/ + YYYY-MM-DD/ + .data_package.yaml storm/ YYYY-MM-DD/ .data_package.yaml @@ -113,6 +124,9 @@ workspace/ hourly/ YYYY-MM-DD/ .render.json + tomorrow/ + YYYY-MM-DD/ + .render.json storm/ YYYY-MM-DD/ .render.json @@ -129,6 +143,9 @@ workspace/ hourly/ YYYY-MM-DD/ .distributor.json + tomorrow/ + YYYY-MM-DD/ + .distributor.json storm/ YYYY-MM-DD/ .distributor.json @@ -141,6 +158,8 @@ workspace/ .md hourly/ .md + tomorrow/ + .md storm/ .md ``` @@ -165,7 +184,8 @@ Each generated report writes metadata that links: - prompt input data package path - preflight output path - managed Markdown report path -- generated text schema ID and generated-text artifact paths for Hourly Report +- generated text schema ID and generated-text artifact paths for + generated-text-template reports - distributor notification debug artifact path, when notification is attempted Batch summaries include report status, error text when applicable, notification diff --git a/docs/templates.md b/docs/templates.md index 37f990d..2c65c12 100644 --- a/docs/templates.md +++ b/docs/templates.md @@ -4,15 +4,17 @@ This guide describes the implemented Markdown report template surface for `weatherreporter`. It is for maintainers editing embedded report templates, -especially the hourly report template. +especially generated-text-template reports. -Templates are Go `text/template` files. The current implemented template is: +Templates are Go `text/template` files. The current implemented templates are: +- `internal/reporttemplate/templates/tomorrow.md.tmpl` - `internal/reporttemplate/templates/hourly.md.tmpl` -The hourly template is rendered from a structured `HourlyRenderContext`. Weather -data collection, derivation, module execution, generated text validation, and -artifact paths are handled before template rendering. +Templates are rendered from structured contexts such as `TomorrowRenderContext` +and `HourlyRenderContext`. Weather data collection, derivation, module +execution, generated text validation, and artifact paths are handled before +template rendering. ## Editing Rules @@ -77,6 +79,41 @@ Example: {{ .GeneratedText.ForecastDiscussion }} ``` +## Tomorrow Context + +The Tomorrow template receives five top-level values: + +| Variable | Type | Description | +| --- | --- | --- | +| `.Report` | TomorrowReportContext | Display metadata and friendly labels for the rendered report. | +| `.GeneratedText` | Tomorrow | Structured text returned by Scriptorium. | +| `.Modules` | TomorrowTemplateModules | Preferred deterministic template surface, keyed by module purpose. | +| `.Collected` | facts.CollectedFacts | Normalized upstream facts for advanced template use. | +| `.Derived` | facts.DerivedFacts | Shared derived facts for advanced template use. | + +Tomorrow report metadata includes `.Report.Title`, `.Report.ForecastDate`, +`.Report.ForecastDateLabel`, `.Report.ForecastDayName`, +`.Report.GeneratedAt`, `.Report.GeneratedAtLabel`, `.Report.ValidPeriod`, and +`.Report.Timezone`. + +Tomorrow generated text uses the same `.GeneratedText.Summary`, +`.GeneratedText.PrecipitationTiming`, and `.GeneratedText.Confidence` fields as +Hourly. `.GeneratedText.ForecastDiscussion` is a slice of paragraphs and should +be rendered with `range`. + +Tomorrow modules include the Hourly module fields plus: + +| Variable | Type | Description | +| --- | --- | --- | +| `.Modules.DerivedDailySummary` | *briefing.DerivedDailySummaryModule | Daily summary facts for the forecast date. | +| `.Modules.DerivedDaypartSummaries` | *map[string]briefing.DerivedDaypartSummaryModule | Raw daypart summary map, when direct keyed access is needed. | +| `.Modules.Dayparts` | []generatedtext.TomorrowDaypartContext | Ordered daypart summaries for deterministic template rendering. | +| `.Modules.TomorrowPlanning` | *briefing.TomorrowPlanningModule | Planning facts for the next local civil day. | + +Prefer `.Modules.Dayparts` over ranging through +`.Modules.DerivedDaypartSummaries`; it follows configured daypart order and +falls back to sorted keys for any unmatched entries. + ## Modules `.Modules` exposes typed outputs from the same module pipeline used for the diff --git a/docs/troubleshooting.md b/docs/troubleshooting.md index edc4e69..47e2776 100644 --- a/docs/troubleshooting.md +++ b/docs/troubleshooting.md @@ -185,6 +185,52 @@ prompt, profile, model configuration, or validation issue. Relevant docs: [Operations guide](operations.md), [Scriptorium integration](integrations/scriptorium.md). +## Generated Text Validation Fails + +Symptom: Tomorrow or Hourly generation fails with generated-text decode, +unknown-field, required-field, or multiple-JSON-values context. + +Likely cause: Scriptorium wrote structured JSON that does not match the +GeneratedText contract for the selected report. + +Diagnostic: + +```sh +weatherreporter inspect metadata RUN_ID +``` + +Then inspect the generated-text raw path recorded in metadata, if present. + +Safe fix: update the Scriptorium prompt or schema configuration so the prompt +writes the expected structured JSON for the report. + +Relevant docs: [Operations guide](operations.md), +[Generated Text internals](internal/generatedtext.md), +[Scriptorium integration](integrations/scriptorium.md). + +## Template Rendering Fails + +Symptom: Tomorrow or Hourly generation fails with report template parsing or +execution context after generated text validation succeeds. + +Likely cause: an embedded template references a missing context field or +receives a value shape that does not match its typed render context. + +Diagnostic: + +```sh +weatherreporter inspect metadata RUN_ID +``` + +If metadata records generated-text and render-context paths, inspect those +artifacts along with the template named by the report definition. + +Safe fix: update the embedded template or render-context builder so the +template uses the implemented typed context. + +Relevant docs: [Report Templates](templates.md), +[Report Template internals](internal/reporttemplate.md). + ## Batch Command Returns Nonzero Symptom: `run morning` or `run evening` returns nonzero.