Remove recent changes from prompt execution

This commit is contained in:
2026-08-01 19:22:11 +00:00
parent 8be9b020d4
commit 5ddd3ee19c
19 changed files with 35 additions and 177 deletions

View File

@@ -68,11 +68,11 @@ func (client *fakeClient) request() promptkit.GenerateRequest {
func TestInspectPromptAndProfile(t *testing.T) { func TestInspectPromptAndProfile(t *testing.T) {
adapter := newTestAdapter(t, &fakeClient{}) adapter := newTestAdapter(t, &fakeClient{})
inspection, err := adapter.InspectPrompt(context.Background(), "weather.daily_generated_text", "1.1.0") inspection, err := adapter.InspectPrompt(context.Background(), "weather.daily_generated_text", "2.0.0")
if err != nil { if err != nil {
t.Fatalf("InspectPrompt() error = %v", err) t.Fatalf("InspectPrompt() error = %v", err)
} }
if inspection.PromptID != "weather.daily_generated_text" || inspection.PromptVersion != "1.1.0" || inspection.DefaultProfileID != "weather-balanced" { if inspection.PromptID != "weather.daily_generated_text" || inspection.PromptVersion != "2.0.0" || inspection.DefaultProfileID != "weather-balanced" {
t.Fatalf("inspection = %#v", inspection) t.Fatalf("inspection = %#v", inspection)
} }
if len(inspection.Inputs) != 1 || inspection.Inputs[0].Name != "data_package" || !inspection.Inputs[0].Required || inspection.Inputs[0].ContentType != "application/yaml" { if len(inspection.Inputs) != 1 || inspection.Inputs[0].Name != "data_package" || !inspection.Inputs[0].Required || inspection.Inputs[0].ContentType != "application/yaml" {
@@ -251,7 +251,7 @@ func TestExecuteEmbeddedHourlyProfileThroughPreparedPath(t *testing.T) {
} }
request := promptexec.ExecuteRequest{ request := promptexec.ExecuteRequest{
PromptID: "weather.hourly_generated_text", PromptID: "weather.hourly_generated_text",
PromptVersion: "1.1.0", PromptVersion: "2.0.0",
ProfileID: "weather-light", ProfileID: "weather-light",
DataPackage: []byte("report:\n id: hourly\nbriefing: {}\n"), DataPackage: []byte("report:\n id: hourly\nbriefing: {}\n"),
DataPackagePath: "data-packages/hourly/data_package.yaml", DataPackagePath: "data-packages/hourly/data_package.yaml",
@@ -528,7 +528,7 @@ func writeProfileFile(t *testing.T, profile string) string {
func testExecuteRequest() promptexec.ExecuteRequest { func testExecuteRequest() promptexec.ExecuteRequest {
return promptexec.ExecuteRequest{ return promptexec.ExecuteRequest{
PromptID: "weather.daily_generated_text", PromptID: "weather.daily_generated_text",
PromptVersion: "1.1.0", PromptVersion: "2.0.0",
ProfileID: "test-profile", ProfileID: "test-profile",
DataPackage: []byte("report:\n id: daily\nbriefing: {}\n"), DataPackage: []byte("report:\n id: daily\nbriefing: {}\n"),
DataPackagePath: "data-packages/daily/data_package.yaml", DataPackagePath: "data-packages/daily/data_package.yaml",

View File

@@ -9,7 +9,6 @@ import (
distributoradapter "gitea.maximumdirect.net/eric/weatherreporter/internal/adapters/distributor" distributoradapter "gitea.maximumdirect.net/eric/weatherreporter/internal/adapters/distributor"
"gitea.maximumdirect.net/eric/weatherreporter/internal/briefing" "gitea.maximumdirect.net/eric/weatherreporter/internal/briefing"
"gitea.maximumdirect.net/eric/weatherreporter/internal/changes"
"gitea.maximumdirect.net/eric/weatherreporter/internal/collect" "gitea.maximumdirect.net/eric/weatherreporter/internal/collect"
"gitea.maximumdirect.net/eric/weatherreporter/internal/config" "gitea.maximumdirect.net/eric/weatherreporter/internal/config"
"gitea.maximumdirect.net/eric/weatherreporter/internal/facts" "gitea.maximumdirect.net/eric/weatherreporter/internal/facts"
@@ -93,8 +92,6 @@ type ReportResult struct {
NotificationPath string NotificationPath string
Metadata state.Metadata Metadata state.Metadata
MetadataPath string MetadataPath string
PriorSnapshot *state.PriorSnapshot
RecentChanges []changes.Change
GeneratedTextRawPath string GeneratedTextRawPath string
GeneratedTextPath string GeneratedTextPath string
RenderContextPath string RenderContextPath string
@@ -998,28 +995,6 @@ func defaultStore(cfg config.Config) (*state.FilesystemStore, error) {
return state.NewFilesystemStore(cfg.Workspace) return state.NewFilesystemStore(cfg.Workspace)
} }
func recentChanges(ctx context.Context, store state.Store, priorSnapshot *state.PriorSnapshot, reportID report.ID, current module.Snapshot, cfg config.RecentChangeConfig) ([]changes.Change, error) {
if priorSnapshot == nil {
return nil, nil
}
previous, err := store.LoadModuleSnapshot(ctx, priorSnapshot.ModuleSnapshotPath)
if err != nil {
return nil, err
}
thresholds := changes.Thresholds{
TemperatureDegrees: cfg.TemperatureDegrees,
PrecipProbabilityPoints: cfg.PrecipProbabilityPoints,
WindGustMilesPerHour: cfg.WindGustMilesPerHour,
PrecipTimingShiftMinutes: cfg.PrecipTimingShiftMinutes,
}
switch reportID {
case report.Daily, report.Today, report.Tomorrow:
return changes.CompareDaily(previous, current, thresholds)
default:
return nil, nil
}
}
func generatedReportError(resolved report.Resolved, runID string, operation string, err error) error { func generatedReportError(resolved report.Resolved, runID string, operation string, err error) error {
if err == nil { if err == nil {
return nil return nil

View File

@@ -379,34 +379,6 @@ func TestRunBatchDetailedKeepsDynamicDailyArtifactsDistinct(t *testing.T) {
} }
} }
func TestRunBatchDetailedUsesPriorSnapshotsForPromptPackages(t *testing.T) {
cfg := assembledBatchConfig(t, false)
firstBundle := assembledBatchBundle(t, "2026-05-31")
setWorkflowTemperatures(&firstBundle, 45)
first, err := RunBatchDetailed(context.Background(), BatchRequest{
Config: cfg, Batch: BatchMorning, Now: workflowTime("2026-05-29T08:00:00-05:00"),
Collector: &workflowCollector{result: &collect.Result{Bundle: &firstBundle}}, Executor: newAssembledBatchExecutor(),
})
if err != nil || first.Failed != 0 {
t.Fatalf("first result/error = %#v/%v", first, err)
}
secondBundle := assembledBatchBundle(t, "2026-05-31")
setWorkflowTemperatures(&secondBundle, 85)
second, err := RunBatchDetailed(context.Background(), BatchRequest{
Config: cfg, Batch: BatchMorning, Now: workflowTime("2026-05-29T08:30:00-05:00"),
Collector: &workflowCollector{result: &collect.Result{Bundle: &secondBundle}}, Executor: newAssembledBatchExecutor(),
})
if err != nil || second.Failed != 0 || len(second.Reports) != len(first.Reports) {
t.Fatalf("second result/error = %#v/%v", second, err)
}
for _, item := range second.Reports {
pkg := loadBatchDataPackage(t, item.DataPackagePath)
if len(pkg.RecentChanges.Items) == 0 {
t.Fatalf("report %s data package has no changes from prior snapshot", item.ReportID)
}
}
}
func assembledBatchConfig(t *testing.T, notify bool) config.Config { func assembledBatchConfig(t *testing.T, notify bool) config.Config {
t.Helper() t.Helper()
cfg := workflowConfig(t) cfg := workflowConfig(t)

View File

@@ -109,10 +109,6 @@ func (w *promptReportWorkflow) buildInputs() error {
return err return err
} }
w.result = &ReportResult{} w.result = &ReportResult{}
priorSnapshot, err := w.store.FindPriorSnapshot(w.ctx, w.req.Resolved)
if err != nil {
return err
}
w.reportFacts, err = BuildReportFacts(ModuleSnapshotRequest{Config: w.req.Config, Resolved: w.req.Resolved}, w.req.Collection.Bundle) w.reportFacts, err = BuildReportFacts(ModuleSnapshotRequest{Config: w.req.Config, Resolved: w.req.Resolved}, w.req.Collection.Bundle)
if err != nil { if err != nil {
return generatedReportError(w.req.Resolved, w.req.Resolved.Metadata().RunID, "build report facts", err) return generatedReportError(w.req.Resolved, w.req.Resolved.Metadata().RunID, "build report facts", err)
@@ -127,13 +123,6 @@ func (w *promptReportWorkflow) buildInputs() error {
} }
w.result.ModuleSnapshot = w.moduleSnapshot w.result.ModuleSnapshot = w.moduleSnapshot
w.result.ModuleSnapshotPath = moduleSnapshotPath w.result.ModuleSnapshotPath = moduleSnapshotPath
w.result.PriorSnapshot = priorSnapshot
recent, err := recentChanges(w.ctx, w.store, priorSnapshot, w.req.Resolved.Definition.ID, w.moduleSnapshot, w.req.Config.RecentChange)
if err != nil {
return err
}
w.result.RecentChanges = recent
w.briefingMetadata = briefing.BuildMetadata(briefingBuildContext(w.req.Config, w.req.Resolved, w.reportFacts.Collected)) w.briefingMetadata = briefing.BuildMetadata(briefingBuildContext(w.req.Config, w.req.Resolved, w.reportFacts.Collected))
w.metadata = state.BuildPromptMetadataFromBriefingMetadata(w.req.Resolved, w.briefingMetadata, state.ArtifactPaths{ w.metadata = state.BuildPromptMetadataFromBriefingMetadata(w.req.Resolved, w.briefingMetadata, state.ArtifactPaths{
ModuleSnapshot: moduleSnapshotPath, ModuleSnapshot: moduleSnapshotPath,
@@ -141,7 +130,7 @@ func (w *promptReportWorkflow) buildInputs() error {
}) })
w.result.Metadata = w.metadata w.result.Metadata = w.metadata
dataPackage, err := promptinput.Build(promptinput.BuildRequest{ dataPackage, err := promptinput.Build(promptinput.BuildRequest{
Metadata: promptMetadata(w.metadata), Modules: w.moduleSnapshot, RecentChanges: recent, Metadata: promptMetadata(w.metadata), Modules: w.moduleSnapshot,
}) })
if err != nil { if err != nil {
return w.reportError("build data package", err) return w.reportError("build data package", err)

View File

@@ -612,69 +612,6 @@ func workflowBundlePaths(id report.ID, validDate, runID string) []string {
} }
} }
func TestGenerateDetailedSelectsPriorSnapshotsForRetainedReports(t *testing.T) {
tests := []struct {
name string
kind ReportKind
id report.ID
date time.Time
raw string
wantPrior bool
wantRecentChanges bool
}{
{name: "daily", kind: ReportDaily, id: report.Daily, date: workflowTime("2026-05-29T12:00:00-05:00"), raw: validDailyWorkflowJSON(), wantPrior: true, wantRecentChanges: true},
{name: "today", kind: ReportToday, id: report.Today, raw: validTodayWorkflowJSON(), wantPrior: true, wantRecentChanges: true},
{name: "tomorrow", kind: ReportTomorrow, id: report.Tomorrow, raw: validTomorrowWorkflowJSON(), wantPrior: true, wantRecentChanges: true},
{name: "hourly", kind: ReportHourly, id: report.Hourly, raw: validHourlyWorkflowJSON()},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
cfg := workflowConfig(t)
cfg.Notify.Distributor.Enabled = false
definition := report.DefaultRegistry().MustLookup(test.id)
firstBundle := workflowBundle(t)
setWorkflowTemperatures(&firstBundle, 45)
first, err := GenerateDetailed(context.Background(), GenerateRequest{
Config: cfg, Report: test.kind, Date: test.date, Now: workflowTime("2026-05-29T08:00:00-05:00"),
Collector: &workflowCollector{result: &collect.Result{Bundle: &firstBundle}},
Executor: &workflowExecutor{definition: definition, raw: []byte(test.raw)},
})
if err != nil {
t.Fatalf("first GenerateDetailed() error = %v", err)
}
secondBundle := workflowBundle(t)
setWorkflowTemperatures(&secondBundle, 85)
second, err := GenerateDetailed(context.Background(), GenerateRequest{
Config: cfg, Report: test.kind, Date: test.date, Now: workflowTime("2026-05-29T08:30:00-05:00"),
Collector: &workflowCollector{result: &collect.Result{Bundle: &secondBundle}},
Executor: &workflowExecutor{definition: definition, raw: []byte(test.raw)},
})
if err != nil {
t.Fatalf("second GenerateDetailed() error = %v", err)
}
if test.wantPrior && (second.PriorSnapshot == nil || second.PriorSnapshot.Metadata.RunID != first.Metadata.RunID || second.PriorSnapshot.Metadata.ReportID != test.id) {
t.Fatalf("prior snapshot = %#v, want first %s run %q", second.PriorSnapshot, test.id, first.Metadata.RunID)
}
if !test.wantPrior && second.PriorSnapshot != nil {
t.Fatalf("prior snapshot = %#v, want none for non-overlapping rolling window", second.PriorSnapshot)
}
if (len(second.RecentChanges) > 0) != test.wantRecentChanges {
t.Fatalf("recent changes = %#v, want present %t", second.RecentChanges, test.wantRecentChanges)
}
if (len(second.DataPackage.RecentChanges.Items) > 0) != test.wantRecentChanges {
t.Fatalf("data package recent changes = %#v, want present %t", second.DataPackage.RecentChanges.Items, test.wantRecentChanges)
}
})
}
}
func setWorkflowTemperatures(bundle *weatherdata.Bundle, temperature float64) {
for index := range bundle.Hourly.Periods {
value := temperature
bundle.Hourly.Periods[index].TemperatureF = &value
}
}
func workflowConfig(t *testing.T) config.Config { func workflowConfig(t *testing.T) config.Config {
t.Helper() t.Helper()
cfg := config.Defaults() cfg := config.Defaults()

View File

@@ -2,7 +2,7 @@ You are WeatherReporter, a concise personal weather briefing writer.
You generate local weather forecast analysis from structured data packages prepared by the weatherreporter application. You generate local weather forecast analysis from structured data packages prepared by the weatherreporter application.
Use only the provided data package as your source of truth. Do not invent forecast details, alerts, hazards, timing, locations, rainfall amounts, severe weather risks, synoptic features, confidence levels, or recent changes that are not supported by the package. Use only the provided data package as your source of truth. Do not invent forecast details, alerts, hazards, timing, locations, rainfall amounts, severe weather risks, synoptic features, or confidence levels that are not supported by the package.
The reader is intelligent and weather-literate, but not a professional meteorologist. If asked to provide narrative analysis or commentary, write in plain, precise, meteorologically informed language. Avoid hype, filler, generic safety advice, and TV-weather style. Provide polished prose that avoids highly technical meteorological jargon or shorthand. The reader is intelligent and weather-literate, but not a professional meteorologist. If asked to provide narrative analysis or commentary, write in plain, precise, meteorologically informed language. Avoid hype, filler, generic safety advice, and TV-weather style. Provide polished prose that avoids highly technical meteorological jargon or shorthand.

View File

@@ -1,5 +1,5 @@
id: weather.daily_generated_text id: weather.daily_generated_text
version: "1.1.0" version: "2.0.0"
default_profile: weather-balanced default_profile: weather-balanced
description: Daily weather report analysis prompt. description: Daily weather report analysis prompt.
inputs: inputs:

View File

@@ -1,5 +1,5 @@
id: weather.hourly_generated_text id: weather.hourly_generated_text
version: "1.1.0" version: "2.0.0"
default_profile: weather-light default_profile: weather-light
description: Hourly weather report analysis prompt. description: Hourly weather report analysis prompt.
inputs: inputs:

View File

@@ -1,5 +1,5 @@
id: weather.today_generated_text id: weather.today_generated_text
version: "1.1.0" version: "2.0.0"
default_profile: weather-balanced default_profile: weather-balanced
description: Today's weather report analysis prompt. description: Today's weather report analysis prompt.
inputs: inputs:

View File

@@ -1,5 +1,5 @@
id: weather.tomorrow_generated_text id: weather.tomorrow_generated_text
version: "1.1.0" version: "2.0.0"
default_profile: weather-balanced default_profile: weather-balanced
description: Tomorrow's weather report analysis prompt. description: Tomorrow's weather report analysis prompt.
inputs: inputs:

View File

@@ -68,8 +68,8 @@ func TestPromptAssetsDeclareTheFourGeneratedTextPrompts(t *testing.T) {
if err := yaml.Unmarshal(data, &definition); err != nil { if err := yaml.Unmarshal(data, &definition); err != nil {
t.Fatalf("decode prompt definition: %v", err) t.Fatalf("decode prompt definition: %v", err)
} }
if definition.ID != tc.id || definition.Version != "1.1.0" || definition.DefaultProfile != tc.profile { if definition.ID != tc.id || definition.Version != "2.0.0" || definition.DefaultProfile != tc.profile {
t.Fatalf("definition = %#v, want %s version 1.1.0 and profile %s", definition, tc.id, tc.profile) t.Fatalf("definition = %#v, want %s version 2.0.0 and profile %s", definition, tc.id, tc.profile)
} }
if len(definition.Inputs) != 1 || definition.Inputs[0].Name != "data_package" || !definition.Inputs[0].Required || definition.Inputs[0].ContentType != "application/yaml" { if len(definition.Inputs) != 1 || definition.Inputs[0].Name != "data_package" || !definition.Inputs[0].Required || definition.Inputs[0].ContentType != "application/yaml" {
t.Fatalf("inputs = %#v, want one required YAML data_package", definition.Inputs) t.Fatalf("inputs = %#v, want one required YAML data_package", definition.Inputs)
@@ -140,11 +140,11 @@ func TestPromptkitInspectsEmbeddedPromptsOffline(t *testing.T) {
{"weather.hourly_generated_text", "weather-light", "deepseek/deepseek-v4-flash"}, {"weather.hourly_generated_text", "weather-light", "deepseek/deepseek-v4-flash"},
} { } {
t.Run(want.id, func(t *testing.T) { t.Run(want.id, func(t *testing.T) {
inspection, err := engine.InspectPrompt(context.Background(), want.id, "1.1.0") inspection, err := engine.InspectPrompt(context.Background(), want.id, "2.0.0")
if err != nil { if err != nil {
t.Fatalf("InspectPrompt() error = %v", err) t.Fatalf("InspectPrompt() error = %v", err)
} }
if inspection.PromptID != want.id || inspection.PromptVersion != "1.1.0" || inspection.DefaultProfileID != want.profile { if inspection.PromptID != want.id || inspection.PromptVersion != "2.0.0" || inspection.DefaultProfileID != want.profile {
t.Fatalf("inspection = %#v", inspection) t.Fatalf("inspection = %#v", inspection)
} }
profile, err := engine.InspectProfile(context.Background(), inspection.DefaultProfileID) profile, err := engine.InspectProfile(context.Background(), inspection.DefaultProfileID)

View File

@@ -7,7 +7,6 @@ import (
"fmt" "fmt"
"time" "time"
"gitea.maximumdirect.net/eric/weatherreporter/internal/changes"
"gitea.maximumdirect.net/eric/weatherreporter/internal/fileutil" "gitea.maximumdirect.net/eric/weatherreporter/internal/fileutil"
"gitea.maximumdirect.net/eric/weatherreporter/internal/module" "gitea.maximumdirect.net/eric/weatherreporter/internal/module"
"gitea.maximumdirect.net/eric/weatherreporter/internal/report" "gitea.maximumdirect.net/eric/weatherreporter/internal/report"
@@ -16,7 +15,7 @@ import (
"gopkg.in/yaml.v3" "gopkg.in/yaml.v3"
) )
const SchemaVersion = "weatherreporter.data_package.v3" const SchemaVersion = "weatherreporter.data_package.v4"
const ( const (
metadataStanza = "metadata" metadataStanza = "metadata"
@@ -54,7 +53,6 @@ var briefingStanzaCategories = map[string]string{
type BuildRequest struct { type BuildRequest struct {
Metadata Metadata Metadata Metadata
Modules module.Snapshot Modules module.Snapshot
RecentChanges []changes.Change
} }
type Metadata struct { type Metadata struct {
@@ -73,7 +71,6 @@ type Package struct {
RunID string `json:"runId" yaml:"run_id"` RunID string `json:"runId" yaml:"run_id"`
Report Report `json:"report" yaml:"report"` Report Report `json:"report" yaml:"report"`
Briefing BriefingStanzas `json:"briefing" yaml:"briefing"` Briefing BriefingStanzas `json:"briefing" yaml:"briefing"`
RecentChanges RecentChanges `json:"recentChanges" yaml:"recent_changes"`
SourceWarnings []weatherdata.SourceWarning `json:"sourceWarnings,omitempty" yaml:"source_warnings,omitempty"` SourceWarnings []weatherdata.SourceWarning `json:"sourceWarnings,omitempty" yaml:"source_warnings,omitempty"`
} }
@@ -92,20 +89,11 @@ type BriefingStanzas struct {
Values map[string]any `json:"-" yaml:"-"` Values map[string]any `json:"-" yaml:"-"`
} }
type RecentChanges struct {
Items []changes.Change `json:"items" yaml:"items"`
}
func Build(req BuildRequest) (Package, error) { func Build(req BuildRequest) (Package, error) {
localDate, err := currentLocalDate(req.Metadata.GeneratedAt, req.Metadata.Timezone) localDate, err := currentLocalDate(req.Metadata.GeneratedAt, req.Metadata.Timezone)
if err != nil { if err != nil {
return Package{}, err return Package{}, err
} }
items := make([]changes.Change, len(req.RecentChanges))
copy(items, req.RecentChanges)
if items == nil {
items = []changes.Change{}
}
pkg := Package{ pkg := Package{
SchemaVersion: SchemaVersion, SchemaVersion: SchemaVersion,
RunID: req.Metadata.RunID, RunID: req.Metadata.RunID,
@@ -119,7 +107,6 @@ func Build(req BuildRequest) (Package, error) {
ValidPeriod: req.Metadata.ValidPeriod, ValidPeriod: req.Metadata.ValidPeriod,
}, },
Briefing: stanzasFromSnapshot(req.Modules), Briefing: stanzasFromSnapshot(req.Modules),
RecentChanges: RecentChanges{Items: items},
SourceWarnings: append([]weatherdata.SourceWarning(nil), req.Metadata.SourceWarnings...), SourceWarnings: append([]weatherdata.SourceWarning(nil), req.Metadata.SourceWarnings...),
} }
if err := Validate(pkg); err != nil { if err := Validate(pkg); err != nil {

View File

@@ -34,9 +34,6 @@ func TestBuildDailyDataPackage(t *testing.T) {
if got := pkg.Briefing.Values["current_conditions"].(map[string]string)["condition_text"]; got != "Partly cloudy" { if got := pkg.Briefing.Values["current_conditions"].(map[string]string)["condition_text"]; got != "Partly cloudy" {
t.Fatalf("current_conditions.condition_text = %q, want Partly cloudy", got) t.Fatalf("current_conditions.condition_text = %q, want Partly cloudy", got)
} }
if pkg.RecentChanges.Items == nil || len(pkg.RecentChanges.Items) != 0 {
t.Fatalf("RecentChanges.Items = %#v, want empty slice", pkg.RecentChanges.Items)
}
} }
func TestBuildCurrentLocalDateUsesReportTimezone(t *testing.T) { func TestBuildCurrentLocalDateUsesReportTimezone(t *testing.T) {
@@ -181,7 +178,7 @@ func TestMarshalYAMLIsDeterministicAndGroupsNamedStanzas(t *testing.T) {
if string(first) != string(second) { if string(first) != string(second) {
t.Fatalf("YAML output changed between marshals:\n%s\n---\n%s", string(first), string(second)) t.Fatalf("YAML output changed between marshals:\n%s\n---\n%s", string(first), string(second))
} }
if !strings.Contains(string(first), "schema_version: weatherreporter.data_package.v3") || if !strings.Contains(string(first), "schema_version: weatherreporter.data_package.v4") ||
!strings.Contains(string(first), "briefing:\n") || !strings.Contains(string(first), "briefing:\n") ||
!strings.Contains(string(first), " applicable_risk_products:\n") || !strings.Contains(string(first), " applicable_risk_products:\n") ||
!strings.Contains(string(first), " derived_summaries:\n") || !strings.Contains(string(first), " derived_summaries:\n") ||
@@ -191,6 +188,9 @@ func TestMarshalYAMLIsDeterministicAndGroupsNamedStanzas(t *testing.T) {
!strings.Contains(string(first), " condition_text: Partly cloudy") { !strings.Contains(string(first), " condition_text: Partly cloudy") {
t.Fatalf("YAML output missing expected grouped stanzas:\n%s", string(first)) t.Fatalf("YAML output missing expected grouped stanzas:\n%s", string(first))
} }
if strings.Contains(string(first), "recent_changes") {
t.Fatalf("YAML output contains removed recent_changes stanza:\n%s", string(first))
}
for _, pair := range []struct { for _, pair := range []struct {
before string before string
after string after string
@@ -289,7 +289,7 @@ func TestMarshalYAMLRejectsUncategorizedStanza(t *testing.T) {
func TestLoadYAMLRejectsMisplacedStanza(t *testing.T) { func TestLoadYAMLRejectsMisplacedStanza(t *testing.T) {
data := []byte(` data := []byte(`
schema_version: weatherreporter.data_package.v3 schema_version: weatherreporter.data_package.v4
run_id: 20260529T100000Z_daily run_id: 20260529T100000Z_daily
report: report:
id: daily id: daily
@@ -306,8 +306,6 @@ briefing:
raw_data: raw_data:
alert_digest: alert_digest:
checked: true checked: true
recent_changes:
items: []
`) `)
_, err := LoadYAML(data) _, err := LoadYAML(data)
@@ -325,10 +323,10 @@ func TestLoadYAMLRejectsOldSchemaVersion(t *testing.T) {
if err != nil { if err != nil {
t.Fatalf("MarshalYAML() error = %v", err) t.Fatalf("MarshalYAML() error = %v", err)
} }
data = []byte(strings.Replace(string(data), "weatherreporter.data_package.v3", "weatherreporter.data_package.v2", 1)) data = []byte(strings.Replace(string(data), "weatherreporter.data_package.v4", "weatherreporter.data_package.v3", 1))
_, err = LoadYAML(data) _, err = LoadYAML(data)
if err == nil || !strings.Contains(err.Error(), "schemaVersion must be weatherreporter.data_package.v3") { if err == nil || !strings.Contains(err.Error(), "schemaVersion must be weatherreporter.data_package.v4") {
t.Fatalf("LoadYAML() error = %v, want current schema version error", err) t.Fatalf("LoadYAML() error = %v, want current schema version error", err)
} }
} }

View File

@@ -12,7 +12,7 @@ func dailyDefinition() Definition {
ID: Daily, ID: Daily,
Name: "Daily Report", Name: "Daily Report",
PromptID: "weather.daily_generated_text", PromptID: "weather.daily_generated_text",
PromptVersion: "1.1.0", PromptVersion: "2.0.0",
TemplateID: "daily", TemplateID: "daily",
GeneratedTextSchemaID: "daily", GeneratedTextSchemaID: "daily",
ComparisonStrategy: CompareSameValidDate, ComparisonStrategy: CompareSameValidDate,

View File

@@ -14,7 +14,7 @@ func hourlyDefinition() Definition {
ID: Hourly, ID: Hourly,
Name: "Hourly Report", Name: "Hourly Report",
PromptID: "weather.hourly_generated_text", PromptID: "weather.hourly_generated_text",
PromptVersion: "1.1.0", PromptVersion: "2.0.0",
TemplateID: "hourly", TemplateID: "hourly",
GeneratedTextSchemaID: "hourly", GeneratedTextSchemaID: "hourly",
ComparisonStrategy: CompareRollingWindow, ComparisonStrategy: CompareRollingWindow,

View File

@@ -51,8 +51,8 @@ func TestRegistryContainsOnlyPromptBackedReports(t *testing.T) {
} }
for _, definition := range definitions { for _, definition := range definitions {
if definition.PromptVersion != "1.1.0" { if definition.PromptVersion != "2.0.0" {
t.Fatalf("%s PromptVersion = %q, want 1.1.0", definition.ID, definition.PromptVersion) t.Fatalf("%s PromptVersion = %q, want 2.0.0", definition.ID, definition.PromptVersion)
} }
if definition.PromptID == "" { if definition.PromptID == "" {
t.Fatalf("%s PromptID is empty", definition.ID) t.Fatalf("%s PromptID is empty", definition.ID)

View File

@@ -10,7 +10,7 @@ func todayDefinition() Definition {
ID: Today, ID: Today,
Name: "Today Report", Name: "Today Report",
PromptID: "weather.today_generated_text", PromptID: "weather.today_generated_text",
PromptVersion: "1.1.0", PromptVersion: "2.0.0",
TemplateID: "today", TemplateID: "today",
GeneratedTextSchemaID: "today", GeneratedTextSchemaID: "today",
ComparisonStrategy: CompareSameValidDate, ComparisonStrategy: CompareSameValidDate,

View File

@@ -10,7 +10,7 @@ func tomorrowDefinition() Definition {
ID: Tomorrow, ID: Tomorrow,
Name: "Tomorrow Report", Name: "Tomorrow Report",
PromptID: "weather.tomorrow_generated_text", PromptID: "weather.tomorrow_generated_text",
PromptVersion: "1.1.0", PromptVersion: "2.0.0",
TemplateID: "tomorrow", TemplateID: "tomorrow",
GeneratedTextSchemaID: "tomorrow", GeneratedTextSchemaID: "tomorrow",
ComparisonStrategy: CompareSameValidDate, ComparisonStrategy: CompareSameValidDate,

View File

@@ -172,9 +172,9 @@ func validPreparationArtifact() PromptPreparationArtifact {
return PromptPreparationArtifact{ return PromptPreparationArtifact{
SchemaVersion: PromptPreparationSchemaVersion, Status: PromptPreparationSucceeded, SchemaVersion: PromptPreparationSchemaVersion, Status: PromptPreparationSucceeded,
ReportID: report.Daily, RunID: "weatherreporter-run", PromptID: "weather.daily_generated_text", ReportID: report.Daily, RunID: "weatherreporter-run", PromptID: "weather.daily_generated_text",
PromptVersion: "1.1.0", DataPackagePath: "/workspace/data.yaml", PromptVersion: "2.0.0", DataPackagePath: "/workspace/data.yaml",
Preparation: &promptexec.Preparation{ Preparation: &promptexec.Preparation{
PromptID: "weather.daily_generated_text", PromptVersion: "1.1.0", PromptID: "weather.daily_generated_text", PromptVersion: "2.0.0",
DataPackagePath: "/workspace/data.yaml", DataPackagePath: "/workspace/data.yaml",
}, },
StartedAt: started, EndedAt: started.Add(time.Second), Duration: time.Second, StartedAt: started, EndedAt: started.Add(time.Second), Duration: time.Second,
@@ -186,9 +186,9 @@ func validExecutionArtifact() PromptExecutionArtifact {
validation := promptexec.NewValidation(promptexec.ValidationPassed, "json_schema", "daily.generated_text.schema.json", nil) validation := promptexec.NewValidation(promptexec.ValidationPassed, "json_schema", "daily.generated_text.schema.json", nil)
return PromptExecutionArtifact{ return PromptExecutionArtifact{
SchemaVersion: PromptExecutionSchemaVersion, Status: PromptExecutionSucceeded, SchemaVersion: PromptExecutionSchemaVersion, Status: PromptExecutionSucceeded,
ReportID: report.Daily, RunID: "weatherreporter-run", PromptID: "weather.daily_generated_text", PromptVersion: "1.1.0", ReportID: report.Daily, RunID: "weatherreporter-run", PromptID: "weather.daily_generated_text", PromptVersion: "2.0.0",
Provenance: &PromptExecutionProvenance{ Provenance: &PromptExecutionProvenance{
RunID: "provider-run", PromptID: "weather.daily_generated_text", PromptVersion: "1.1.0", RunID: "provider-run", PromptID: "weather.daily_generated_text", PromptVersion: "2.0.0",
PromptHash: "prompt-hash", RenderedPromptHash: "rendered-hash", ProfileID: "profile", PromptHash: "prompt-hash", RenderedPromptHash: "rendered-hash", ProfileID: "profile",
BackendID: "backend", ModelName: "model", DataPackagePath: "/workspace/data.yaml", BackendID: "backend", ModelName: "model", DataPackagePath: "/workspace/data.yaml",
StartedAt: started, EndedAt: started.Add(time.Second), Duration: time.Second, StartedAt: started, EndedAt: started.Add(time.Second), Duration: time.Second,
@@ -201,7 +201,7 @@ func validFailedExecutionArtifact() PromptExecutionArtifact {
started := time.Date(2026, 5, 29, 15, 0, 0, 0, time.UTC) started := time.Date(2026, 5, 29, 15, 0, 0, 0, time.UTC)
return PromptExecutionArtifact{ return PromptExecutionArtifact{
SchemaVersion: PromptExecutionSchemaVersion, Status: PromptExecutionFailed, SchemaVersion: PromptExecutionSchemaVersion, Status: PromptExecutionFailed,
ReportID: report.Daily, RunID: "weatherreporter-run", PromptID: "weather.daily_generated_text", PromptVersion: "1.1.0", ReportID: report.Daily, RunID: "weatherreporter-run", PromptID: "weather.daily_generated_text", PromptVersion: "2.0.0",
StartedAt: started, EndedAt: started, Error: &PromptArtifactError{Category: promptexec.Generation, Message: "provider unavailable"}, StartedAt: started, EndedAt: started, Error: &PromptArtifactError{Category: promptexec.Generation, Message: "provider unavailable"},
} }
} }