Refresh report and template internals documentation
This commit is contained in:
@@ -1,290 +1,68 @@
|
||||
# Module Contract Internals
|
||||
|
||||
This document describes the module contract in `internal/module`.
|
||||
`internal/module` defines the stable envelope between report composition,
|
||||
module builders, snapshots, comparisons, templates, and prompt packages. It
|
||||
does not define a report, execute a builder, or choose prompt-export policy;
|
||||
those responsibilities belong to [report registry](report-registry.md) and
|
||||
[briefing](briefing.md).
|
||||
|
||||
## Purpose
|
||||
## Outputs and snapshots
|
||||
|
||||
`internal/module` defines the shared identifiers and data envelopes used for
|
||||
report modules. Report definitions use module IDs for composition, module
|
||||
builders produce rich outputs with stanza names, prompt input packages consume
|
||||
runtime prompt export values, and Recent Changes compares snapshot stanzas.
|
||||
Each `Output` has a module ID, stanza name, rich `Value`, and runtime-only
|
||||
`PromptValue`. `DataPackageValue` returns the prompt value when present and
|
||||
otherwise the rich value. This permits custom prompt exports without shrinking
|
||||
the template and inspection value.
|
||||
|
||||
## Inputs And Outputs
|
||||
`NewSnapshot` builds the ordered `weatherreporter.modules.v1` snapshot and
|
||||
validates it. Snapshot JSON persists IDs, stanza names, and rich values only;
|
||||
`PromptValue` is deliberately excluded. `StanzaValue` decodes a named rich
|
||||
stanza into a caller-supplied type, reporting a missing stanza separately from
|
||||
a decoding error.
|
||||
|
||||
Inputs:
|
||||
Snapshots reject missing schema versions, empty IDs or stanza names, and
|
||||
duplicate IDs or stanza names. Output order is caller-owned and preserved.
|
||||
|
||||
- ordered `module.ConfigItem` values from report definitions or config
|
||||
overrides
|
||||
- `module.Output` values produced by module builders
|
||||
## Registered IDs and default composition
|
||||
|
||||
Outputs:
|
||||
The registered IDs are `metadata`, `current_conditions`,
|
||||
`narrative_forecast`, `hourly_forecast`, `derived_daily_summary`,
|
||||
`derived_daypart_summaries`, `precip_timing`, `alert_digest`,
|
||||
`spc_convective_outlooks`, `area_forecast_discussion`,
|
||||
`spc_convective_discussion`, `weather_story`, `outdoor_windows`,
|
||||
`today_planning`, `tomorrow_planning`, and `daily_planning`.
|
||||
|
||||
- stable `module.ID` constants
|
||||
- typed option structs for registered modules
|
||||
- `module.Snapshot` with schema version `weatherreporter.modules.v1`
|
||||
- ordered snapshot outputs with module ID, stanza name, and typed value
|
||||
- runtime-only prompt export values on module outputs
|
||||
- `module.Output.DataPackageValue`, which selects the prompt export value and
|
||||
falls back to the rich value for hand-built or loaded snapshots
|
||||
- typed stanza lookup through `module.StanzaValue`
|
||||
The registry declares these ordered default compositions:
|
||||
|
||||
## Rich Values And Prompt Exports
|
||||
| Report | Ordered modules |
|
||||
| --- | --- |
|
||||
| Daily | metadata, current conditions, narrative forecast, daily summary, daypart summaries, precipitation timing, alert digest, SPC outlooks, AFD (long term), SPC discussion, weather story, outdoor windows, daily planning, hourly forecast |
|
||||
| Today | metadata, current conditions, narrative forecast, daily summary, daypart summaries, precipitation timing, alert digest, SPC outlooks, AFD, SPC discussion, weather story, outdoor windows, hourly forecast, today planning |
|
||||
| Tomorrow | metadata, current conditions, narrative forecast, daily summary, daypart summaries, precipitation timing, alert digest, SPC outlooks, AFD, SPC discussion, weather story, outdoor windows, tomorrow planning, hourly forecast |
|
||||
| Hourly | metadata, current conditions, hourly forecast, precipitation timing, alert digest, SPC outlooks, AFD (key messages and short term), SPC discussion, weather story |
|
||||
| Three-day and Weekend | metadata, current conditions, daypart summaries, precipitation timing, alert digest, SPC outlooks, AFD, SPC discussion, weather story, outdoor windows |
|
||||
| Storm | metadata, current conditions, precipitation timing, alert digest, SPC outlooks, AFD, SPC discussion, weather story |
|
||||
|
||||
Each `module.Output` has two value surfaces:
|
||||
The only non-empty default option is the AFD section selection. It accepts a
|
||||
`sections` list; omitted or empty selects all available sections. Report
|
||||
definitions may narrow it as shown above. Option shape and report compatibility
|
||||
are validated by the briefing registry.
|
||||
|
||||
- `Value`: the rich module value used by templates, module snapshots,
|
||||
inspection, Recent Changes, and render contexts.
|
||||
- `PromptValue`: the runtime-only prompt export used when building Scriptorium
|
||||
data packages.
|
||||
## Rich and prompt-facing values
|
||||
|
||||
`PromptValue` is deliberately excluded from module snapshot JSON. Persisted
|
||||
module snapshots keep only the rich `value` field so inspection and
|
||||
render-context reconstruction keep the full deterministic template surface.
|
||||
Rich values remain available to snapshots, comparisons, and render contexts.
|
||||
Briefing attaches custom prompt exports only for current conditions, hourly
|
||||
forecast, and derived daypart summaries; all other current builders use
|
||||
pass-through values. The prompt package owns how exported stanzas are grouped
|
||||
and serialized; see [prompt input](prompt-input.md).
|
||||
|
||||
The `internal/briefing` module registry attaches prompt export values when it
|
||||
builds module outputs. Modules without a custom exporter use default
|
||||
pass-through behavior, so their prompt value is the same as their rich value.
|
||||
Modules with custom prompt export policy own typed prompt export structs near
|
||||
the module builder. Custom prompt exports are:
|
||||
## Verification and invariants
|
||||
|
||||
- `current_conditions`
|
||||
- `hourly_forecast`
|
||||
- `derived_daypart_summaries`
|
||||
Focused tests cover snapshot validation and order, typed stanza lookup, and
|
||||
prompt-value fallback:
|
||||
|
||||
Custom exporters remove template-only helpers or confusing duplicates from the
|
||||
data package without shrinking the rich module structs used by templates.
|
||||
Exporter failures include module ID and stanza context.
|
||||
|
||||
## Registered Module IDs
|
||||
|
||||
The registry recognizes these IDs:
|
||||
|
||||
- `metadata`
|
||||
- `current_conditions`
|
||||
- `narrative_forecast`
|
||||
- `hourly_forecast`
|
||||
- `derived_daily_summary`
|
||||
- `derived_daypart_summaries`
|
||||
- `precip_timing`
|
||||
- `alert_digest`
|
||||
- `spc_convective_outlooks`
|
||||
- `area_forecast_discussion`
|
||||
- `spc_convective_discussion`
|
||||
- `weather_story`
|
||||
- `outdoor_windows`
|
||||
- `today_planning`
|
||||
- `tomorrow_planning`
|
||||
- `daily_planning`
|
||||
|
||||
Every registered module has a builder. Report composition entries that refer to
|
||||
unknown or unimplemented module IDs fail validation instead of being skipped.
|
||||
|
||||
## Daily Composition
|
||||
|
||||
The default Daily 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. `daily_planning`
|
||||
14. `hourly_forecast`
|
||||
|
||||
The embedded Daily template uses selected deterministic fields from these
|
||||
module outputs after GeneratedText validation. Its `area_forecast_discussion`
|
||||
item is configured to include only `long_term`.
|
||||
|
||||
## Today Composition
|
||||
|
||||
The default Today 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. `hourly_forecast`
|
||||
14. `today_planning`
|
||||
|
||||
The embedded Today template uses selected deterministic fields from these
|
||||
module outputs after GeneratedText validation.
|
||||
|
||||
## 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.
|
||||
|
||||
## Daily Planning
|
||||
|
||||
`daily_planning` emits dated daily planning facts for the `daily` report ID.
|
||||
Its output stanza is also named `daily_planning`. The module is supported only
|
||||
by that report ID and depends on daily summaries for the selected local civil
|
||||
day. The default Daily Report composition includes it.
|
||||
|
||||
The output uses this shape:
|
||||
|
||||
- `morning_readiness`
|
||||
- `commute_school_workday_concerns`
|
||||
- `overnight_change_watch`
|
||||
|
||||
The type is `briefing.DailyPlanningModule`; it is independent from
|
||||
`briefing.TomorrowPlanningModule`.
|
||||
|
||||
## Today Planning
|
||||
|
||||
`today_planning` emits current-day planning facts for Today Report. Its output
|
||||
stanza is also named `today_planning`. The module is supported only by Today
|
||||
Report and depends on daily and daypart summaries for the current local civil
|
||||
day.
|
||||
|
||||
The output uses this shape:
|
||||
|
||||
- `morning_readiness`
|
||||
- `commute_school_workday_concerns`
|
||||
- `outdoor_planning`
|
||||
- `late_day_change_watch`
|
||||
|
||||
The type is `briefing.TodayPlanningModule`; it is independent from
|
||||
`briefing.TomorrowPlanningModule`.
|
||||
|
||||
## Hourly Composition
|
||||
|
||||
The default Hourly Report module order is:
|
||||
|
||||
1. `metadata`
|
||||
2. `current_conditions`
|
||||
3. `hourly_forecast`
|
||||
4. `precip_timing`
|
||||
5. `alert_digest`
|
||||
6. `spc_convective_outlooks`
|
||||
7. `area_forecast_discussion`
|
||||
8. `spc_convective_discussion`
|
||||
9. `weather_story`
|
||||
|
||||
Hourly Report does not include daily or daypart summary modules by default.
|
||||
Its `area_forecast_discussion` item is configured to include only
|
||||
`key_messages` and `short_term`.
|
||||
|
||||
## Options
|
||||
|
||||
Most modules use an empty options struct, including
|
||||
`spc_convective_outlooks` and `spc_convective_discussion`.
|
||||
`area_forecast_discussion` accepts:
|
||||
|
||||
```yaml
|
||||
sections:
|
||||
- product
|
||||
- key_messages
|
||||
- short_term
|
||||
- long_term
|
||||
```sh
|
||||
go test ./internal/module
|
||||
```
|
||||
|
||||
An omitted or empty `sections` list includes all available discussion sections.
|
||||
Invalid option shapes fail during config normalization or composition
|
||||
validation.
|
||||
|
||||
## SPC Convective Module Outputs
|
||||
|
||||
`spc_convective_outlooks` emits a risk-product stanza with:
|
||||
|
||||
- `checked`
|
||||
- `as_of`
|
||||
- `issued_at`
|
||||
- `location_id`
|
||||
- `location_name`
|
||||
- `outlook_count`
|
||||
- `outlooks`
|
||||
- `risk_digest`
|
||||
|
||||
Each outlook entry may include `day`, `outlook_type`, `label`, `label_text`,
|
||||
`background_definition`, `period_begins`, `period_ends`, `issued_at`,
|
||||
`contains_location`, and `image_url`. `background_definition` is embedded
|
||||
briefing reference content for known outlook type/label pairs and may include
|
||||
`plain_language`, `official_description`, and `relative_level`. It omits GeoJSON
|
||||
geometry, source URL, expiration time, and severity rank.
|
||||
|
||||
The optional `risk_digest` list is a curated report-rendering subset of
|
||||
categorical outlooks that overlap the report period, contain the configured
|
||||
location, and meet the minimum severity threshold. Entries include `label_text`,
|
||||
`risk_label`, `period_begins`, and `period_ends`; they do not expose severity
|
||||
rank.
|
||||
|
||||
`spc_convective_discussion` emits a narrative stanza only when a retained
|
||||
report-period categorical outlook has severity rank `3` or higher and matching
|
||||
discussion text is available. Its output includes `included_because` and
|
||||
`discussions`; each discussion may include `day`, `period_begins`,
|
||||
`period_ends`, `headline`, `summary`, `discussion`, and `updated_at`.
|
||||
Discussions are included only for SPC days whose retained categorical outlooks
|
||||
meet the severity threshold.
|
||||
|
||||
## Boundaries
|
||||
|
||||
- This package owns module identifiers, config item envelopes, output
|
||||
envelopes, snapshot validation, and typed stanza lookup.
|
||||
- It does not define report IDs, execute builders, collect weather data, derive
|
||||
forecast facts, write state, or invoke Scriptorium.
|
||||
|
||||
## State Or Manifest Behavior
|
||||
|
||||
`module.Snapshot` values are persisted by `internal/state` as JSON. Snapshot
|
||||
validation rejects missing schema version, missing module IDs, missing stanza
|
||||
names, duplicate module outputs, and duplicate stanza names while preserving
|
||||
output order. Snapshot JSON contains rich module values only; runtime prompt
|
||||
export values are not persisted.
|
||||
|
||||
## Failure Behavior
|
||||
|
||||
- Snapshot construction fails for duplicate module outputs or duplicate stanza
|
||||
names.
|
||||
- Typed stanza lookup returns `found=false` for missing stanzas.
|
||||
- Typed stanza lookup wraps JSON marshal/decode failures with stanza context.
|
||||
|
||||
## Tests
|
||||
|
||||
Inspect:
|
||||
|
||||
- `internal/module/module_test.go`
|
||||
- `internal/briefing/modules_test.go`
|
||||
- `internal/report/period_test.go`
|
||||
|
||||
## Invariants
|
||||
|
||||
- `internal/module` does not import `internal/report`.
|
||||
- Module IDs are stable strings.
|
||||
- Each emitted module output has exactly one stanza name and one rich typed
|
||||
value.
|
||||
- Built module outputs have a data-package value, either from a custom prompt
|
||||
exporter or from default pass-through behavior.
|
||||
- Snapshot output order is caller-owned and preserved.
|
||||
Module IDs and stanza names are stable, every emitted output has one of each,
|
||||
and this package never imports the report registry.
|
||||
|
||||
Reference in New Issue
Block a user