diff --git a/docs/roadmap/implementation.md b/docs/roadmap/implementation.md index 8d7bb5f..a64ffbb 100644 --- a/docs/roadmap/implementation.md +++ b/docs/roadmap/implementation.md @@ -412,6 +412,14 @@ Validation: focused domain/use-case tests when runnable and symbol searches for remaining label plumbing. Do not promise old hash values or remove source-version checks, collection release labels, or session metadata. +Completion: internal prompt definitions, identities, snapshots, execution, +inspection, smoke diagnostics, and observations no longer propagate prompt +labels. Label-only tests were removed while retained-field ownership and metadata +checks remain. Domain, observation, and prompt package tests passed, as did +focused runner and inspection tests with Go 1.27.0; symbol review and +`git diff --check` passed. Full use-case tests still have failing configuration +fixtures awaiting their scheduled list conversion; public facade cleanup follows. + ## Stage 10: Remove Public Prompt Labels And Serialized Fields Scope: `types.go`, `listing.go`, `requirements.go`, `observations.go`, diff --git a/internal/configuration/benchmark_test.go b/internal/configuration/benchmark_test.go index 8c9d5ea..2734212 100644 --- a/internal/configuration/benchmark_test.go +++ b/internal/configuration/benchmark_test.go @@ -262,7 +262,7 @@ func BenchmarkInspectionSelection(b *testing.B) { } ctx := context.Background() definition, hash, err := result.GetPromptSnapshot(ctx, "inspection") - if err != nil || definition.ID != "inspection" || definition.Version == nil || *definition.Version != "1" || hash == "" || + if err != nil || definition.ID != "inspection" || hash == "" || len(definition.Inputs) != 1 || definition.Inputs[0].Name != "transcript" || !definition.Inputs[0].Required { b.Fatalf("prompt lookup fixture is invalid: definition=%+v hash=%q err=%v", definition, hash, err) } @@ -272,7 +272,7 @@ func BenchmarkInspectionSelection(b *testing.B) { b.Fatalf("profile lookup fixture is invalid: profile=%+v err=%v", profile, err) } inspection, err := result.GetPromptInspection(ctx, "inspection") - if err != nil || inspection.PromptID != "inspection" || inspection.PromptVersion == nil || *inspection.PromptVersion != "1" || + if err != nil || inspection.PromptID != "inspection" || len(inspection.Inputs) != 1 || inspection.Inputs[0].Name != "transcript" { b.Fatalf("prompt inspection fixture is invalid: inspection=%+v err=%v", inspection, err) } diff --git a/internal/configuration/listing.go b/internal/configuration/listing.go index f8e7f55..3c1ea2e 100644 --- a/internal/configuration/listing.go +++ b/internal/configuration/listing.go @@ -77,9 +77,6 @@ func (state *loaded) ListPrompts(ctx context.Context) ([]domain.PromptIdentity, return nil, err } prompts := append([]domain.PromptIdentity(nil), state.promptIDs...) - for index := range prompts { - prompts[index].Version = stringPointerValue(prompts[index].Version) - } if err := ctx.Err(); err != nil { return nil, err } diff --git a/internal/configuration/listing_test.go b/internal/configuration/listing_test.go index 8a6e679..78a2a3b 100644 --- a/internal/configuration/listing_test.go +++ b/internal/configuration/listing_test.go @@ -48,9 +48,9 @@ func TestLoadedListsSortedEffectiveIdentities(t *testing.T) { t.Fatal(err) } wantPrompts := []domain.PromptIdentity{ - {ID: " z/prompt ", Version: stringPointer("v1")}, - {ID: "a/prompt", Version: stringPointer("v1")}, - {ID: "z/prompt", Version: stringPointer("v1")}, + {ID: " z/prompt "}, + {ID: "a/prompt"}, + {ID: "z/prompt"}, } if !reflect.DeepEqual(prompts, wantPrompts) { t.Fatalf("prompt identities = %#v, want %#v", prompts, wantPrompts) @@ -84,7 +84,6 @@ func TestLoadedListsAreIndependent(t *testing.T) { t.Fatal(err) } prompts[0].ID = "changed prompt" - *prompts[0].Version = "changed version" profiles, err = state.ListProfiles(context.Background()) if err != nil { @@ -101,7 +100,7 @@ func TestLoadedListsAreIndependent(t *testing.T) { if err != nil { t.Fatal(err) } - wantPrompts := []domain.PromptIdentity{{ID: "example", Version: stringPointer("v1")}} + wantPrompts := []domain.PromptIdentity{{ID: "example"}} if !reflect.DeepEqual(prompts, wantPrompts) { t.Fatalf("caller mutation changed prompt inventory: %#v", prompts) } diff --git a/internal/configuration/load.go b/internal/configuration/load.go index 71e6b8c..c182e63 100644 --- a/internal/configuration/load.go +++ b/internal/configuration/load.go @@ -274,7 +274,7 @@ func load(main configkit.Input, underlays []configkit.Underlay) (*loaded, error) state.backendIDs = sortedKeys(indexedBackends(&result.Config)) state.profileIDs = sortedKeys(state.indexedProfiles) for _, id := range sortedKeys(state.prompts) { - state.promptIDs = append(state.promptIDs, domain.PromptIdentity{ID: id, Version: stringPointerValue(state.prompts[id].definition.Version)}) + state.promptIDs = append(state.promptIDs, domain.PromptIdentity{ID: id}) } state.namespaces = map[string][]string{ "backend": resourceNamespaces(state.backendIDs), diff --git a/internal/configuration/load_test.go b/internal/configuration/load_test.go index 9842a62..887adfe 100644 --- a/internal/configuration/load_test.go +++ b/internal/configuration/load_test.go @@ -171,8 +171,8 @@ func TestLoadComposesLiteralKeysAndEffectivePresence(t *testing.T) { if !exists || collectionProfile.ID != "base/namespaced" || collectionProfile.ResolvedTarget == nil || collectionProfile.ResolvedTarget.BackendID != "base/local.v1" { t.Fatalf("collection profile snapshot = %+v, exists %t", collectionProfile, exists) } - if loaded.result.Config.Collections["invoice"].Prompts["summary"].Messages[0].Content == nil || loaded.result.Config.Collections["invoice"].Prompts["summary"].Version == nil || *loaded.result.Config.Collections["invoice"].Prompts["summary"].Version != "v1.0" { - t.Fatal("literal prompt ID and version were not decoded") + if loaded.result.Config.Collections["invoice"].Prompts["summary"].Messages[0].Content == nil { + t.Fatal("literal prompt content was not decoded") } if loaded.result.Config.Version == nil || *loaded.result.Config.Version != "1" || loaded.result.Config.Collections["base"].ReleaseVersion == nil || *loaded.result.Config.Collections["base"].ReleaseVersion != "" || diff --git a/internal/configuration/prompts.go b/internal/configuration/prompts.go index 4db79b1..7d127de 100644 --- a/internal/configuration/prompts.go +++ b/internal/configuration/prompts.go @@ -297,7 +297,6 @@ func (state *loaded) lookupPromptEntry(id string) (*promptEntry, error) { } func ownPromptDefinition(definition domain.PromptDefinition) *domain.PromptDefinition { - definition.Version = stringPointerValue(definition.Version) definition.Inputs = append([]domain.PromptInput(nil), definition.Inputs...) definition.Templates = append([]domain.PromptMessageTemplate(nil), definition.Templates...) for index := range definition.Templates { @@ -310,7 +309,6 @@ func ownPromptDefinition(definition domain.PromptDefinition) *domain.PromptDefin } func ownPromptInspection(inspection domain.PromptInspection) *domain.PromptInspection { - inspection.PromptVersion = stringPointerValue(inspection.PromptVersion) inspection.Inputs = append([]domain.PromptInput(nil), inspection.Inputs...) return &inspection } diff --git a/internal/configuration/prompts_test.go b/internal/configuration/prompts_test.go index cd035e6..9748e73 100644 --- a/internal/configuration/prompts_test.go +++ b/internal/configuration/prompts_test.go @@ -136,7 +136,7 @@ func TestLoadCapturesNativeContentFile(t *testing.T) { } } -func TestLoadRejectsMalformedStaticSyntaxAndCapturesOptionalVersion(t *testing.T) { +func TestLoadRejectsMalformedStaticSyntax(t *testing.T) { _, err := load(mapping("bad-template", map[string]any{"prompts": map[string]any{"example": map[string]any{ "messages": []any{map[string]any{"role": "user", "content": "{{if}}"}}, "output": map[string]any{"format": "text", "validation_mode": "basic"}, @@ -144,119 +144,6 @@ func TestLoadRejectsMalformedStaticSyntaxAndCapturesOptionalVersion(t *testing.T if err == nil || !strings.Contains(err.Error(), "invalid template syntax") { t.Fatalf("expected construction-time syntax error, got %v", err) } - state, err := load(mapping("version metadata", map[string]any{"prompts": map[string]any{"example": map[string]any{ - "version": "release label", - "messages": []any{map[string]any{"role": "user", "content": "one"}}, - "output": map[string]any{"format": "text", "validation_mode": "basic"}, - }}}), nil) - if err != nil { - t.Fatal(err) - } - definition, inspection, err := state.prompt("example") - if err != nil || definition.Version == nil || *definition.Version != "release label" || - inspection.PromptVersion == nil || *inspection.PromptVersion != "release label" { - t.Fatalf("optional version metadata was not retained: definition=%+v inspection=%+v err=%v", definition, inspection, err) - } -} - -func TestPromptVersionLabelPresenceAndOwnership(t *testing.T) { - tests := []struct { - name string - version any - include bool - wantPresent bool - wantValue string - }{ - {name: "absent"}, - {name: "null", include: true}, - {name: "empty", version: "", include: true, wantPresent: true}, - {name: "exact text", version: " Release/v1 ", include: true, wantPresent: true, wantValue: " Release/v1 "}, - } - - hashes := make(map[string]string, len(tests)) - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - definition := map[string]any{ - "messages": []any{map[string]any{"role": "user", "content": "hello"}}, - "output": map[string]any{"format": "text", "validation_mode": "basic"}, - } - if test.include { - definition["version"] = test.version - } - state, err := load(mapping("prompt version label", map[string]any{ - "prompts": map[string]any{"example": definition}, - }), nil) - if err != nil { - t.Fatal(err) - } - - captured, inspection, err := state.prompt("example") - if err != nil { - t.Fatal(err) - } - identities, err := state.ListPrompts(context.Background()) - if err != nil || len(identities) != 1 { - t.Fatalf("list prompt labels: %#v, %v", identities, err) - } - check := func(name string, value *string) { - t.Helper() - if !test.wantPresent { - if value != nil { - t.Errorf("%s label = %q, want absent", name, *value) - } - return - } - if value == nil || *value != test.wantValue { - t.Errorf("%s label = %v, want %q", name, value, test.wantValue) - } - } - check("definition", captured.Version) - check("inspection", inspection.PromptVersion) - check("listing", identities[0].Version) - if captured.Version != nil && (captured.Version == inspection.PromptVersion || captured.Version == identities[0].Version || inspection.PromptVersion == identities[0].Version) { - t.Fatal("returned prompt labels share mutable pointers") - } - hashes[test.name] = inspection.PromptHash - - if captured.Version != nil { - *captured.Version = "mutated definition" - } - if inspection.PromptVersion != nil { - *inspection.PromptVersion = "mutated inspection" - } - if identities[0].Version != nil { - *identities[0].Version = "mutated listing" - } - again, againInspection, err := state.prompt("example") - if err != nil { - t.Fatal(err) - } - check("captured definition after mutation", again.Version) - check("captured inspection after mutation", againInspection.PromptVersion) - }) - } - if hashes["absent"] == hashes["empty"] { - t.Fatal("absent and declared-empty labels produced the same definition hash") - } -} - -func TestPromptVersionNullClearsInheritedLabel(t *testing.T) { - prompt := map[string]any{ - "version": "base-label", - "messages": []any{map[string]any{"role": "user", "content": "hello"}}, - "output": map[string]any{"format": "text", "validation_mode": "basic"}, - } - state, err := load( - mapping("clear inherited label", map[string]any{"prompts": map[string]any{"example": map[string]any{"version": nil}}}), - []configkit.Underlay{{Inputs: []configkit.Input{mapping("base prompt", map[string]any{"prompts": map[string]any{"example": prompt}})}}}, - ) - if err != nil { - t.Fatal(err) - } - definition, inspection, err := state.prompt("example") - if err != nil || definition.Version != nil || inspection.PromptVersion != nil { - t.Fatalf("null overlay retained inherited label: definition=%+v inspection=%+v err=%v", definition, inspection, err) - } } func TestPromptInspectionLookupMatchesSnapshotSelectionAndOwnsInputs(t *testing.T) { @@ -272,7 +159,6 @@ func TestPromptInspectionLookupMatchesSnapshotSelectionAndOwnsInputs(t *testing. } } selectedPrompt := makePrompt("selected") - selectedPrompt["version"] = "v2" state, err := load(mapping("prompt inspection lookup", map[string]any{ "profiles": map[string]any{"partial": map[string]any{}}, "prompts": map[string]any{ @@ -292,8 +178,7 @@ func TestPromptInspectionLookupMatchesSnapshotSelectionAndOwnsInputs(t *testing. if err != nil { t.Fatalf("snapshot prompt ID: %v", err) } - if inspection.PromptID != definition.ID || inspection.PromptVersion == nil || definition.Version == nil || - *inspection.PromptVersion != *definition.Version || + if inspection.PromptID != definition.ID || inspection.PromptHash != hash || inspection.DefaultProfileID != "partial" || len(inspection.Inputs) != 1 || inspection.Inputs[0].Name != "document" || inspection.Inputs[0].ContentType != "text/plain" || inspection.Inputs[0].Description != "source document" { diff --git a/internal/domain/domain.go b/internal/domain/domain.go index de6b552..78a423d 100644 --- a/internal/domain/domain.go +++ b/internal/domain/domain.go @@ -80,7 +80,6 @@ type RunResult struct { RawOutput string Validation ValidationResult PromptID string - PromptVersion *string PromptHash string SessionID string RenderedPromptHash string @@ -100,7 +99,6 @@ type RunResult struct { // It must never include resolved API key values, model output, or validation data. type PreparedRun struct { PromptID string - PromptVersion *string PromptHash string SelectedProfileID string SelectedBackendID string @@ -134,16 +132,14 @@ type Artifact struct { Hash string } -// PromptIdentity identifies one configured prompt and its descriptive version label. +// PromptIdentity identifies one configured prompt. type PromptIdentity struct { - ID string - Version *string + ID string } // PromptDefinition represents a configured prompt execution definition. type PromptDefinition struct { ID string `yaml:"id"` - Version *string `yaml:"version"` DefaultProfile string `yaml:"default_profile"` Description string `yaml:"description"` SessionID string `yaml:"session_id" json:"session_id,omitempty"` @@ -156,7 +152,6 @@ type PromptDefinition struct { // PromptInspection is the resolved result of exact prompt inspection. type PromptInspection struct { PromptID string - PromptVersion *string PromptHash string DefaultProfileID string Inputs []PromptInput diff --git a/internal/domain/observation.go b/internal/domain/observation.go index 054b507..f9257fd 100644 --- a/internal/domain/observation.go +++ b/internal/domain/observation.go @@ -68,7 +68,6 @@ type ExecutionEvent struct { RunID string Time time.Time PromptID string - PromptVersion *string ProfileID string BackendID string Model string diff --git a/internal/domain/requirements.go b/internal/domain/requirements.go index 288440c..b7b2b1b 100644 --- a/internal/domain/requirements.go +++ b/internal/domain/requirements.go @@ -48,11 +48,10 @@ type RequirementIssue struct { // RequirementInspection contains the inventories discovered for one prompt. type RequirementInspection struct { - PromptID string - PromptVersion *string - ProfileID string - Inputs []InputRequirement - Variables []VariableRequirement - Complete bool - Issues []RequirementIssue + PromptID string + ProfileID string + Inputs []InputRequirement + Variables []VariableRequirement + Complete bool + Issues []RequirementIssue } diff --git a/internal/domain/smoke_diagnostics.go b/internal/domain/smoke_diagnostics.go index d2056c5..21d3efe 100644 --- a/internal/domain/smoke_diagnostics.go +++ b/internal/domain/smoke_diagnostics.go @@ -65,7 +65,6 @@ type SmokeTestTarget struct { BackendID string ProfileID string PromptID string - PromptVersion *string DefaultSourceBackendID string SelectionSource string Endpoint string @@ -180,7 +179,6 @@ func CloneSmokeTestRequest(request SmokeTestRequest) (SmokeTestRequest, error) { func CloneSmokeTestReport(report SmokeTestReport) (SmokeTestReport, error) { cloned := report cloned.Applicable = cloneBool(report.Applicable) - cloned.Target.PromptVersion = cloneString(report.Target.PromptVersion) cloned.Facts = cloneSmokeTestFacts(report.Facts) cloned.Timing = cloneSmokeTestTiming(report.Timing) var err error diff --git a/internal/domain/smoke_diagnostics_test.go b/internal/domain/smoke_diagnostics_test.go index a6abc72..3e198d3 100644 --- a/internal/domain/smoke_diagnostics_test.go +++ b/internal/domain/smoke_diagnostics_test.go @@ -77,11 +77,9 @@ func TestCloneSmokeTestReportOwnsMutableValues(t *testing.T) { reachable := true status := 200 wait := 3 * time.Millisecond - promptVersion := "v1" report := SmokeTestReport{ Applicable: &applicable, Target: SmokeTestTarget{ - PromptVersion: &promptVersion, EffectiveSettings: ExecutionTarget{ ExtraParams: map[string]any{"nested": map[string]any{"value": "original"}}, }, @@ -103,10 +101,9 @@ func TestCloneSmokeTestReportOwnsMutableValues(t *testing.T) { *cloned.Attempts[0].HTTPStatus = 503 *cloned.Attempts[0].Facts.EndpointReachable = false *cloned.Attempts[0].Timing.CapacityWait = time.Second - *cloned.Target.PromptVersion = "changed" cloned.Target.EffectiveSettings.ExtraParams["nested"].(map[string]any)["value"] = "changed" - if !applicable || !reachable || status != 200 || wait != 3*time.Millisecond || promptVersion != "v1" { + if !applicable || !reachable || status != 200 || wait != 3*time.Millisecond { t.Fatal("cloned report shared optional scalar pointers") } if got := report.Target.EffectiveSettings.ExtraParams["nested"].(map[string]any)["value"]; got != "original" { diff --git a/internal/observation/recorder.go b/internal/observation/recorder.go index 7302264..ba9b65e 100644 --- a/internal/observation/recorder.go +++ b/internal/observation/recorder.go @@ -33,11 +33,10 @@ type Recorder struct { // Selection contains resolved identities only. Unresolved fields stay empty. type Selection struct { - PromptID string - PromptVersion *string - ProfileID string - BackendID string - Model string + PromptID string + ProfileID string + BackendID string + Model string } // AttemptData describes one completed active-permit acquisition and optional @@ -104,7 +103,6 @@ func (r *Recorder) Start() { // UpdateSelection replaces the known resolved identities for later events. func (r *Recorder) UpdateSelection(selection Selection) { - selection.PromptVersion = copyStringPointer(selection.PromptVersion) r.selection = selection } @@ -180,19 +178,11 @@ func (r *Recorder) Finish(data FinishData) { func (r *Recorder) event(kind domain.ExecutionEventKind) domain.ExecutionEvent { return domain.ExecutionEvent{ Kind: kind, RunID: r.runID, Time: r.now(), PromptID: r.selection.PromptID, - PromptVersion: copyStringPointer(r.selection.PromptVersion), ProfileID: r.selection.ProfileID, + ProfileID: r.selection.ProfileID, BackendID: r.selection.BackendID, Model: r.selection.Model, } } -func copyStringPointer(value *string) *string { - if value == nil { - return nil - } - copy := *value - return © -} - func (r *Recorder) deliver(event domain.ExecutionEvent) { if r.observer != nil { r.observer(event) diff --git a/internal/observation/recorder_test.go b/internal/observation/recorder_test.go index b30813b..98c302d 100644 --- a/internal/observation/recorder_test.go +++ b/internal/observation/recorder_test.go @@ -50,8 +50,7 @@ func TestRecorderAccountsDispatchedAttemptsAndReportedUsage(t *testing.T) { var events []domain.ExecutionEvent recorder := newRecorder("run-1", now, func(event domain.ExecutionEvent) { events = append(events, event) }) recorder.Start() - version := "v1" - recorder.UpdateSelection(Selection{PromptID: "prompt", PromptVersion: &version, ProfileID: "profile", BackendID: "backend", Model: "model"}) + recorder.UpdateSelection(Selection{PromptID: "prompt", ProfileID: "profile", BackendID: "backend", Model: "model"}) recorder.Attempt(AttemptData{ Generation: 1, Attempt: 1, Dispatched: true, CapacityWait: 2 * time.Millisecond, GenerationDuration: 10 * time.Millisecond, diff --git a/internal/prompt/compiled_renderer_benchmark_test.go b/internal/prompt/compiled_renderer_benchmark_test.go index c7d5bde..0986319 100644 --- a/internal/prompt/compiled_renderer_benchmark_test.go +++ b/internal/prompt/compiled_renderer_benchmark_test.go @@ -52,7 +52,7 @@ func BenchmarkCompiledRendering(b *testing.B) { small := newCompiledRenderingBenchmarkCase(b, "small_message", "", []string{"Summary for {{.audience}}."}, &domain.PromptDefinition{ - ID: "compiled-render-benchmark", Version: stringPointerForTest("v1"), + ID: "compiled-render-benchmark", Templates: []domain.PromptMessageTemplate{{Role: "user", Content: "Summary for {{.audience}}."}}, }, nil, map[string]string{"audience": "project leads"}, len("Summary for project leads."), @@ -68,7 +68,7 @@ func BenchmarkCompiledRendering(b *testing.B) { } largeContent := `{{input "document"}}` largeDefinition := &domain.PromptDefinition{ - ID: "compiled-render-benchmark", Version: stringPointerForTest("v1"), + ID: "compiled-render-benchmark", Inputs: []domain.PromptInput{{Name: "document"}}, Templates: []domain.PromptMessageTemplate{{Role: "user", Content: largeContent}}, } @@ -91,7 +91,7 @@ func BenchmarkCompiledRendering(b *testing.B) { `{{input "document"}}|{{input "document"}}`, } repeatedDefinition := &domain.PromptDefinition{ - ID: "compiled-render-benchmark", Version: stringPointerForTest("v1"), SessionID: repeatedSession, + ID: "compiled-render-benchmark", SessionID: repeatedSession, Inputs: []domain.PromptInput{{Name: "document"}}, Templates: []domain.PromptMessageTemplate{ {Role: "system", Content: repeatedMessages[0]}, @@ -141,7 +141,7 @@ func BenchmarkRetainedRenderedOutput(b *testing.B) { bodyText := string(body) content := `{{input "document"}}` definition := &domain.PromptDefinition{ - ID: "retained-render-benchmark", Version: stringPointerForTest("v1"), + ID: "retained-render-benchmark", Inputs: []domain.PromptInput{{Name: "document"}}, Templates: []domain.PromptMessageTemplate{{Role: "user", Content: content}}, } diff --git a/internal/prompt/renderer_test.go b/internal/prompt/renderer_test.go index 3acdfc5..df9c16c 100644 --- a/internal/prompt/renderer_test.go +++ b/internal/prompt/renderer_test.go @@ -507,7 +507,6 @@ func TestGoRendererArtifactTextLifecycle(t *testing.T) { func TestCompiledRendererKeepsConcurrentRequestsIndependent(t *testing.T) { const ( promptID = "concurrent-render" - version = "v1" ) sessionTemplate, err := CompileTemplate("session", "session-{{.request}}") if err != nil { @@ -525,7 +524,7 @@ func TestCompiledRendererKeepsConcurrentRequestsIndependent(t *testing.T) { }, }) definition := &domain.PromptDefinition{ - ID: promptID, Version: stringPointerForTest(version), SessionID: "session-{{.request}}", + ID: promptID, SessionID: "session-{{.request}}", Inputs: []domain.PromptInput{{Name: "document"}}, Templates: []domain.PromptMessageTemplate{{Role: "user", Content: `request={{.request}};document={{input "document"}}`}}, } diff --git a/internal/prompt/requirements.go b/internal/prompt/requirements.go index 61063b0..8ba7525 100644 --- a/internal/prompt/requirements.go +++ b/internal/prompt/requirements.go @@ -384,7 +384,6 @@ func AnalyzeRequirements( result := domain.RequirementInspection{Complete: true} if definition != nil { result.PromptID = definition.ID - result.PromptVersion = copyString(definition.Version) } walker := &requirementWalker{ ctx: ctx, diff --git a/internal/prompt/requirements_test.go b/internal/prompt/requirements_test.go index f7da7bf..b66a073 100644 --- a/internal/prompt/requirements_test.go +++ b/internal/prompt/requirements_test.go @@ -108,7 +108,7 @@ func TestRequirementWalkerDoesNotRescanNestedUnsupportedNames(t *testing.T) { func TestAnalyzeRequirementsCollectsDeclaredAndLiteralInputs(t *testing.T) { description := "retained metadata" - definition := &domain.PromptDefinition{ID: "capture", Version: stringPointerForTest("v1")} + definition := &domain.PromptDefinition{ID: "capture"} snapshot := RequirementSnapshot{ Inputs: []InputMetadata{ {Name: "optional", ContentType: stringPointerForTest("text/plain"), Description: &description}, @@ -123,7 +123,7 @@ func TestAnalyzeRequirementsCollectsDeclaredAndLiteralInputs(t *testing.T) { if err != nil { t.Fatal(err) } - if result.PromptID != "capture" || result.PromptVersion == nil || *result.PromptVersion != "v1" || !result.Complete || len(result.Issues) != 0 { + if result.PromptID != "capture" || !result.Complete || len(result.Issues) != 0 { t.Fatalf("unexpected result identity or completeness: %+v", result) } wantNames := []string{"extra", "optional", "transcript", "unused"} diff --git a/internal/usecase/prepared_execution.go b/internal/usecase/prepared_execution.go index 5f2464c..7865dfb 100644 --- a/internal/usecase/prepared_execution.go +++ b/internal/usecase/prepared_execution.go @@ -128,7 +128,7 @@ func (r *Runner) RunPrepared(ctx context.Context, prepared *PreparedExecution) ( ctx = observation.WithRecorder(ctx, recorder) selected := payload.prepared recorder.UpdateSelection(observation.Selection{PromptID: selected.PromptID, - PromptVersion: selected.PromptVersion, ProfileID: selected.SelectedProfileID, + ProfileID: selected.SelectedProfileID, BackendID: selected.SelectedBackendID, Model: selected.EffectiveModelParams.Model}) recorder.Start() if idErr != nil { @@ -229,7 +229,6 @@ func clonePreparedRun(source *domain.PreparedRun) (*domain.PreparedRun, error) { } copied := *source - copied.PromptVersion = copyStringPointer(source.PromptVersion) extraParams, err := jsonvalue.CopyMap(source.EffectiveModelParams.ExtraParams) if err != nil { return nil, err diff --git a/internal/usecase/prepared_execution_test.go b/internal/usecase/prepared_execution_test.go index f511e44..58778d8 100644 --- a/internal/usecase/prepared_execution_test.go +++ b/internal/usecase/prepared_execution_test.go @@ -662,7 +662,6 @@ func TestRunnerPreparedExecutionDetachesTransferredSnapshotSources(t *testing.T) "properties": map[string]any{"value": map[string]any{"type": "string"}}, } definition := promptDef(domain.FormatJSON, domain.ValidationJSONSchema, 0) - wantPromptVersion := *definition.Version definition.Validation.SchemaPath = "schema.json" control := &domain.CacheControl{Type: domain.CacheControlEphemeral, TTL: "1h"} rendered := &domain.RenderedPrompt{Messages: []domain.RenderedMessage{{ @@ -698,7 +697,6 @@ func TestRunnerPreparedExecutionDetachesTransferredSnapshotSources(t *testing.T) } mutateExtra(profileExtras, "mutated profile source") mutateExtra(requestExtras, "mutated request source") - *definition.Version = "mutated definition label" rendered.Messages[0].Content = "mutated renderer source" control.TTL = "mutated renderer cache metadata" schemaDocument["properties"].(map[string]any)["value"].(map[string]any)["type"] = "integer" @@ -713,21 +711,15 @@ func TestRunnerPreparedExecutionDetachesTransferredSnapshotSources(t *testing.T) details.StructuredOutput.JSONSchema.Schema.(map[string]any)["properties"].(map[string]any)["value"].(map[string]any)["type"] != "string" { t.Fatalf("prepared details retained a source alias: %#v", details) } - if details.PromptVersion == nil || *details.PromptVersion != wantPromptVersion { - t.Fatalf("prepared details did not preserve the captured version: %+v", details) - } - *details.PromptVersion = "mutated details label" - if again := prepared.Details(); again == nil || again.PromptVersion == nil || *again.PromptVersion != wantPromptVersion { - t.Fatalf("details version mutation escaped into the retained snapshot: %+v", again) + details.Messages[0].Content = "mutated details" + if again := prepared.Details(); again == nil || len(again.Messages) != 1 || again.Messages[0].Content != "captured rendering" { + t.Fatalf("details mutation escaped into the retained snapshot: %+v", again) } result, err := runner.RunPrepared(context.Background(), prepared) if err != nil || result == nil || !result.Validation.IsValid || result.InputHashes["transcript"] != "captured-input-hash" { t.Fatalf("run prepared snapshot=(%+v, %v)", result, err) } - if result.PromptVersion == nil || *result.PromptVersion != wantPromptVersion { - t.Fatalf("details version mutation escaped into execution metadata: %+v", result) - } if len(llmClient.lastReq.Prompt.Messages) != 1 || llmClient.lastReq.StructuredOutput == nil || llmClient.lastReq.StructuredOutput.JSONSchema == nil || extraLabel(llmClient.lastReq.Target.ExtraParams) != wantLabel || @@ -737,7 +729,6 @@ func TestRunnerPreparedExecutionDetachesTransferredSnapshotSources(t *testing.T) t.Fatalf("execution snapshot retained a source alias: %#v", llmClient.lastReq) } if completedDetails := prepared.Details(); completedDetails == nil || - completedDetails.PromptVersion == nil || *completedDetails.PromptVersion != wantPromptVersion || len(completedDetails.Messages) != 1 || completedDetails.Messages[0].Content != "captured rendering" || extraLabel(completedDetails.EffectiveModelParams.ExtraParams) != wantLabel { t.Fatalf("completed details changed after source mutation: %#v", completedDetails) diff --git a/internal/usecase/prompt_inspection.go b/internal/usecase/prompt_inspection.go index 71b147e..82c0aa4 100644 --- a/internal/usecase/prompt_inspection.go +++ b/internal/usecase/prompt_inspection.go @@ -76,7 +76,6 @@ func (r *Runner) resolvePromptInspection(ctx context.Context, promptID string) ( definition := selection.definition return &domain.PromptInspection{ PromptID: definition.ID, - PromptVersion: copyStringPointer(definition.Version), PromptHash: selection.hash, DefaultProfileID: definition.DefaultProfile, Inputs: append([]domain.PromptInput(nil), definition.Inputs...), diff --git a/internal/usecase/prompt_inspection_test.go b/internal/usecase/prompt_inspection_test.go index 3ed6bf0..5fc85c3 100644 --- a/internal/usecase/prompt_inspection_test.go +++ b/internal/usecase/prompt_inspection_test.go @@ -52,7 +52,6 @@ func (r *promptMetadataRepository) GetPromptInspection( func TestRunnerInspectPromptResolvesOneDefinitionWithoutExecutionCollaborators(t *testing.T) { definition := &domain.PromptDefinition{ ID: "normalized.prompt", - Version: stringPtr("1.2.3"), DefaultProfile: "not-resolved", Inputs: []domain.PromptInput{ {Name: "document", Required: true, ContentType: "text/plain", Description: "Source document."}, @@ -79,8 +78,7 @@ func TestRunnerInspectPromptResolvesOneDefinitionWithoutExecutionCollaborators(t if repository.calls != 1 || repository.id != " prompt-id " { t.Fatalf("prompt lookup=(calls=%d id=%q), want one unchanged ID lookup", repository.calls, repository.id) } - if inspection.PromptID != definition.ID || inspection.PromptVersion == nil || definition.Version == nil || - *inspection.PromptVersion != *definition.Version || + if inspection.PromptID != definition.ID || inspection.PromptHash != wantHash || inspection.DefaultProfileID != definition.DefaultProfile || !reflect.DeepEqual(inspection.Inputs, definition.Inputs) || @@ -100,7 +98,7 @@ func TestRunnerInspectPromptResolvesOneDefinitionWithoutExecutionCollaborators(t func TestRunnerInspectPromptUsesOwnedMetadataWhenRepositorySupportsIt(t *testing.T) { repository := &promptMetadataRepository{inspection: domain.PromptInspection{ - PromptID: "captured.prompt", PromptVersion: stringPtr("v2"), PromptHash: "cached-prompt-hash", + PromptID: "captured.prompt", PromptHash: "cached-prompt-hash", DefaultProfileID: "partial-profile", Inputs: []domain.PromptInput{{Name: "document", Required: true, ContentType: "text/plain", Description: "Captured."}}, OutputContract: domain.OutputContract{Format: domain.FormatJSON, ValidationMode: domain.ValidationJSON}, @@ -108,7 +106,7 @@ func TestRunnerInspectPromptUsesOwnedMetadataWhenRepositorySupportsIt(t *testing runner := &Runner{promptDefs: repository} inspection, err := runner.InspectPrompt(context.Background(), " requested-id ") if err != nil || repository.lookupID != " requested-id " || - inspection.PromptID != "captured.prompt" || inspection.PromptVersion == nil || *inspection.PromptVersion != "v2" || + inspection.PromptID != "captured.prompt" || inspection.PromptHash != "cached-prompt-hash" || inspection.DefaultProfileID != "partial-profile" || !reflect.DeepEqual(inspection.Inputs, repository.inspection.Inputs) || inspection.OutputContract != repository.inspection.OutputContract { t.Fatalf("metadata inspection=(%+v, %v), lookup=%q", inspection, err, repository.lookupID) @@ -124,11 +122,11 @@ func TestRunnerInspectPromptUsesOwnedMetadataWhenRepositorySupportsIt(t *testing func TestRunnerInspectPromptUsesCapturedHashWhenMetadataCapabilityIsAbsent(t *testing.T) { repository := &requirementInspectionPromptRepository{ definition: &domain.PromptDefinition{ - ID: "captured.prompt", Version: stringPtr("v1"), Inputs: []domain.PromptInput{{Name: "document"}}, + ID: "captured.prompt", Inputs: []domain.PromptInput{{Name: "document"}}, }, } inspection, err := (&Runner{promptDefs: repository}).InspectPrompt(context.Background(), " requested-id ") - if err != nil || inspection.PromptID != "captured.prompt" || inspection.PromptVersion == nil || *inspection.PromptVersion != "v1" || + if err != nil || inspection.PromptID != "captured.prompt" || inspection.PromptHash != "captured-hash" || inspection.Inputs[0].Name != "document" { t.Fatalf("captured-hash fallback inspection=(%+v, %v)", inspection, err) } diff --git a/internal/usecase/requirements_inspection.go b/internal/usecase/requirements_inspection.go index b713dcb..77dcb27 100644 --- a/internal/usecase/requirements_inspection.go +++ b/internal/usecase/requirements_inspection.go @@ -69,7 +69,7 @@ func (r *Runner) InspectRequirements( if !ok { return nil, fmt.Errorf("%w: captured requirement snapshot is unavailable", ErrPromptLoad) } - definition := &domain.PromptDefinition{ID: selection.PromptID, Version: copyStringPointer(selection.PromptVersion)} + definition := &domain.PromptDefinition{ID: selection.PromptID} inspection, err := prompt.AnalyzeRequirements(ctx, definition, snapshot, req.Vars, directSessionID != "") if err != nil { return nil, fmt.Errorf("%w: %w", ErrPromptLoad, err) diff --git a/internal/usecase/requirements_inspection_test.go b/internal/usecase/requirements_inspection_test.go index 8a58046..2049549 100644 --- a/internal/usecase/requirements_inspection_test.go +++ b/internal/usecase/requirements_inspection_test.go @@ -88,7 +88,7 @@ func TestRunnerInspectRequirementsUsesCapturedSelectionWithoutExecutionWork(t *t if err != nil { t.Fatal(err) } - definition := &domain.PromptDefinition{ID: "selected", Version: stringPtr("v2"), DefaultProfile: "default-profile"} + definition := &domain.PromptDefinition{ID: "selected", DefaultProfile: "default-profile"} repository := &requirementInspectionPromptRepository{ definition: definition, snapshotFound: true, @@ -113,7 +113,7 @@ func TestRunnerInspectRequirementsUsesCapturedSelectionWithoutExecutionWork(t *t repository.snapshotCalls, repository.snapshotID) } if profiles.calls != 1 || profiles.id != "partial-profile" || inspection.PromptID != definition.ID || - inspection.PromptVersion == nil || *inspection.PromptVersion != *definition.Version || inspection.ProfileID != "partial-profile" || !inspection.Complete || + inspection.ProfileID != "partial-profile" || !inspection.Complete || len(inspection.Inputs) != 1 || inspection.Inputs[0].Name != "message-input" || len(inspection.Variables) != 1 || inspection.Variables[0].Name != "caller" || !inspection.Variables[0].Supplied { t.Fatalf("unexpected requirement inspection: %+v", inspection) @@ -138,7 +138,7 @@ func TestRunnerInspectRequirementsUsesPromptMetadataAndExactRequirementSnapshot( promptRepository := &requirementPromptMetadataRepository{ requirementInspectionPromptRepository: baseRepository, inspection: domain.PromptInspection{ - PromptID: "captured-id", PromptVersion: stringPtr("captured-version"), PromptHash: "captured-hash", + PromptID: "captured-id", PromptHash: "captured-hash", DefaultProfileID: "default-profile", }, } @@ -155,7 +155,7 @@ func TestRunnerInspectRequirementsUsesPromptMetadataAndExactRequirementSnapshot( if err != nil || promptRepository.lookupID != " requested-id " || baseRepository.snapshotID != "captured-id" || profiles.identityID != "default-profile" || inspection.PromptID != "captured-id" || - inspection.PromptVersion == nil || *inspection.PromptVersion != "captured-version" || inspection.ProfileID != "default-profile" || + inspection.ProfileID != "default-profile" || !inspection.Complete || len(inspection.Inputs) != 1 || inspection.Inputs[0].Name != "message-input" || len(inspection.Variables) != 1 || inspection.Variables[0].Name != "caller" || !inspection.Variables[0].Supplied { t.Fatalf("metadata-based requirement inspection=(%+v, %v), prompt lookup=%q, snapshot=%q, profile=%q", @@ -168,7 +168,7 @@ func TestRunnerInspectRequirementsUsesProfileIdentityCapabilityOrFallback(t *tes if err != nil { t.Fatal(err) } - definition := &domain.PromptDefinition{ID: "selected", Version: stringPtr("v1")} + definition := &domain.PromptDefinition{ID: "selected"} makePromptRepository := func() *requirementInspectionPromptRepository { return &requirementInspectionPromptRepository{ definition: definition, snapshotFound: true, @@ -234,7 +234,7 @@ func TestRunnerInspectRequirementsUsesPromptDefaultAndIncludesSessionWhenNotBypa if err != nil { t.Fatal(err) } - definition := &domain.PromptDefinition{ID: "selected", Version: stringPtr("v1"), DefaultProfile: "default-profile"} + definition := &domain.PromptDefinition{ID: "selected", DefaultProfile: "default-profile"} repository := &requirementInspectionPromptRepository{ definition: definition, snapshotFound: true, snapshot: prompt.RequirementSnapshot{Templates: prompt.CompiledTemplates{Messages: []*template.Template{message}, Session: session}}, @@ -260,7 +260,7 @@ func TestRunnerInspectRequirementsReturnsIncompleteDiscoveryAsSuccess(t *testing t.Fatal(err) } repository := &requirementInspectionPromptRepository{ - definition: &domain.PromptDefinition{ID: "selected", Version: stringPtr("v1"), DefaultProfile: "profile"}, + definition: &domain.PromptDefinition{ID: "selected", DefaultProfile: "profile"}, snapshotFound: true, snapshot: prompt.RequirementSnapshot{Templates: prompt.CompiledTemplates{Messages: []*template.Template{compiled}}}, } diff --git a/internal/usecase/resolved_targets_test.go b/internal/usecase/resolved_targets_test.go index 50a469d..fd11b6f 100644 --- a/internal/usecase/resolved_targets_test.go +++ b/internal/usecase/resolved_targets_test.go @@ -29,7 +29,7 @@ func TestPreparationCompletesResolvedTargetThroughRequestOverrides(t *testing.T) Temperature: 0, ExtraParams: map[string]any{"configured": true}, }, TargetPresence: domain.ExecutionTargetPresence{Temperature: true}} runner := &Runner{ - promptDefs: staticPromptRepo{def: &domain.PromptDefinition{ID: "prompt", Version: stringPtr("v1"), Validation: domain.OutputContract{ + promptDefs: staticPromptRepo{def: &domain.PromptDefinition{ID: "prompt", Validation: domain.OutputContract{ Format: domain.FormatText, ValidationMode: domain.ValidationNone, }}}, profiles: staticExecutionProfileRepo{profile: profile}, renderer: staticRenderer{}, @@ -63,7 +63,7 @@ func TestResolvedTargetCredentialAvailability(t *testing.T) { Endpoint: "https://provider.example/v1", Model: "model", APIKeyRequired: true, }} runner := &Runner{ - promptDefs: staticPromptRepo{def: &domain.PromptDefinition{ID: "prompt", Version: stringPtr("v1"), Validation: domain.OutputContract{ + promptDefs: staticPromptRepo{def: &domain.PromptDefinition{ID: "prompt", Validation: domain.OutputContract{ Format: domain.FormatText, ValidationMode: domain.ValidationNone, }}}, profiles: staticExecutionProfileRepo{profile: profile}, renderer: staticRenderer{}, diff --git a/internal/usecase/runner.go b/internal/usecase/runner.go index fd2bd69..4868684 100644 --- a/internal/usecase/runner.go +++ b/internal/usecase/runner.go @@ -252,7 +252,6 @@ func (r *Runner) executePreparedRun( RawOutput: genResp.Content, Validation: validationResult, PromptID: prepared.PromptID, - PromptVersion: copyStringPointer(prepared.PromptVersion), PromptHash: prepared.PromptHash, SessionID: prepared.SessionID, RenderedPromptHash: prepared.RenderedPromptHash, @@ -321,7 +320,7 @@ func (r *Runner) resolvePreparation( } def := promptSelection.definition if recorder := observation.FromContext(ctx); recorder != nil { - recorder.UpdateSelection(observation.Selection{PromptID: def.ID, PromptVersion: copyStringPointer(def.Version)}) + recorder.UpdateSelection(observation.Selection{PromptID: def.ID}) } promptDefinitionHash := promptSelection.hash effectiveContract, err := resolveOutputContract(def, req.Validation) @@ -342,7 +341,7 @@ func (r *Runner) resolvePreparation( return nil, err } if recorder := observation.FromContext(ctx); recorder != nil { - recorder.UpdateSelection(observation.Selection{PromptID: def.ID, PromptVersion: copyStringPointer(def.Version), ProfileID: selection.id}) + recorder.UpdateSelection(observation.Selection{PromptID: def.ID, ProfileID: selection.id}) } effectiveModel, targetPresence := resolveExecutionTarget(selection.profile, req.Execution) @@ -355,7 +354,7 @@ func (r *Runner) resolvePreparation( return nil, fmt.Errorf("%w: %w", ErrInvalidRequest, err) } if recorder := observation.FromContext(ctx); recorder != nil { - recorder.UpdateSelection(observation.Selection{PromptID: def.ID, PromptVersion: copyStringPointer(def.Version), + recorder.UpdateSelection(observation.Selection{PromptID: def.ID, ProfileID: selection.id, BackendID: effectiveModel.BackendID, Model: effectiveModel.Model}) } @@ -481,7 +480,6 @@ func (r *Runner) completePreparationWithStructuredOutput( effectiveModel.APIKey = "" return &domain.PreparedRun{ PromptID: state.definition.ID, - PromptVersion: copyStringPointer(state.definition.Version), PromptHash: state.promptDefinitionHash, SelectedProfileID: state.selectedProfileID, SelectedBackendID: state.effectiveModel.BackendID, @@ -713,11 +711,3 @@ func buildOutputArtifact(content string, format domain.OutputFormat) domain.Arti func hashPromptDefinition(def *domain.PromptDefinition) (string, error) { return promptdef.HashDefinition(def) } - -func copyStringPointer(value *string) *string { - if value == nil { - return nil - } - copy := *value - return © -} diff --git a/internal/usecase/runner_test.go b/internal/usecase/runner_test.go index 3e8cf06..e765072 100644 --- a/internal/usecase/runner_test.go +++ b/internal/usecase/runner_test.go @@ -366,7 +366,7 @@ func TestRunnerPrepareWithExplicitProfileSelection(t *testing.T) { if err != nil { t.Fatalf("expected no error, got %v", err) } - if prepared.PromptID != "p" || prepared.PromptVersion == nil || *prepared.PromptVersion != "1" { + if prepared.PromptID != "p" { t.Fatalf("unexpected prepared prompt metadata: %+v", prepared) } if prepared.SelectedProfileID != "exec" { @@ -1257,7 +1257,7 @@ func TestRunnerRunSuccessful(t *testing.T) { if err != nil { t.Fatalf("expected no error, got %v", err) } - if res.PromptID != "p" || res.PromptVersion == nil || *res.PromptVersion != "1" { + if res.PromptID != "p" { t.Fatalf("unexpected prompt metadata: %+v", res) } if res.SelectedProfileID != "exec" { @@ -2504,7 +2504,6 @@ func TestBuildOutputArtifactDefaults(t *testing.T) { func promptDef(format domain.OutputFormat, mode domain.ValidationMode, attempts int) *domain.PromptDefinition { return &domain.PromptDefinition{ ID: "p", - Version: stringPtr("1"), DefaultProfile: "exec", Inputs: []domain.PromptInput{{Name: "transcript", Required: true}}, Templates: []domain.PromptMessageTemplate{{Role: "user", Content: "x"}}, diff --git a/internal/usecase/smoke_selection.go b/internal/usecase/smoke_selection.go index 5b5706b..41a0dc7 100644 --- a/internal/usecase/smoke_selection.go +++ b/internal/usecase/smoke_selection.go @@ -129,7 +129,6 @@ func (r *Runner) resolveSmokeTestTarget(ctx context.Context, req domain.SmokeTes if promptInspection != nil { selection.target.PromptID = promptInspection.PromptID - selection.target.PromptVersion = promptInspection.PromptVersion } selection.target.Endpoint, err = resolveSmokeTestEndpoint(req.Kind, selection.target.EffectiveSettings.Endpoint, selection.backend.SmokeTests) if err != nil {