Verify ownership across execution and repair

This commit is contained in:
2026-11-05 20:14:37 +00:00
parent 5dd1418953
commit b3b5ec3820
2 changed files with 114 additions and 0 deletions

View File

@@ -410,6 +410,17 @@ configured-default/runtime-override diagnostics through the public facade.
## Stage 15: Verify Cross-Path Ownership And Runtime Compatibility ## Stage 15: Verify Cross-Path Ownership And Runtime Compatibility
**Completed:** Cross-path checks cover mutations by an injected client on both
initial and repair requests, detached results and details, concurrent details
with claim/discard, and typed, numeric, null, cyclic, unsupported, and deep
runtime provider values. Remaining validating copies serve configuration
acceptance/overlay or public override acceptance; outward projections and schema
detachment remain ownership boundaries. Go 1.27.3 `go test -race
./internal/usecase ./internal/configuration ./internal/jsonvalue . -run
'Prepared|Provider|Extra|Smoke|Profile|Schema'` passed (the filtered jsonvalue
package selected no tests); the full `go test -race ./internal/jsonvalue` passed
separately.
**Findings:** 5–8. **Scope:** focused existing public and package contract tests. **Findings:** 5–8. **Scope:** focused existing public and package contract tests.
1. Reconcile the ownership inventory with the delivered call paths. Verify that 1. Reconcile the ownership inventory with the delivered call paths. Verify that

View File

@@ -307,6 +307,95 @@ func TestPreparedExecutionRepairsEmptyBasicOutput(t *testing.T) {
} }
} }
func TestPreparedExecutionIsolatesMutatingClientAcrossRepair(t *testing.T) {
client := &mutatingRepairClient{}
engine, err := promptkit.NewEngine(preparedContractConfig("original message", false),
promptkit.WithSchemaFS(preparedSchemaSource(), "."), promptkit.WithLLMClient(client))
if err != nil {
t.Fatal(err)
}
prepared, err := engine.PrepareExecution(context.Background(), promptkit.RunRequest{
PromptID: "prepared",
Execution: &promptkit.ExecutionTargetOverride{ExtraParams: map[string]any{
"nested": map[string]any{"value": "original"},
}},
Validation: &promptkit.OutputContract{Format: promptkit.FormatJSON, ValidationMode: promptkit.ValidationJSONSchema, SchemaPath: "schema.json", RepairAttempts: 1},
AppendedMessages: []promptkit.RenderedMessage{{Role: "user", Content: "appended", CacheControl: &promptkit.CacheControl{Type: promptkit.CacheControlEphemeral, TTL: "1h"}}},
})
if err != nil {
t.Fatal(err)
}
before := prepared.Details()
result, err := engine.RunPrepared(context.Background(), prepared)
if err != nil || result == nil || result.Validation.Status != promptkit.ValidationPassed || client.calls != 2 {
t.Fatalf("run result = %+v, error = %v, calls = %d", result, err, client.calls)
}
if !reflect.DeepEqual(before, prepared.Details()) {
t.Fatal("client mutation changed retained prepared metadata")
}
result.EffectiveModelParams.ExtraParams["nested"].(map[string]any)["value"] = "result mutation"
if prepared.Details().EffectiveModelParams.ExtraParams["nested"].(map[string]any)["value"] != "original" {
t.Fatal("result mutation changed retained prepared metadata")
}
}
type mutatingRepairClient struct{ calls int }
func (c *mutatingRepairClient) Generate(_ context.Context, request promptkit.GenerateRequest) (*promptkit.GenerateResponse, error) {
c.calls++
if len(request.Prompt.Messages) < 2 || request.Prompt.Messages[0].Content != "original message" ||
request.Prompt.Messages[1].Content != "appended" || request.Prompt.Messages[1].CacheControl == nil ||
request.Prompt.Messages[1].CacheControl.TTL != "1h" ||
request.Target.ExtraParams["nested"].(map[string]any)["value"] != "original" ||
request.StructuredOutput.JSONSchema.Schema.(map[string]any)["title"] != "original root" {
return nil, fmt.Errorf("generation %d received mutated request", c.calls)
}
request.Prompt.Messages[0].Content = "client mutation"
request.Prompt.Messages[1].CacheControl.TTL = "client mutation"
request.Target.ExtraParams["nested"].(map[string]any)["value"] = "client mutation"
request.StructuredOutput.JSONSchema.Schema.(map[string]any)["title"] = "client mutation"
if c.calls == 1 {
return &promptkit.GenerateResponse{Content: `{"value":"invalid"}`}, nil
}
return &promptkit.GenerateResponse{Content: `{"value":3}`}, nil
}
type runtimeLabels []string
type runtimeCounts map[string]int
func TestPreparedExecutionRuntimeProviderValues(t *testing.T) {
engine := newPreparedContractEngine(t, &preparedRecordingClient{response: &promptkit.GenerateResponse{Content: "ok"}}, "message")
labels := runtimeLabels{"original"}
counts := runtimeCounts{"count": 3}
request := promptkit.RunRequest{PromptID: "prepared", Execution: &promptkit.ExecutionTargetOverride{ExtraParams: map[string]any{
"labels": labels, "counts": counts, "number": json.Number("9007199254740993"), "null": nil,
}}}
prepared, err := engine.PrepareExecution(context.Background(), request)
if err != nil {
t.Fatal(err)
}
labels[0], counts["count"] = "changed", 9
details := prepared.Details().EffectiveModelParams.ExtraParams
if details["labels"].(runtimeLabels)[0] != "original" || details["counts"].(runtimeCounts)["count"] != 3 ||
details["number"] != json.Number("9007199254740993") || details["null"] != nil {
t.Fatalf("runtime values changed: %#v", details)
}
cycle := map[string]any{}
cycle["self"] = cycle
var deep any = "leaf"
for range 150 {
deep = map[string]any{"child": deep}
}
for name, invalid := range map[string]any{"cyclic": cycle, "unsupported": func() {}, "too deep": deep} {
t.Run(name, func(t *testing.T) {
_, err := engine.PrepareExecution(context.Background(), promptkit.RunRequest{PromptID: "prepared", Execution: &promptkit.ExecutionTargetOverride{ExtraParams: map[string]any{"value": invalid}}})
if !errors.Is(err, promptkit.ErrInvalidRequest) {
t.Fatalf("invalid runtime provider value error = %v", err)
}
})
}
}
func TestPreparedExecutionConcurrentClaimAllowsOneGeneration(t *testing.T) { func TestPreparedExecutionConcurrentClaimAllowsOneGeneration(t *testing.T) {
release := make(chan struct{}) release := make(chan struct{})
client := &preparedRecordingClient{ client := &preparedRecordingClient{
@@ -383,6 +472,7 @@ func TestPreparedExecutionRunAndDiscardRaceHasOneWinner(t *testing.T) {
} }
runOutcome := make(chan outcome, 1) runOutcome := make(chan outcome, 1)
discardDone := make(chan struct{}) discardDone := make(chan struct{})
detailsDone := make(chan bool, 1)
go func() { go func() {
<-start <-start
@@ -394,10 +484,23 @@ func TestPreparedExecutionRunAndDiscardRaceHasOneWinner(t *testing.T) {
prepared.Discard() prepared.Discard()
close(discardDone) close(discardDone)
}() }()
go func() {
<-start
for range 20 {
if prepared.Details().PromptID != "prepared" {
detailsDone <- false
return
}
}
detailsDone <- true
}()
close(start) close(start)
runResult := <-runOutcome runResult := <-runOutcome
<-discardDone <-discardDone
if !<-detailsDone {
t.Fatalf("attempt %d lost details during lifecycle race", i)
}
calls := len(client.snapshot()) calls := len(client.snapshot())
switch { switch {
case runResult.err == nil: case runResult.err == nil: