Document Tomorrow generated-text workflow

This commit is contained in:
2026-06-14 23:53:31 +00:00
parent dc0172ff82
commit 98cab70b53
11 changed files with 221 additions and 56 deletions

View File

@@ -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`.

View File

@@ -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

View File

@@ -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`,

View File

@@ -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

View File

@@ -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:

View File

@@ -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,

View File

@@ -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

View File

@@ -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

View File

@@ -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/
<run_id>.generated_text.run.json
<run_id>.generated_text.json
<run_id>.render_context.json
tomorrow/
YYYY-MM-DD/
<run_id>.modules.json
<run_id>.metadata.json
<run_id>.generated_text.raw.json
<run_id>.generated_text.run.json
<run_id>.generated_text.json
<run_id>.render_context.json
storm/
YYYY-MM-DD/
<run_id>.modules.json
@@ -97,6 +105,9 @@ workspace/
hourly/
YYYY-MM-DD/
<run_id>.data_package.yaml
tomorrow/
YYYY-MM-DD/
<run_id>.data_package.yaml
storm/
YYYY-MM-DD/
<run_id>.data_package.yaml
@@ -113,6 +124,9 @@ workspace/
hourly/
YYYY-MM-DD/
<run_id>.render.json
tomorrow/
YYYY-MM-DD/
<run_id>.render.json
storm/
YYYY-MM-DD/
<run_id>.render.json
@@ -129,6 +143,9 @@ workspace/
hourly/
YYYY-MM-DD/
<run_id>.distributor.json
tomorrow/
YYYY-MM-DD/
<run_id>.distributor.json
storm/
YYYY-MM-DD/
<run_id>.distributor.json
@@ -141,6 +158,8 @@ workspace/
<run_id>.md
hourly/
<run_id>.md
tomorrow/
<run_id>.md
storm/
<run_id>.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

View File

@@ -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

View File

@@ -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.