20 KiB
Data Package Export Implementation Roadmap
Purpose
This roadmap defines the staged implementation plan for
docs/roadmap/data-package-exports.md. It is written for an LLM coding agent
that will implement the curated data-package export feature in order.
The goal is to separate rich module values used by templates from curated prompt-facing values serialized into Scriptorium data packages. The change should make YAML data packages smaller and clearer without reducing deterministic template composability.
This is a planning document only. It may describe unimplemented behavior because
it lives under docs/roadmap/.
Source Roadmap
Use docs/roadmap/data-package-exports.md as the feature roadmap and target
state authority. That document defines the user intent, module/export boundary,
initial cleanup targets, and non-goals.
If this implementation plan and the feature roadmap conflict, update the feature roadmap first so it remains the conceptual source of truth, then update this file.
Locked Decisions
- Keep rich module values available for templates, module snapshots, inspection, Recent Changes, and render contexts.
- Serialize curated prompt-facing module values into data packages.
- Store prompt-facing values on
module.Outputas runtime-only values. - Do not persist prompt-facing values in module snapshot JSON.
- Keep
internal/promptinputindependent frominternal/briefing. - Add prompt export behavior to the module registry/definition path where module output policy already lives.
- Default modules without custom exporters to pass-through prompt values.
- Bump the data-package schema version from
weatherreporter.data_package.v2toweatherreporter.data_package.v3. - No compatibility aliases are required for removed prompt-facing fields.
- Do not add user-configurable field selection, field allowlists, reflection filters, or prompt-specific field profiles in this implementation.
Implementation Principles
- Preserve existing generated Markdown output.
- Preserve existing template render-context richness.
- Preserve existing module snapshot richness and JSON shape except for any unavoidable schema-version-only change. The preferred approach is to omit prompt values from module snapshot JSON entirely.
- Keep exporter code near the module that owns the shape.
- Use typed prompt-facing structs for modules that need reshaping.
- Keep simple modules on default pass-through behavior.
- Make exporter errors actionable and include module ID/stanza context.
- Keep YAML category ordering unchanged.
- Treat generated-template
today,tomorrow, anddailyreports as equal consumers of rich template module values. - Update implemented docs only after code behavior exists.
Stage 1: Runtime Prompt Value Contract
Goal
Add a runtime-only prompt value to module outputs while preserving rich module snapshot persistence.
Files To Inspect
internal/module/module.gointernal/module/module_test.gointernal/state/filesystem.gointernal/state/filesystem_test.gointernal/app/app.gointernal/app/app_test.go
Implementation
- Add a runtime-only field to
module.Output, for example:
type Output struct {
ID ID `json:"id"`
StanzaName string `json:"stanzaName"`
Value any `json:"value"`
PromptValue any `json:"-"`
}
- Add a small helper in
internal/module, for example:
func (o Output) DataPackageValue() any
The helper should return PromptValue when non-nil and fall back to Value
otherwise. This fallback keeps tests and any manually built snapshots simple.
- Do not require
PromptValueinSnapshot.Validate. - Do not persist
PromptValueto module snapshot JSON. - Do not change
StanzaValue; it should continue decoding richValue.
Acceptance Criteria
- Module snapshots still serialize rich module values under
value. - Module snapshots do not serialize
promptValueor any equivalent field. - Existing rich-module snapshot lookup and
StanzaValuebehavior remain unchanged. - Code has a single helper for choosing the data-package value from an output.
Tests
- Add module tests proving:
DataPackageValueusesPromptValuewhen set;DataPackageValuefalls back toValue;- marshaled snapshot JSON omits
PromptValue; StanzaValuecontinues decoding richValue.
Suggested focused command:
go test ./internal/module ./internal/state
Prompt Size
Small enough for one implementation prompt.
Stage 2: Module Registry Prompt Exporters
Goal
Teach the module registry to attach prompt-facing values to outputs as modules are built.
Files To Inspect
internal/briefing/modules.gointernal/briefing/modules_test.gointernal/briefing/*_module.gointernal/module/module.go
Implementation
- Add a prompt exporter type in
internal/briefing, for example:
type ModulePromptExporter func(value any) (any, error)
- Add
PromptExporter ModulePromptExportertoModuleDefinition. - In
ModuleRegistry.BuildModule, after builder output ID and stanza validation:- if
PromptExporteris nil, setoutput.PromptValue = output.Value; - if
PromptExporteris present, call it withoutput.Value; - set
output.PromptValueto the returned value; - wrap exporter errors with module ID and stanza context.
- if
- Add a typed exporter helper if it keeps module exporters concise, for example:
func promptExporter[T any](fn func(T) (any, error)) ModulePromptExporter
The helper should convert any through JSON marshal/unmarshal or direct type
assertion only if it meaningfully reduces boilerplate without hiding errors.
- Do not make
internal/promptinputimportinternal/briefing. - Do not move module-specific export policy into
internal/promptinput.
Acceptance Criteria
- Every built module output has a non-nil data-package value.
- Modules without custom exporters pass through rich values.
- Custom exporter errors identify the module and stanza.
- Registry validation remains focused on definitions, duplicate IDs/stanzas, supported reports, options, builders, and missing-data policy.
Tests
- Add or update module registry tests for:
- default pass-through prompt values;
- custom exporter prompt values;
- exporter error wrapping;
- output ID/stanza validation still runs before or around export behavior;
- output
PromptValueis not persisted in snapshot JSON.
Suggested focused command:
go test ./internal/briefing ./internal/module
Prompt Size
Small enough for one implementation prompt.
Stage 3: Promptinput Uses Exported Values And Schema V3
Goal
Make data-package construction serialize prompt-facing values and bump the data package schema version.
Files To Inspect
internal/promptinput/package.gointernal/promptinput/package_test.gointernal/state/filesystem_test.gointernal/app/app.gointernal/app/app_test.godocs/roadmap/data-package-exports.md
Implementation
- Change
promptinput.SchemaVersionto:
const SchemaVersion = "weatherreporter.data_package.v3"
- Update
stanzasFromSnapshotto useoutput.DataPackageValue()rather thanoutput.Value. - Keep
BriefingStanzascategory grouping and ordering unchanged. - Keep
LoadYAMLvalidation strict for the current schema version. - Update tests and fixtures that assert
weatherreporter.data_package.v2.
Acceptance Criteria
- Saved data packages use schema version
weatherreporter.data_package.v3. - Data package stanzas use
PromptValuewhen present. - Data package stanzas fall back to rich
ValuewhenPromptValueis absent, which keeps hand-built test snapshots and loaded rich snapshots usable. - YAML category ordering remains unchanged.
- Module snapshots remain rich and unaffected by prompt export serialization.
Tests
- Update promptinput tests for:
- schema version
v3; - prompt value preferred over rich value;
- fallback to rich value;
- deterministic categorized YAML output unchanged apart from stanza values and schema version;
- unknown/uncategorized stanza behavior unchanged.
- schema version
- Update state tests that load data packages.
Suggested focused command:
go test ./internal/promptinput ./internal/state
Prompt Size
Small enough for one implementation prompt.
Stage 4: Current Conditions And Hourly Forecast Exports
Goal
Add custom prompt exports for the clearest noisy raw-data modules:
current_conditions and hourly_forecast.
Files To Inspect
internal/briefing/current_conditions_module.gointernal/briefing/hourly_forecast_module.gointernal/briefing/base_modules_test.gointernal/generatedtext/render_context.gointernal/reporttemplate/templates/*.md.tmplinternal/app/app_test.go
Current Conditions Export Shape
The prompt-facing current_conditions export should keep:
condition_textis_daytemperature_ctemperature_fapparent_temperature_capparent_temperature_fdewpoint_cdewpoint_frelative_humidity_percentwind_speed_kmhwind_speed_mphwind_direction
It should remove:
condition_text_lowerwind_direction_text
The rich CurrentConditionsModule should keep those helper fields for
templates.
Hourly Forecast Export Shape
The prompt-facing hourly_forecast export should keep module-level fields:
productissued_atupdated_atsource_locationsource_location_idperiods
Each prompt-facing hourly period should keep:
period_beginsperiod_endsnameis_daycondition_codetext_description- temperature fields
- dewpoint fields
- wind speed and gust fields
wind_direction- pressure fields
- visibility fields
- apparent temperature fields
cloud_cover_percentprobability_of_precipitation_percent- precipitation amount fields
- snowfall depth fields
uv_indexrelative_humidity_percent
Each prompt-facing hourly period should remove:
hour_labeltext_description_lowermention_precipitation
The rich HourlyForecastPeriod should keep those helper fields for templates.
Implementation
- Add prompt export structs near each module.
- Add exporter functions near each module.
- Register exporters in the default module definitions.
- Prefer straightforward field copying over reflection.
- Preserve
omitemptybehavior.
Acceptance Criteria
- Saved data packages no longer include removed fields for current conditions or hourly forecast.
- Rich module snapshots and render contexts still include template helper fields.
- Existing hourly, today, tomorrow, and daily template output remains unchanged wherever those templates consume current conditions or hourly forecast values.
Tests
- Update module tests to prove prompt exports omit stale fields and keep expected factual fields.
- Update template/render-context tests to prove helper fields remain available to templates.
- Update app workflow tests to inspect saved YAML and reject:
condition_text_lower;wind_direction_text;hour_label;text_description_lower;mention_precipitation.
Suggested focused command:
go test ./internal/briefing ./internal/generatedtext ./internal/reporttemplate ./internal/app
Prompt Size
Medium. Suitable for one implementation prompt.
Stage 5: Derived Daypart Summary Export
Goal
Add a curated prompt export for derived_daypart_summaries while preserving the
rich daypart fields used by Daily, Today, and Tomorrow templates.
Files To Inspect
internal/briefing/derived_daypart_summaries_module.gointernal/briefing/derived_modules_test.gointernal/generatedtext/render_context.gointernal/reporttemplate/templates/daily.md.tmplinternal/reporttemplate/templates/today.md.tmplinternal/reporttemplate/templates/tomorrow.md.tmplinternal/reporttemplate/reporttemplate_test.gointernal/app/app_test.go
Export Shape
For each daypart, the prompt-facing export should keep:
datedisplay_nameperiod_beginsperiod_endstemp_range_fapparent_temp_range_fmax_pop_percentmax_pop_timemention_precipitationmax_wind_gust_mphmax_wind_gust_timedominant_conditiontemperature_trendtemperature_start_phrase_ftemperature_end_phrase_ftemperature_peak_phrase_ftemperature_steady_phrase_fnotable_conditionssnowicefogheatcoldwindrelevant_alert_count
The prompt-facing export should remove:
temperature_phrase_fdominant_condition_lowerdominant_condition_displaymax_pop_time_label
For max_pop_time, use the most readable existing time label. Prefer
MaxPopTimeLabel when present, falling back to MaxPopTime, while keeping the
prompt-facing field name max_pop_time.
Keep the temperature trend phrase fields even though only some are populated for each trend. They are not duplicates when used according to the trend:
- rising/falling use start and end phrases;
- peaking uses peak phrase;
- steady uses steady phrase.
Implementation
- Add prompt export structs near the daypart module.
- Add an exporter for the
map[string]DerivedDaypartSummaryModulevalue. - Preserve map keys and values for all emitted dayparts.
- Register the exporter in the default module definitions.
- Do not alter rich
DerivedDaypartSummaryModulefields used by templates.
Acceptance Criteria
- Saved data packages no longer include the removed daypart fields.
- The prompt-facing daypart export still gives the LLM enough information to understand condition, temperature trend, precipitation, wind, notable conditions, and alert relevance.
- Daily, Today, and Tomorrow template output remains unchanged.
- Render contexts still expose rich daypart helper fields.
Tests
- Update daypart module tests for:
- prompt export field presence;
- removed field absence;
- rising, falling, peaking, and steady trend values;
- max PoP time using the friendly label under the stable
max_pop_timekey.
- Update template tests to prove rich helper fields still render Daily, Today, and Tomorrow daypart wording.
- Update app data-package tests to reject stale daypart fields.
Suggested focused command:
go test ./internal/briefing ./internal/generatedtext ./internal/reporttemplate ./internal/app
Prompt Size
Medium. Suitable for one implementation prompt.
Stage 6: Pass-Through Review And Workflow Regression Tests
Goal
Confirm all other modules export correctly through pass-through behavior and add workflow-level coverage proving the new boundary.
Files To Inspect
internal/briefing/*_module.gointernal/briefing/modules.gointernal/promptinput/package_test.gointernal/app/app_test.gointernal/generatedtext/render_context_test.gointernal/reporttemplate/reporttemplate_test.go
Implementation
- Review remaining modules:
metadatanarrative_forecastderived_daily_summaryprecip_timingoutdoor_windowsalert_digestspc_convective_outlooksspc_convective_discussionarea_forecast_discussionweather_story- planning modules
- Keep pass-through behavior for modules that are already prompt-appropriate.
- Add custom exporters only if a module contains clear template-only helpers or confusing duplicate fields.
- Do not broaden this stage into a general prompt-schema redesign.
- Add workflow assertions that:
- module snapshots contain rich values;
- render contexts contain rich values;
- data packages contain curated values;
- Scriptorium receives the curated data package path exactly as before.
- Include workflow coverage for generated-template
today,tomorrow, anddailyreports when asserting daypart and render-context behavior.
Acceptance Criteria
- Every default module either has a custom exporter or intentionally uses pass-through.
- App workflow tests prove saved data packages omit the cleaned fields.
- App workflow tests prove rich helper fields remain available where templates use them.
- App workflow or render-context tests include
dailyalongsidetodayandtomorrowfor daypart helper coverage. - Existing report generation behavior remains unchanged except for data-package YAML content and schema version.
Tests
Suggested focused command:
go test ./internal/briefing ./internal/promptinput ./internal/generatedtext ./internal/reporttemplate ./internal/app
Prompt Size
Small to medium. Suitable for one implementation prompt.
Stage 7: Documentation And Final Validation
Goal
Update implemented documentation after the code exists and run full validation.
Files To Inspect
docs/internal/module.mddocs/internal/prompt-input.mddocs/templates.mddocs/operations.mddocs/troubleshooting.mddocs/roadmap/data-package-exports.md- tests that assert documentation examples or data-package snippets
Implementation
- Update
docs/internal/module.mdto describe:- rich module output;
- runtime prompt export values;
- pass-through exporters;
- module-owned prompt export policy.
- Update
docs/internal/prompt-input.mdto document:- schema version
weatherreporter.data_package.v3; - curated module export behavior;
- category ordering unchanged;
- data packages are not full template render contexts.
- schema version
- Update
docs/templates.mdto clarify that templates can use richer fields than the data package exposes. - Update any Daily template-variable documentation alongside Today and Tomorrow references where the same module fields are discussed.
- Update any implemented docs containing stale examples of removed data-package fields.
- Do not document deferred configurable field profiles or reflection filters as implemented behavior.
Acceptance Criteria
- Non-roadmap docs describe only implemented data-package export behavior.
- Docs do not imply data packages contain template-only helper fields.
- Examples and snippets use schema version
v3when they show data packages.
Validation Commands
go test ./internal/module ./internal/briefing ./internal/promptinput
go test ./internal/generatedtext ./internal/reporttemplate ./internal/app
go test ./...
go run ./cmd/weatherreporter --help
git diff --check
Stale Field Greps
Run focused checks against docs and expected saved data-package fixtures/snippets:
rg -n "condition_text_lower|wind_direction_text|hour_label|text_description_lower|mention_precipitation|dominant_condition_lower|dominant_condition_display|max_pop_time_label|temperature_phrase_f" docs internal/*/*_test.go
Review matches manually. Some matches should remain in rich module structs, template tests, and render-context tests; they should not remain as expected data-package fields.
Prompt Size
Medium. Suitable for one implementation prompt.
Deferred Work
Do not include these in this implementation sequence:
- user-configurable data-package field selection;
- per-prompt custom field profiles;
- reflection-based generic include/exclude lists;
- automatic schema generation for data-package exports;
- changing collected facts or derived facts contracts;
- changing Scriptorium invocation behavior;
- changing generated Markdown templates beyond preserving their output;
- migrating old workspace data-package artifacts.
Open Questions
No open questions block implementation.
The implementation plan intentionally locks in the recommended choices from the
feature roadmap: schema version bump to weatherreporter.data_package.v3, and
runtime-only prompt export values stored on module.Output.
Global Validation Checklist
go test ./...passes.go run ./cmd/weatherreporter --helpis accurate.git diff --checkpasses.- Saved data packages use schema version
weatherreporter.data_package.v3. - Saved data packages serialize prompt-facing module exports.
- Saved module snapshots serialize rich module values and do not persist prompt-facing values.
- Render contexts continue to expose rich module values.
- Hourly, Today, Tomorrow, and Daily templates continue to render the same Markdown output.
- YAML briefing category order remains unchanged.
current_conditionsdata packages omitcondition_text_lowerandwind_direction_text.hourly_forecast.periods[]data packages omithour_label,text_description_lower, andmention_precipitation.derived_daypart_summariesdata packages omittemperature_phrase_f,dominant_condition_lower,dominant_condition_display, andmax_pop_time_label.- Non-roadmap docs describe only implemented behavior.