Verify ownership across execution and repair
This commit is contained in:
@@ -410,6 +410,17 @@ configured-default/runtime-override diagnostics through the public facade.
|
||||
|
||||
## 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.
|
||||
|
||||
1. Reconcile the ownership inventory with the delivered call paths. Verify that
|
||||
|
||||
@@ -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) {
|
||||
release := make(chan struct{})
|
||||
client := &preparedRecordingClient{
|
||||
@@ -383,6 +472,7 @@ func TestPreparedExecutionRunAndDiscardRaceHasOneWinner(t *testing.T) {
|
||||
}
|
||||
runOutcome := make(chan outcome, 1)
|
||||
discardDone := make(chan struct{})
|
||||
detailsDone := make(chan bool, 1)
|
||||
|
||||
go func() {
|
||||
<-start
|
||||
@@ -394,10 +484,23 @@ func TestPreparedExecutionRunAndDiscardRaceHasOneWinner(t *testing.T) {
|
||||
prepared.Discard()
|
||||
close(discardDone)
|
||||
}()
|
||||
go func() {
|
||||
<-start
|
||||
for range 20 {
|
||||
if prepared.Details().PromptID != "prepared" {
|
||||
detailsDone <- false
|
||||
return
|
||||
}
|
||||
}
|
||||
detailsDone <- true
|
||||
}()
|
||||
|
||||
close(start)
|
||||
runResult := <-runOutcome
|
||||
<-discardDone
|
||||
if !<-detailsDone {
|
||||
t.Fatalf("attempt %d lost details during lifecycle race", i)
|
||||
}
|
||||
calls := len(client.snapshot())
|
||||
switch {
|
||||
case runResult.err == nil:
|
||||
|
||||
Reference in New Issue
Block a user