From b3f7c9c1f2ce6792bdef97235f1d4f40b51d0ff3 Mon Sep 17 00:00:00 2001 From: Eric Rakestraw Date: Sat, 13 Jun 2026 23:20:04 -0500 Subject: [PATCH] Add roadmap and implementation plan to move towards hybrid deterministic/llm generation of reports --- docs/roadmap/generated-text.md | 343 +++++++++++ docs/roadmap/implementation.md | 1028 ++++++++++++++++++++------------ 2 files changed, 997 insertions(+), 374 deletions(-) create mode 100644 docs/roadmap/generated-text.md diff --git a/docs/roadmap/generated-text.md b/docs/roadmap/generated-text.md new file mode 100644 index 0000000..826b52b --- /dev/null +++ b/docs/roadmap/generated-text.md @@ -0,0 +1,343 @@ +# GeneratedText Report Rendering Roadmap + +## Purpose + +This roadmap defines planned work to move selected reports from full Markdown +LLM generation to deterministic Markdown rendering with structured LLM-filled +text slots. The first target report is the rolling next-hours report, now named +`hourly` rather than `near_term`. + +This feature is not implemented yet, so this document lives under +`docs/roadmap/`. + +## Intent + +The current module and prompt-input system has reached the point where many +report facts can be calculated deterministically. The next step is to let Go +own report structure while using the LLM for only the prose that benefits from +language synthesis. + +The intended workflow is: + +1. collect upstream facts once; +2. derive deterministic report facts; +3. build module outputs and the prompt data package; +4. ask Scriptorium for structured JSON using the schema associated with the + prompt ID on the Scriptorium backend; +5. validate and persist that structured response as `GeneratedText`; +6. build a curated render context; +7. render final Markdown from an embedded human-editable template. + +This should improve consistency for headings, dates, ordering, units, recurring +sections, and report-specific format while preserving the LLM's role in +summarizing and explaining weather impacts. + +## Terminology + +- `CollectedFacts`: normalized upstream inputs from Weather API sources. +- `DerivedFacts`: deterministic Go-calculated facts sliced to a report period. +- `ModuleOutput`: prompt-facing deterministic module stanzas. +- `GeneratedText`: validated structured LLM output. It is not authoritative + weather data; it is prose or short text intended to fill named template + slots. +- `RenderContext`: the final deterministic object passed to a Markdown + template. It may expose selected values from CollectedFacts, DerivedFacts, + ModuleOutput, report metadata, and GeneratedText. + +The report template should not receive the unbounded raw data package directly. +It should receive a curated render context so templates remain readable and do +not become a second application logic layer. + +## Locked Decisions + +- Use `GeneratedText` as the name for the structured LLM output layer. +- Continue using Scriptorium behind `internal/adapters/scriptorium`. +- Use Scriptorium structured JSON output. The Scriptorium backend associates + the prompt ID with the JSON schema; weatherreporter does not pass a schema + file at runtime. +- Keep Scriptorium retries inside Scriptorium. +- Also validate the returned JSON inside `weatherreporter` before rendering. +- Use embedded, separate, human-editable Markdown template files. +- Use embedded, separate, human-readable JSON schema files. +- Use Go standard library `text/template` for Markdown rendering unless a + concrete limitation appears. +- Let Scriptorium enforce the prompt-associated JSON Schema and perform + retries, then have weatherreporter validate by unmarshalling into a typed + GeneratedText struct and checking required fields. Do not add a Go JSON + Schema dependency in the first implementation. +- Do not generate schemas or templates from inline Go. +- Start with the rolling next-hours report only. +- Rename the planned `near_term` report to `hourly`. +- Do not keep both `near_term` and `hourly` public report IDs. +- Do not add a generic workflow engine or plugin system. + +## Hourly Report Target + +The planned `near_term` report should become the `hourly` report. + +Target report identity: + +- report ID: `hourly` +- CLI command: `weatherreporter generate hourly` +- display name: `Hourly Report` +- prompt ID for structured text generation: `weather.hourly_generated_text` +- Markdown template ID: `hourly` +- GeneratedText schema ID: `hourly` +- artifact group: `hourly` +- batch output name: `hourly.md` +- default valid-period length: 6 hours +- valid period: `[generation_time, generation_time + hourlyReportHours)` + +Use a package-owned constant for the valid-period length, initially 6 hours. +The duration should not be user-configurable in the first implementation. + +The report remains explicit-generation only at first. Do not add it to morning +or evening batches until cadence and downstream behavior are proven. + +## Embedded Asset Layout + +Recommended asset package: + +```text +internal/reporttemplate/ + templates/ + hourly.md.tmpl + schemas/ + hourly.generated_text.schema.json +``` + +`internal/reporttemplate` should embed assets with `go:embed` and expose a +narrow API for: + +- looking up a template by template ID; +- looking up a schema by schema ID; +- rendering Markdown from a typed render context. + +Do not allow template or schema asset IDs to come from free-form user input in +the initial implementation. Report definitions should declare the IDs. + +## Report Definition Fields + +Extend report definitions only as needed for this generation mode. Required +fields: + +- `GenerationMode` +- `TemplateID` +- `GeneratedTextSchemaID` + +Recommended generation modes: + +- `GenerationModeScriptoriumMarkdown`: existing full Markdown generation path. +- `GenerationModeGeneratedTextTemplate`: structured GeneratedText plus + deterministic template rendering. + +Existing reports should stay on the current full Markdown path until migrated. +The `hourly` report should use the GeneratedText/template path from the start. +For generated-text reports, the existing `PromptID` field remains the +Scriptorium prompt ID and should be set to `weather.hourly_generated_text`. + +## GeneratedText Contract + +The hourly JSON schema should define only the text slots the template needs. +Keep the first schema small. Example shape: + +```json +{ + "summary": "Brief near-term overview.", + "timing": "Plain-language timing of notable changes.", + "impacts": "Practical impacts for the next few hours.", + "confidence": "Optional confidence or uncertainty note." +} +``` + +The exact schema should be locked during implementation based on the first +hourly template. Required fields should be genuinely required by the template. +Optional fields should be omitted from the rendered Markdown when empty. + +GeneratedText should be persisted as structured JSON exactly as validated by +weatherreporter. Raw Scriptorium output should also be persisted for debugging +when practical. + +## Template Contract + +The hourly Markdown template should be a readable file that a human can edit +without digging into Go code. It should own: + +- Markdown heading structure; +- section order; +- deterministic fact placement; +- conditional inclusion of optional GeneratedText slots; +- final report layout. + +The template should not own: + +- Weather API fetching; +- fact derivation; +- module composition; +- Scriptorium invocation; +- schema validation; +- path construction; +- distributor upload behavior. + +The render context should prepare strings and simple values so template logic +stays shallow. Prefer small conditionals over complex template functions. + +## Scriptorium Adapter Direction + +`internal/adapters/scriptorium` should expose weatherreporter-owned request and +result types for structured JSON generation. The adapter should hide +Scriptorium argv details from app, report, facts, module, prompt-input, and +template packages. + +Scriptorium uses the prompt ID to select the backend-associated structured +output schema. The adapter should not pass a schema path at runtime. + +## State And Artifacts + +Add managed artifacts for the new generation path. Recommended logical +artifacts: + +- module snapshot JSON; +- data package YAML; +- raw GeneratedText JSON written by Scriptorium; +- structured Scriptorium command result for the GeneratedText run; +- validated GeneratedText JSON; +- render context JSON; +- rendered Markdown report; +- metadata linking all of the above. + +The existing `preflight` artifact should remain as the rendered prompt/input +inspection artifact before structured generation. GeneratedText reports add +separate artifacts for raw GeneratedText, structured run result, validated +GeneratedText, and render context. Metadata should record the +`GeneratedTextSchemaID`; no per-run schema artifact is persisted. + +Artifact paths should remain under the workspace and use the report artifact +group, valid start date, and RunID conventions already used by state. + +## End-To-End Behavior + +Reports using `GenerationModeGeneratedTextTemplate` should follow the same +fact collection, derivation, module snapshot, prompt package, state, and +notification boundaries as current reports. The difference is limited to the +last rendering segment: Scriptorium returns structured GeneratedText, Go +validates that structure, Go builds a curated render context, and an embedded +template renders the final Markdown. + +The existing full-Markdown Scriptorium path should remain available for +reports that have not migrated. + +## Target Architecture + +GeneratedText rendering adds one new generation mode, not a replacement for the +whole report system. Reports that opt into this mode should continue to use +the existing report registry, fact pipeline, module snapshot, data package, +state store, and notification boundaries. + +The target flow is: + +1. `internal/report` declares whether a report uses the existing full-Markdown + Scriptorium path or the GeneratedText/template path. +2. The app builds CollectedFacts, DerivedFacts, module snapshots, and the + prompt data package using the same boundaries as existing reports. +3. `internal/adapters/scriptorium` asks Scriptorium for structured JSON using + the report's prompt ID. The Scriptorium backend owns the prompt-to-schema + association. +4. Weatherreporter validates the returned JSON as typed `GeneratedText`. +5. A report-specific render context is built from selected deterministic facts, + module outputs, metadata, and GeneratedText. +6. `internal/reporttemplate` renders the final Markdown from an embedded, + human-editable template. +7. The managed Markdown report remains the canonical output and the distributor + upload source. + +This structure keeps each layer understandable: + +- CollectedFacts and DerivedFacts remain weather-data contracts. +- ModuleOutput remains the prompt-facing deterministic facts contract. +- GeneratedText remains LLM-written prose slots, not weather truth. +- RenderContext remains a curated template input, not a second raw data package. +- Templates own presentation order and headings, not weather derivation logic. + +## Editing Surface + +The desired human editing surface is file-based and report-specific. For the +hourly report, the important files should be easy to find and inspect: + +- one Markdown template file for the report layout; +- one JSON schema file describing the expected GeneratedText payload; +- one report definition declaring the prompt ID, template ID, schema ID, module + composition, valid period, artifact group, and output naming policy. + +The template and schema should be embedded into the binary, but they should +remain ordinary files in the repository. A maintainer should be able to tweak +headings, reorder sections, or inspect the structured prose contract without +reading unrelated orchestration code. + +## Boundary Guidelines + +- Keep report identity, prompt IDs, generation mode, template IDs, schema IDs, + valid periods, module composition, and output naming in `internal/report`. +- Keep Scriptorium subprocess details in `internal/adapters/scriptorium`. +- Keep Markdown template lookup and rendering in `internal/reporttemplate`. +- Keep GeneratedText validation in a narrow package or component with typed + report-specific structs. +- Keep app orchestration responsible for sequencing and artifact persistence, + not for report-specific prose or template structure. +- Keep distributor upload behavior unchanged: upload the managed Markdown + report after it exists. +- Do not let templates fetch data, derive facts, perform source selection, or + construct workspace paths. +- Do not let Scriptorium-specific flags, schema paths, or subprocess details + leak into report definitions or domain packages. +- Do not add user-provided template or schema paths in the first + implementation. + +## Target Acceptance Criteria + +The feature is successful when: + +- `weatherreporter generate hourly` produces a deterministic Markdown report + whose headings, dates, and section order come from an embedded template; +- Scriptorium fills only the report's structured GeneratedText slots; +- weatherreporter validates GeneratedText before rendering; +- the raw GeneratedText, validated GeneratedText, render context, rendered + Markdown, and metadata are inspectable after a run; +- existing daily, tomorrow, three-day, weekend, and storm reports keep their + current full-Markdown Scriptorium path until explicitly migrated; +- failures in structured generation, validation, or template rendering are + diagnosable from saved artifacts where practical; +- the managed Markdown report remains the source for optional copies and + distributor uploads; +- the implementation does not introduce a generic workflow engine, plugin + system, or broad report rewrite. + +## Deferred Work + +Do not include these in the first implementation: + +- migrating daily, tomorrow, three-day, weekend, or storm to GeneratedText; +- user-configurable template or local schema source overrides; +- user-defined templates or schemas; +- a generic workflow engine; +- template hot reloading; +- dynamic module selection for templates; +- distributor-specific generated-text behavior; +- prompt/schema generation from Go structs; +- scheduled hourly batch membership; +- configurable hourly report duration. + +## Open Questions + +No questions block roadmap creation. + +Recommended approach: keep JSON schema enforcement in Scriptorium for retry +behavior, and also perform weatherreporter-side typed validation before +rendering. Weatherreporter should validate the returned JSON by unmarshalling +into the report's GeneratedText type and checking required fields; it should +not add a Go JSON Schema dependency in the first implementation. + +Viable alternative: trust Scriptorium validation and only unmarshal into typed +Go structs without explicit required-field checks. This is simpler, but it +weakens weatherreporter's rendering contract and may make empty or malformed +LLM output harder to diagnose. diff --git a/docs/roadmap/implementation.md b/docs/roadmap/implementation.md index 51c7420..625ff56 100644 --- a/docs/roadmap/implementation.md +++ b/docs/roadmap/implementation.md @@ -1,40 +1,189 @@ -# Near-Term Report Implementation Roadmap +# GeneratedText Hourly Report Implementation Roadmap ## Purpose This roadmap defines the concrete implementation sequence for -`docs/roadmap/near-term.md`. It is written for an LLM coding agent that will -implement each stage in order. +`docs/roadmap/generated-text.md`. It is written for an LLM coding agent that +will implement each stage in order. This is a future-work roadmap. Until a stage is implemented, non-roadmap docs -must not describe `near_term` behavior as available. +must not describe `GeneratedText`, embedded report templates, or +`weatherreporter generate hourly` as available behavior. ## Source Feature Roadmap -Use `docs/roadmap/near-term.md` as the authoritative feature roadmap for user -intent, target behavior, and policy choices. This document is the implementation -plan. If the target behavior changes, update `near-term.md` first, then update -this roadmap. +Use `docs/roadmap/generated-text.md` as the authoritative feature roadmap for +intent, terminology, target behavior, and policy choices. This document is the +step-by-step implementation plan. If target behavior changes, update +`generated-text.md` first, then update this plan. + +Future implementation agents should read `generated-text.md` before starting +Stage 1. Treat that file as the conceptual contract for why the feature exists +and what final shape it should have; treat this file as the ordered work plan +for reaching that shape. ## Locked Implementation Decisions -- Add report ID `near_term` and Go constant `report.NearTerm`. -- Add CLI command `weatherreporter generate near-term`. -- Do not add `--date`, `--start`, `--end`, or duration flags for this report. -- Do not add `near_term` to morning or evening batches in the first +- Implement the first GeneratedText path for the rolling next-hours report, + named `hourly`. +- Replace planned/public `near_term` naming with `hourly`; do not keep a + public `near-term` alias. +- Use report ID `hourly`, artifact group `hourly`, batch output name + `hourly.md`, and CLI command `weatherreporter generate hourly`. +- Keep the rolling report explicit-generation only; do not add scheduled batch + membership in this implementation. +- Keep the valid-period length as a package-owned constant, initially 6 hours. +- Add report generation modes: + - `scriptorium_markdown` for existing reports; + - `generated_text_template` for `hourly`. +- Keep existing reports on the current Scriptorium Markdown path. +- For generated-text reports, keep `Definition.PromptID` as the Scriptorium + prompt ID and set the hourly value to `weather.hourly_generated_text`. +- Add `TemplateID` and `GeneratedTextSchemaID` to report definitions. +- Use embedded assets under `internal/reporttemplate`. +- Use standard-library `text/template`. +- Let Scriptorium enforce its prompt-associated JSON Schema and retries; + weatherreporter validates by unmarshalling into a typed GeneratedText struct + and checking required fields. Do not add a Go JSON Schema dependency in this implementation. -- Define the valid-period duration with a package-owned constant, initially - `6` hours. -- Resolve the valid period as `[generation_time, generation_time + 6h)` in the - effective report timezone. -- Use prompt ID `weather.near_term_report`. -- Use artifact group `near-term` and batch output name `near-term.md`. -- Add comparison strategy constant `CompareRollingWindow = "rolling_window"`. -- Declare `CompatiblePriorIDs: []report.ID{report.NearTerm}`, but do not emit - Recent Changes for `near_term` in the first implementation. -- Keep state prior lookup returning `nil` for rolling-window reports until a - future comparison algorithm is designed. -- Default module order is: +- Keep the existing preflight artifact for prompt/input inspection. +- Add managed artifacts for raw GeneratedText JSON, structured Scriptorium run + result, validated GeneratedText JSON, and render context JSON. +- Distributor notification continues to upload the managed Markdown report only. +- Preserve current CLI behavior, artifact paths, and generated output behavior + for daily, tomorrow, three-day, weekend, and storm reports. + +## Stage 1: Roadmap Reconciliation And Hourly Identity + +Goal: remove conflicting future instructions and establish `hourly` as the only +planned public name for the rolling next-hours report. + +Files to inspect: + +- `docs/roadmap/generated-text.md` +- `docs/roadmap/near-term.md` +- `docs/roadmap/future.md` +- `docs/roadmap/implementation.md` +- `internal/app/app.go` +- `internal/report/definition.go` +- `internal/report/registry.go` + +Implementation: + +- Replace `docs/roadmap/near-term.md` with a short superseded note pointing to + `docs/roadmap/generated-text.md`, or delete it if the repository policy at + implementation time prefers removing obsolete roadmap files. +- Ensure no roadmap instructs a future agent to implement public + `near_term` / `near-term` behavior. +- If partial `near_term` code already exists, rename it to `hourly` rather + than adding aliases: + - `ReportNearTerm` -> `ReportHourly`; + - `report.NearTerm` -> `report.Hourly`; + - CLI command `near-term` -> `hourly`; + - artifact group `near-term` -> `hourly`; + - prompt IDs and tests updated to hourly names. +- Do not add compatibility aliases for `near-term`. + +Acceptance criteria: + +- Active roadmap docs use `hourly` for the rolling report. +- No active roadmap describes `near_term` as the target public report ID. +- Existing code compiles after any rename work. + +Suggested validation: + +```bash +rg -n "near_term|near-term|NearTerm" docs internal +go test ./internal/app ./internal/report ./internal/cli +``` + +This stage is small enough for one implementation prompt. + +## Stage 2: Report Definition Generation Mode + +Goal: add generation-mode metadata to report definitions without changing the +current report generation path. + +Files to inspect: + +- `internal/report/definition.go` +- `internal/report/registry.go` +- `internal/report/*_report.go` +- `internal/report/period_test.go` +- `internal/state/filesystem.go` + +Implementation: + +- Add a `GenerationMode` type in `internal/report`. +- Add constants: + - `GenerationModeScriptoriumMarkdown` + - `GenerationModeGeneratedTextTemplate` +- Add fields to `report.Definition`: + - `GenerationMode GenerationMode` + - `TemplateID string` + - `GeneratedTextSchemaID string` +- Default or explicitly set existing reports to + `GenerationModeScriptoriumMarkdown`. +- Update report definition tests so every generated report declares a + generation mode. +- Do not wire app behavior in this stage. +- Keep existing `PromptID` semantics: it is the Scriptorium prompt ID for the + report's generation mode. + +Acceptance criteria: + +- Existing report definitions retain current prompt IDs and behavior. +- Registry tests fail if a generated report omits generation mode. +- Generated-text-only fields are empty for existing Markdown reports. + +Suggested validation: + +```bash +go test ./internal/report ./internal/state +``` + +This stage is small enough for one implementation prompt. + +## Stage 3: Hourly Report Definition, Modules, Facts, And CLI Shell + +Goal: add the `hourly` report identity, rolling valid period, module +composition, facts derivation, config override aliases, and CLI command shell. + +Files to inspect: + +- `internal/report/definition.go` +- `internal/report/registry.go` +- new `internal/report/hourly_report.go` +- `internal/report/period_test.go` +- `internal/briefing/modules.go` +- `internal/facts/facts.go` +- `internal/facts/facts_test.go` +- `internal/config/reports.go` +- `internal/config/config_test.go` +- `internal/app/app.go` +- `internal/cli/root.go` +- `internal/cli/root_test.go` + +Implementation: + +- Add report ID `Hourly ID = "hourly"`. +- Add comparison strategy `CompareRollingWindow = "rolling_window"`. +- Add `hourlyReportHours` constant, initially `6`. +- Add `hourlyDefinition()`: + - `Name: "Hourly Report"` + - `PromptID: "weather.hourly_generated_text"` + - `GenerationMode: GenerationModeGeneratedTextTemplate` + - `TemplateID: "hourly"` + - `GeneratedTextSchemaID: "hourly"` + - `ComparisonStrategy: CompareRollingWindow` + - `ArtifactGroup: "hourly"` + - `BatchOutputName: "hourly.md"` + - `Generated: true` + - `CompatiblePriorIDs: []ID{Hourly}` + - no morning/evening batch membership +- Resolve valid period as `[generation_time, generation_time + 6h)` in the + effective timezone. +- Add default hourly modules in order: 1. `metadata` 2. `current_conditions` 3. `hourly_forecast` @@ -44,411 +193,546 @@ this roadmap. 7. `area_forecast_discussion` 8. `spc_convective_discussion` 9. `weather_story` -- Configure the near-term `area_forecast_discussion` module with only - `key_messages` and `short_term` sections. -- Do not include daily/daypart modules in the default near-term composition. -- Alert and SPC modules must use existing valid-period overlap behavior. -- Preserve existing public behavior for all current report types. +- Configure hourly `area_forecast_discussion` options to include only: + - `key_messages` + - `short_term` +- Update module supported-report lists: + - include `report.Hourly` in all broadly compatible modules; + - include `report.Hourly` for `hourly_forecast`; + - keep daily/daypart modules incompatible. +- Add `facts.BuildDerived` support for hourly: + - valid-period hourly slice; + - precipitation timing from that slice; + - alert overlap filtering; + - SPC outlook/discussion overlap filtering; + - no daily summaries, daypart summaries, or storm summary. +- Add config report aliases: + - `hourly` + - reject or do not recognize `near_term` / `near-term`. +- Add app report kind `ReportHourly = "hourly"` and map it to + `report.Hourly`. +- Add CLI parsing and help for: -## Stage 1: Report Identity And Period Resolution + ```bash + weatherreporter generate hourly [--config PATH] [--units VALUE] [--tz NAME] [--out PATH] + ``` -Goal: add the `near_term` report definition, constants, registry entry, and -rolling six-hour valid-period resolver. +- Do not add `--date`, `--start`, `--end`, or duration flags. +- It is acceptable for `generate hourly` to return an explicit + "generation mode not implemented" error until the app workflow stage, but + parser and resolution tests should pass. + +Acceptance criteria: + +- `report.DefaultRegistry().Lookup(report.Hourly)` succeeds. +- hourly valid period is exactly six hours and not civil-day truncated. +- hourly default module composition validates. +- daily-only modules reject hourly. +- facts derivation supports the hourly module list. +- CLI help includes `generate hourly`. +- Existing command behavior is unchanged. + +Suggested validation: + +```bash +go test ./internal/report ./internal/briefing ./internal/facts +go test ./internal/config ./internal/app ./internal/cli +go run ./cmd/weatherreporter --help +``` + +This stage is large but still suitable for one implementation prompt if kept +strictly to report identity/facts/CLI shell. If it becomes unwieldy, split it +after report/facts and implement CLI/config aliases in a follow-up prompt. + +## Stage 4: Embedded Template And Schema Assets + +Goal: create the embedded asset package and first hourly template/schema. + +Files to create: + +- `internal/reporttemplate/reporttemplate.go` +- `internal/reporttemplate/templates/hourly.md.tmpl` +- `internal/reporttemplate/schemas/hourly.generated_text.schema.json` +- `internal/reporttemplate/reporttemplate_test.go` + +Implementation: + +- Use `go:embed` for templates and schemas. +- Expose a narrow API: + - `Template(id string) (string, error)` or equivalent; + - `Schema(id string) ([]byte, error)` or equivalent; + - `Render(id string, data any) ([]byte, error)`. +- Use `text/template`. +- Register only `hourly` assets initially. +- Missing or unknown asset IDs must return actionable errors. +- Keep template functions minimal. Prefer prepared render-context strings over + complex template logic. +- Add an initial hourly JSON Schema with: + - required `summary`; + - required `timing`; + - required `impacts`; + - optional `confidence`; + - all fields strings; + - `additionalProperties: false`. +- Add an initial hourly Markdown template that is readable and deterministic. + It should include fixed headings, report valid period, selected deterministic + facts, and GeneratedText slots. + +Acceptance criteria: + +- schema asset can be looked up for tests, local validation, and out-of-band + Scriptorium registration; +- template asset can be looked up; +- template renders from a test render context; +- missing template/schema IDs fail clearly; +- no generated schema or template content is inline in Go. + +Suggested validation: + +```bash +go test ./internal/reporttemplate +``` + +This stage is small enough for one implementation prompt. + +## Stage 5: GeneratedText Contract And Render Context + +Goal: add typed GeneratedText validation and a typed hourly render context. + +Files to create or inspect: + +- new `internal/generatedtext` package, or another narrow package if the + implementation chooses a better local name +- `internal/reporttemplate` +- `internal/module` +- `internal/briefing` +- `internal/promptinput` + +Implementation: + +- Add typed hourly GeneratedText: + + ```go + type Hourly struct { + Summary string `json:"summary"` + Timing string `json:"timing"` + Impacts string `json:"impacts"` + Confidence string `json:"confidence,omitempty"` + } + ``` + +- Add validation: + - malformed JSON fails; + - empty required fields fail after trimming whitespace; + - unknown fields fail by using `json.Decoder.DisallowUnknownFields`; + - validated output is normalized to stable JSON. +- Do not add a Go JSON Schema dependency in this implementation. +- Add typed hourly render context. +- Build render context from: + - report metadata and location; + - selected module outputs from the module snapshot; + - validated hourly GeneratedText. +- Use `module.StanzaValue` or equivalent typed decoding rather than ad hoc map + traversal where practical. +- Keep render context curated; do not pass the full raw data package to the + template. + +Acceptance criteria: + +- valid GeneratedText passes and round-trips to stable JSON; +- missing required fields fail; +- unknown fields fail; +- render context has enough deterministic fields for the hourly template; +- render context construction fails clearly when required module stanzas are + missing. + +Suggested validation: + +```bash +go test ./internal/generatedtext ./internal/reporttemplate ./internal/module +``` + +This stage is small enough for one implementation prompt. + +## Stage 6: State Artifacts For GeneratedText Reports + +Goal: add explicit managed paths and save/load helpers for generated-text +artifacts. Files to inspect: -- `internal/report/definition.go` -- `internal/report/registry.go` -- `internal/report/period.go` -- `internal/report/daily_report.go` -- `internal/report/period_test.go` - `internal/state/filesystem.go` +- `internal/state/store.go` +- `internal/state/metadata.go` +- `internal/state/filesystem_test.go` +- `internal/fileutil` Implementation: -- Add `NearTerm ID = "near_term"` in `internal/report/definition.go`. -- Add `CompareRollingWindow ComparisonStrategy = "rolling_window"`. -- Add `internal/report/near_term_report.go`. -- Define a package-local duration constant in that file, for example: +- Extend `state.ArtifactPaths` with: + - `GeneratedTextRaw` + - `GeneratedTextResult` + - `GeneratedText` + - `RenderContext` +- Store all four under the existing snapshots tree: + - `snapshots///.generated_text.raw.json` + - `snapshots///.generated_text.run.json` + - `snapshots///.generated_text.json` + - `snapshots///.render_context.json` +- Do not add new workspace config directories. +- Extend `state.Metadata` with optional path fields for those artifacts. + Also record `GeneratedTextSchemaID` as metadata for auditability without + copying schema contents into every run. +- Add store methods as needed, for example: + - `SaveGeneratedTextRaw` + - `SaveGeneratedTextResult` + - `SaveGeneratedText` + - `SaveRenderContext` +- Use atomic writes through existing `fileutil` helpers. +- Keep existing metadata JSON shape for Markdown-path reports, with new fields + omitted when empty. - ```go - const nearTermHours = 6 - ``` +Acceptance criteria: -- Add `nearTermDefinition()` returning: - - `ID: NearTerm` - - `Name: "Near-Term Report"` - - `PromptID: "weather.near_term_report"` - - `ComparisonStrategy: CompareRollingWindow` - - `ArtifactGroup: "near-term"` - - `BatchOutputName: "near-term.md"` - - `Generated: true` - - `CompatiblePriorIDs: []ID{NearTerm}` - - `Modules: nearTermModules()` - - no `Morning` or `Evening` membership - - `resolve: resolveNearTerm` -- Implement `resolveNearTerm` as generation-time anchored: +- path tests cover all new artifact paths; +- save helpers create parent directories and write atomically; +- metadata links generated-text paths for hourly reports; +- existing report metadata tests still pass. - ```go - localNow := req.Now.In(req.Location) - return timeutil.Period{ - Start: localNow, - End: localNow.Add(nearTermHours * time.Hour), - }, nil - ``` - -- Add `nearTermDefinition()` to `DefaultRegistry()`. -- Add `NearTerm` to `Registry.All()` in a stable order after - `DailyTomorrow` and before `ThreeDay`. -- Do not change `BatchReports`. -- Leave `state.FindPriorSnapshot` behavior unchanged for - `CompareRollingWindow`; it should return `nil` because it only supports - same-date and weekend lookup. - -Tests: - -- Add report period tests for: - - lookup succeeds for `NearTerm`; - - `Registry.All()` includes `NearTerm`; - - fixed generation time resolves to exactly six hours; - - timezone-aware start and end use the effective location; - - valid period is not civil-day truncated; - - metadata RunID includes `near_term`; - - batch membership remains unchanged. -- Update registry metadata/path tests to include: - - artifact group `near-term`; - - batch output name `near-term.md`; - - generated `true`; - - compatible prior IDs `[]ID{NearTerm}`; - - comparison strategy `CompareRollingWindow`. - -Validation: +Suggested validation: ```bash -go test ./internal/report ./internal/state +go test ./internal/state ./internal/fileutil ``` This stage is small enough for one implementation prompt. -## Stage 2: Module Compatibility And Default Composition +## Stage 7: Scriptorium GeneratedText Run Adapter -Goal: make existing modules compatible with `near_term` where appropriate and -declare the default module composition. +Goal: add a GeneratedText run helper behind the Scriptorium adapter while +preserving the existing `scriptorium run` command shape. Files to inspect: -- `internal/report/near_term_report.go` -- `internal/briefing/modules.go` -- `internal/module/module.go` -- `internal/briefing/modules_test.go` -- `internal/briefing/base_modules_test.go` -- `internal/briefing/derived_modules_test.go` +- `internal/adapters/scriptorium/runner.go` +- `internal/adapters/scriptorium/runner_test.go` +- `docs/integrations/scriptorium.md` after implementation Implementation: -- Add `nearTermModules()` in `internal/report/near_term_report.go`. -- Use explicit module items in this order: - - `module.Metadata` - - `module.CurrentConditions` - - `module.HourlyForecast` - - `module.PrecipTiming` - - `module.AlertDigest` - - `module.SPCConvectiveOutlooks` - - `module.AreaForecastDiscussion` with options: +- Add weatherreporter-owned request/result types: - ```go - module.AreaForecastDiscussionOptions{ - Sections: []string{"key_messages", "short_term"}, - } - ``` + ```go + type StructuredRunRequest struct { + PromptID string + DataPackagePath string + OutputPath string + } + ``` - - `module.SPCConvectiveDiscussion` - - `module.WeatherStory` -- Expand module `SupportedReports` in `internal/briefing/modules.go`: - - include `report.NearTerm` in `allReports`; - - include `report.NearTerm` for `HourlyForecast`; - - do not include `report.NearTerm` for `NarrativeForecast`; - - do not include `report.NearTerm` in `daypartReports`; - - do not include `report.NearTerm` for `DerivedDailySummary`, - `DerivedDaypartSummaries`, `OutdoorWindows`, or `TomorrowPlanning`. -- Keep `PrecipTiming`, `AlertDigest`, `SPCConvectiveOutlooks`, - `AreaForecastDiscussion`, `SPCConvectiveDiscussion`, and `WeatherStory` - compatible through `allReports`. -- Do not add a new module ID in this stage. +- Add `StructuredRun(ctx, req)` to the runner. +- Validate prompt ID, data package path, and output path before + subprocess execution. +- Preserve current command result capture behavior: + - full argv; + - stdout; + - stderr; + - truncation flags; + - exit code; + - output path. +- Do not change current `Render` or `Run` methods. +- Scriptorium selects the structured output schema from the backend prompt + configuration associated with `PromptID`; do not pass a schema path at + runtime. +- Use the same `scriptorium run` command form as Markdown report generation. + The only material difference is that `--out` points to the raw GeneratedText + JSON artifact path rather than a Markdown report path. +- Use this argv shape: -Tests: + ```bash + scriptorium run \ + --prompt \ + --input data_package= \ + --out + ``` -- Add or update module registry tests proving: - - default near-term composition validates; - - all default near-term modules have builders; - - daily/daypart-only modules reject `report.NearTerm`; - - `HourlyForecast` builds for `report.NearTerm`; - - AFD options for the near-term default include only key messages and short - term. -- Add a focused AFD module test that near-term options omit long term when the - source provides it. +- Do not add `--format json` or any schema flag. Structured output is selected + by Scriptorium's internal prompt configuration. +- If local Scriptorium documentation needs updating after implementation, + update `docs/integrations/scriptorium.md` to describe this contract. -Validation: +Acceptance criteria: + +- argv construction tests prove `scriptorium run` is invoked without + `--format` or schema flags and with `--out `; +- nonzero exits return captured result plus error; +- missing required fields fail before subprocess execution; +- current Scriptorium render/run tests still pass; +- tokens/secrets are not added to argv or errors. + +Suggested validation: ```bash -go test ./internal/report ./internal/briefing ./internal/module +go test ./internal/adapters/scriptorium ``` This stage is small enough for one implementation prompt. -## Stage 3: Derived Facts For Rolling Windows +## Stage 8: GeneratedText App Workflow Success Path -Goal: teach `internal/facts` to build the facts needed by the near-term module -set without requiring daily summaries or daypart summaries. - -Files to inspect: - -- `internal/facts/facts.go` -- `internal/facts/facts_test.go` -- `internal/forecast` -- `internal/timeutil` -- `internal/briefing/modules.go` - -Implementation: - -- Add `report.NearTerm` handling in `facts.BuildDerived`. -- For near-term: - - populate `ValidPeriodHourlyPeriods` from the resolved six-hour valid - period; - - populate `ValidPeriodNarrativePeriods` if the existing generic selection - already does so, but do not require it for default near-term modules; - - build `PrecipTiming` from `ValidPeriodHourlyPeriods`; - - select alert overlaps using the near-term valid period; - - select SPC outlooks and discussions using the near-term valid period; - - do not build or require `DailySummaries`; - - do not build or require `DaypartSummaries`; - - do not build `StormWindowSummary`. -- Preserve existing daily, tomorrow, three-day, weekend, and storm derivation. -- If any existing helper assumes civil-day coverage, keep near-term on the - generic valid-period hourly path instead of reusing that helper. - -Tests: - -- Add facts tests for: - - valid-period hourly selection over a rolling six-hour window; - - precipitation timing based only on the near-term hourly slice; - - alert overlap inclusion/exclusion by near-term period; - - SPC outlook inclusion/exclusion by near-term period; - - SPC discussion records retained only for retained overlapping SPC days; - - no daily/daypart facts required. -- Add a regression test that an unsupported future report still returns an - actionable derivation error. - -Validation: - -```bash -go test ./internal/facts ./internal/forecast ./internal/timeutil -``` - -This stage is small enough for one implementation prompt. - -## Stage 4: App Report Mapping And CLI Command - -Goal: add explicit `generate near-term` support while preserving existing CLI -syntax and app behavior. +Goal: wire the successful generated-text/template path into app orchestration +for hourly while preserving the existing path for all current reports. Files to inspect: - `internal/app/app.go` - `internal/app/app_test.go` -- `internal/cli/root.go` -- `internal/cli/root_test.go` -- `cmd/weatherreporter/main.go` +- `internal/adapters/scriptorium` +- `internal/reporttemplate` +- `internal/generatedtext` +- `internal/state` +- `internal/promptinput` Implementation: -- Add app report kind: +- Extend the app `Renderer` interface, or introduce a second narrow interface, + so tests can fake structured generation. +- Branch report generation by `req.Resolved.Definition.GenerationMode`. +- For `GenerationModeScriptoriumMarkdown`, keep the existing flow unchanged. +- For the successful `GenerationModeGeneratedTextTemplate` path: + 1. run existing preflight and save preflight artifact; + 2. call Scriptorium structured generation with raw output path + `GeneratedTextRaw`; + 3. save structured Scriptorium command result to `GeneratedTextResult`; + 4. validate raw JSON into typed GeneratedText; + 5. save normalized GeneratedText to `GeneratedText`; + 6. build hourly render context from metadata, module snapshot, and + GeneratedText; + 7. save render context JSON; + 8. render Markdown via embedded template to managed report path; + 9. save final metadata with all generated-text paths and the schema ID; +- Defer distributor notification and optional output copy regression coverage + to Stage 10. +- Keep failure-specific persistence tests minimal in this stage; Stage 9 owns + failure artifact behavior. +- Keep Recent Changes empty for hourly. - ```go - ReportNearTerm ReportKind = "near-term" - ``` +Acceptance criteria: -- Map `ReportNearTerm` to `report.NearTerm` in `reportIDForCommand`. -- Add `near-term` to CLI generate report parsing. -- Add help usage line: +- hourly generation reaches Scriptorium structured generation, validates + GeneratedText, renders Markdown, and persists final metadata; +- daily/tomorrow/three-day/weekend/storm generation still uses existing + Markdown Scriptorium path; +- generated-text artifacts are linked from metadata for successful hourly runs; +- app tests cover the successful hourly workflow with fake structured + Scriptorium output. - ```text - weatherreporter generate near-term [--config PATH] [--units VALUE] [--tz NAME] [--out PATH] - ``` - -- Reuse existing generate command flags: - - allow `--config`; - - allow `--units`; - - allow `--tz`; - - allow `--out`; - - do not allow or require `--date`; - - do not allow or require storm `--start` / `--end`. -- Ensure the CLI returns an actionable error for unknown report names as today. -- Do not change batch commands. - -Tests: - -- Add CLI parser tests for: - - `generate near-term`; - - `generate near-term --config PATH`; - - `generate near-term --units us --tz America/Chicago --out PATH`; - - rejected `generate near-term --date YYYY-MM-DD`; - - rejected storm-only `--start` / `--end` on near-term if current parser - behavior supports this distinction. -- Add app resolution tests proving `ReportNearTerm` resolves `report.NearTerm`. -- Update help tests to assert `generate near-term` appears. -- Existing generate and run command tests must remain unchanged. - -Validation: +Suggested validation: ```bash -go test ./internal/app ./internal/cli +go test ./internal/app ./internal/state ./internal/reporttemplate ./internal/generatedtext +``` + +This stage is small enough for one implementation prompt if failure behavior is +kept to the later stages. + +## Stage 9: GeneratedText Failure Artifact Persistence + +Goal: make generated-text failures inspectable without changing successful +behavior from Stage 8. + +Files to inspect: + +- `internal/app/app.go` +- `internal/app/app_test.go` +- `internal/adapters/scriptorium` +- `internal/generatedtext` +- `internal/reporttemplate` +- `internal/state` + +Implementation: + +- Add focused app tests for failures at each generated-text step: + - preflight failure; + - structured Scriptorium generation failure; + - GeneratedText validation failure; + - render context construction failure; + - Markdown template rendering failure. +- Ensure failures preserve available artifacts: + - failed preflight saves preflight output when possible; + - failed structured generation saves command result when available; + - validation failures preserve raw GeneratedText output when available; + - render-context failures preserve validated GeneratedText when already + available; + - template failures preserve GeneratedText and render context when already + built. +- Return actionable errors that include report ID, RunID, and operation context + without exposing data package contents or secrets. +- Do not run distributor notification after any generated-text failure. +- Do not create optional `--out` copies after any generated-text failure. + +Acceptance criteria: + +- every generated-text failure point has an app-level regression test; +- partial artifacts are persisted where available and linked from metadata when + metadata can be safely written; +- failed hourly runs do not notify distributor; +- existing Markdown-generation failure behavior is unchanged. + +Suggested validation: + +```bash +go test ./internal/app ./internal/state ./internal/generatedtext ./internal/reporttemplate +``` + +This stage is small enough for one implementation prompt. + +## Stage 10: GeneratedText Output Copy And Notification Regression Coverage + +Goal: preserve existing user-facing output behavior and distributor boundaries +for generated-text reports. + +Files to inspect: + +- `internal/app/app.go` +- `internal/app/app_test.go` +- `internal/adapters/distributor` +- `internal/state` + +Implementation: + +- Keep `--out` copy behavior unchanged after the managed Markdown report + exists. +- Keep `--out-dir` behavior unchanged for batch paths if hourly is ever run + through a batch helper during tests. +- Run distributor notification, if enabled, only after: + - structured generation succeeds; + - GeneratedText validates; + - Markdown template rendering succeeds; + - final metadata is saved. +- Use the managed Markdown report path as the distributor source. +- Never use optional `--out` or `--out-dir` copies as distributor source files. +- Keep distributor request construction template-driven; do not add + hourly-specific path branching. +- Keep Recent Changes empty for hourly in this implementation. + +Acceptance criteria: + +- `--out` copies the managed generated-text Markdown report after success; +- distributor notification receives the managed Markdown report path; +- distributor notification is not called on any failure from Stage 9; +- enabled distributor failure makes hourly generation fail with the same + semantics as existing generated reports; +- existing daily/tomorrow/three-day/weekend/storm distributor behavior is + unchanged. + +Suggested validation: + +```bash +go test ./internal/app ./internal/adapters/distributor ./internal/state +``` + +This stage is small enough for one implementation prompt. + +## Stage 11: End-To-End CLI, Config, And Prompt Package Coverage + +Goal: prove the public hourly workflow and configured module overrides work +through the CLI/app boundary. + +Files to inspect: + +- `internal/cli/root.go` +- `internal/cli/root_test.go` +- `internal/config/reports.go` +- `internal/config/config_test.go` +- `internal/app/app_test.go` +- `examples/config.yml` + +Implementation: + +- Add CLI command tests for `generate hourly`. +- Reject `generate near-term`. +- Reject hourly `--date`, `--start`, `--end`, and duration-like flags. +- Add config override tests for `reports.hourly.deterministic_modules`. +- Ensure `near_term` and `near-term` report config keys are rejected. +- Add app/CLI workflow tests using fake Scriptorium structured output. +- Update examples only if they enumerate report module overrides. + +Acceptance criteria: + +- `weatherreporter --help` lists `generate hourly`; +- `generate near-term` is not accepted; +- hourly config overrides validate compatible modules; +- incompatible daily/daypart modules fail clearly for hourly; +- examples load successfully. + +Suggested validation: + +```bash +go test ./internal/cli ./internal/config ./internal/app go run ./cmd/weatherreporter --help ``` This stage is small enough for one implementation prompt. -## Stage 5: End-To-End Generation And Artifact Behavior +## Stage 12: Implemented Documentation -Goal: prove a near-term report can run through the app workflow and persist the -expected artifacts. - -Files to inspect: - -- `internal/app/app_test.go` -- `internal/state` -- `internal/promptinput` -- Weather API fixture server helpers in app tests -- distributor notification tests if report paths are asserted - -Implementation: - -- Add an app-level test that generates `ReportNearTerm` with fixture weather - data and fake Scriptorium. -- Assert: - - `ReportResult.Metadata.ReportID == report.NearTerm`; - - prompt ID is `weather.near_term_report`; - - managed report path uses artifact group `near-term`; - - data package path uses artifact group `near-term`; - - module snapshot contains the near-term module list in order; - - data package categories are unchanged; - - `hourly_forecast` contains only periods overlapping the six-hour window; - - `precip_timing` reflects only the six-hour window; - - `alert_digest` and SPC stanzas respect overlap behavior; - - AFD includes key messages and short term but not long term; - - `recent_changes.items` is empty when no rolling-window comparison exists. -- Add an app-level case for distributor notification only if existing tests - assert report-specific path rendering. The expected distributor templates - should work through existing `{artifact_group}`, `{report_id}`, - `{valid_start_date}`, and `{valid_start_time}` values without special - near-term behavior. -- Do not add `near_term` to scheduled batch tests. - -Tests: - -- Add stale-key checks if generated YAML is inspected: - - module intervals use `period_begins` / `period_ends`; - - top-level metadata keeps canonical `valid_period`. -- Ensure optional output copy via `--out` continues to use existing app copy - behavior for generated reports. - -Validation: - -```bash -go test ./internal/app ./internal/state ./internal/promptinput -``` - -This stage is small enough for one implementation prompt. - -## Stage 6: Config Overrides And Examples - -Goal: allow configured module overrides for `near_term` while keeping -maintained examples valid. - -Files to inspect: - -- `internal/config/reports.go` -- `internal/config/config_test.go` -- `docs/config.md` after implementation -- `examples/config.yml` - -Implementation: - -- Add report config key aliases: - - `near_term` - - `near-term` -- Map both aliases to `report.NearTerm`. -- Validate near-term module overrides through the existing module registry. -- Ensure incompatible modules fail clearly, for example - `derived_daily_summary` should not be compatible with `near_term`. -- Update config tests for: - - successful `reports.near_term.deterministic_modules`; - - successful `reports.near-term.deterministic_modules`; - - duplicate canonical near-term aliases rejected if both are present; - - incompatible daily-only module rejected. -- Update `examples/config.yml` only if it enumerates all report module - overrides. If it does not need a near-term override, do not add one just to - demonstrate the feature. - -Validation: - -```bash -go test ./internal/config ./internal/report ./internal/briefing -``` - -This stage is small enough for one implementation prompt. - -## Stage 7: Implemented Documentation - -Goal: update non-roadmap documentation after the feature exists. +Goal: document implemented behavior after the code exists. Files to inspect and update: - `docs/cli.md` -- `docs/config.md` - `docs/operations.md` +- `docs/config.md` +- `docs/internal/app-orchestration.md` - `docs/internal/report-registry.md` -- `docs/internal/module.md` -- `docs/internal/facts.md` - `docs/internal/briefing.md` - `docs/internal/prompt-input.md` -- `examples/config.yml`, only if changed in Stage 6 +- `docs/internal/scriptorium-adapter.md` +- `docs/internal/state.md` +- new `docs/internal/reporttemplate.md` +- `docs/integrations/scriptorium.md` +- `docs/roadmap/future.md` +- `docs/roadmap/near-term.md` +- `examples/config.yml`, only if examples changed Documentation requirements: -- Describe `generate near-term` in CLI docs after implementation. -- Document that the first version is explicit generation only and is not part - of scheduled batches. -- Document the six-hour rolling valid period and that the duration is an - internal constant, not a config field. -- Document near-term report identity, artifact group, batch output name, prompt - ID, and module composition in internal docs. -- Document near-term config override keys only if Stage 6 implements them. -- Keep deferred items under roadmap docs only: - - configurable duration; - - batch membership; - - dedicated `derived_near_term_summary`; - - near-term Recent Changes comparison output. +- Non-roadmap docs describe only implemented hourly and GeneratedText behavior. +- `docs/cli.md` documents `generate hourly`, not `generate near-term`. +- `docs/config.md` documents `reports.hourly` only if override support is + implemented. +- Internal docs distinguish: + - CollectedFacts; + - DerivedFacts; + - ModuleOutput; + - GeneratedText; + - RenderContext. +- Scriptorium integration docs include the structured-output command actually + used by the adapter. +- State docs list generated-text artifacts and metadata links. +- `docs/roadmap/near-term.md` is deleted or marked superseded. +- Deferred migrations for other report types stay under roadmap docs only. Acceptance criteria: -- Non-roadmap docs describe only implemented behavior. -- Docs do not imply a duration config field or scheduled batch behavior. -- Maintained examples load. +- No non-roadmap docs describe unimplemented generated-text migrations. +- No active docs describe `near_term` as the target public report. +- Maintained examples remain valid. -Validation: +Suggested validation: ```bash go test ./internal/config +rg -n "near_term|near-term|NearTerm" docs internal examples git diff --check ``` This stage is small enough for one implementation prompt. -## Stage 8: Final Validation +## Stage 13: Final Validation -Goal: run the complete project validation after implementation and docs are -updated. +Goal: run full validation and perform targeted manual checks. Commands: @@ -460,13 +744,16 @@ git diff --check Manual checks: -- `weatherreporter --help` lists `generate near-term`. -- No existing command syntax changed. -- `near_term` is absent from morning and evening batch membership. -- No non-roadmap docs describe unimplemented deferred near-term work. -- No config examples include secrets or invalid module IDs. -- Distributor bundle path rendering remains template-driven and does not need - report-specific branching. +- `weatherreporter --help` lists `generate hourly`. +- `weatherreporter --help` does not list `generate near-term`. +- generated hourly Markdown has deterministic headings and section order. +- GeneratedText JSON is persisted separately from deterministic facts. +- render context JSON is persisted and readable. +- embedded hourly template is readable and easy to edit. +- embedded hourly schema is readable and easy to edit. +- existing Markdown-generation reports still run through the previous + Scriptorium `run --out ` path. +- distributor notification, when enabled, uploads the managed Markdown report. This stage is small enough for one implementation prompt. @@ -474,25 +761,18 @@ This stage is small enough for one implementation prompt. Do not include these in the first implementation: -- user-configurable near-term duration; -- scheduled near-term batch membership or a new high-frequency batch command; -- dedicated `derived_near_term_summary`; -- narrative forecast periods in the default near-term module list; -- separate AFD section modules; -- rolling-window Recent Changes comparison output; -- custom CLI duration flags; -- distributor-specific behavior for near-term reports. +- migrating daily, tomorrow, three-day, weekend, or storm to GeneratedText; +- user-configurable template or local schema source overrides; +- user-defined templates or schemas; +- a generic workflow engine; +- template hot reloading; +- dynamic module selection for templates; +- prompt/schema generation from Go structs; +- scheduled hourly batch membership; +- configurable hourly report duration; +- rolling-window Recent Changes for hourly; +- distributor-specific generated-text behavior. ## Open Questions None block implementation. - -Recommendation: keep the first version explicit and narrow: `generate -near-term`, six-hour constant, no batch membership, no Recent Changes output. -This fits the existing registry/module architecture and lets prompt quality be -tested before adding scheduler or comparison complexity. - -Viable alternative: implement rolling-window Recent Changes immediately by -finding the most recent prior `near_term` report with an overlapping or adjacent -window. That could be useful later, but it needs a well-defined comparison -contract and should not block the first report implementation.