288 lines
8.7 KiB
Markdown
288 lines
8.7 KiB
Markdown
# Module Contract Internals
|
|
|
|
This document describes the module contract in `internal/module`.
|
|
|
|
## Purpose
|
|
|
|
`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.
|
|
|
|
## Inputs And Outputs
|
|
|
|
Inputs:
|
|
|
|
- ordered `module.ConfigItem` values from report definitions or config
|
|
overrides
|
|
- `module.Output` values produced by module builders
|
|
|
|
Outputs:
|
|
|
|
- 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`
|
|
|
|
## Rich Values And Prompt Exports
|
|
|
|
Each `module.Output` has two value surfaces:
|
|
|
|
- `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.
|
|
|
|
`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.
|
|
|
|
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:
|
|
|
|
- `current_conditions`
|
|
- `hourly_forecast`
|
|
- `derived_daypart_summaries`
|
|
|
|
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.
|
|
|
|
## 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
|
|
```
|
|
|
|
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`,
|
|
`period_begins`, `period_ends`, `issued_at`, `contains_location`, and
|
|
`image_url`. 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, fetch 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.
|