Record effective sessions in debug provenance
This commit is contained in:
@@ -249,10 +249,24 @@ func TestRunAutoReusesPlanWhenRunInputsChange(t *testing.T) {
|
||||
}
|
||||
harness.mu.Lock()
|
||||
chunkCalls := harness.chunkCalls
|
||||
sessions := append([]string(nil), harness.sessionIDs...)
|
||||
harness.mu.Unlock()
|
||||
if chunkCalls != 1 {
|
||||
t.Fatalf("chunk calls across changed run inputs = %d, want 1", chunkCalls)
|
||||
}
|
||||
rawInput, err := os.ReadFile(roots.input)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
wantSessionID, err := resolvePromptSessionID("", "test/input", rawInput)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
for _, sessionID := range sessions {
|
||||
if sessionID != wantSessionID {
|
||||
t.Fatalf("session IDs across reference changes = %#v, want %q", sessions, wantSessionID)
|
||||
}
|
||||
}
|
||||
assertFile(t, filepath.Join(roots.plans, strings.TrimPrefix(stateTestDigest, "sha256:"), "plan.json"))
|
||||
assertAnyFile(t, roots.output)
|
||||
}
|
||||
|
||||
@@ -418,6 +418,10 @@ func runPipelineCommand(args []string, stdout, stderr io.Writer, opts Options) i
|
||||
if err != nil {
|
||||
return failPipelineCommand(stderr, commandState, terminalWriter, err)
|
||||
}
|
||||
invocation.SessionID = effectiveSessionID
|
||||
if err := writeSummary(summary, func() error { return summary.WriteInvocation(invocation) }); err != nil {
|
||||
return failPipelineCommand(stderr, commandState, terminalWriter, fmt.Errorf("write debug invocation metadata: %w", err))
|
||||
}
|
||||
chunkPlans, err := chunkPlanStoreForRun(effective.Config.Cache.ChunkPlans, opts)
|
||||
if err != nil {
|
||||
return failPipelineCommand(stderr, commandState, terminalWriter, err)
|
||||
|
||||
@@ -515,7 +515,7 @@ func TestRunSessionIDUsesEffectiveValueForPromptRequests(t *testing.T) {
|
||||
}
|
||||
}
|
||||
harness := newStateTestHarness()
|
||||
args := append([]string{"run", "sample", "--config", roots.config, "--input", roots.input, "--chunk_cache", "bypass"}, tt.args...)
|
||||
args := append([]string{"run", "sample", "--config", roots.config, "--input", roots.input, "--chunk_cache", "bypass", "--debug"}, tt.args...)
|
||||
var stdout, stderr bytes.Buffer
|
||||
code := RunWithOptions(args, &stdout, &stderr, harness.options())
|
||||
if code != 0 || stderr.Len() != 0 {
|
||||
@@ -532,6 +532,11 @@ func TestRunSessionIDUsesEffectiveValueForPromptRequests(t *testing.T) {
|
||||
t.Fatalf("session IDs = %#v, want %q", sessions, want)
|
||||
}
|
||||
}
|
||||
var manifest artifacts.RunManifest
|
||||
readStateTestSummaryJSON(t, onlyChildDir(t, roots.debug), "run-manifest.json", &manifest)
|
||||
if session, ok := manifest.Metadata["session_id"]; !ok || session != want {
|
||||
t.Fatalf("manifest session = %#v, want %q; metadata = %#v", session, want, manifest.Metadata)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -329,11 +329,25 @@ func TestRunUsesOneInjectedIdentityForDebugOutputAndManifest(t *testing.T) {
|
||||
if manifest.StartedAt == nil || !manifest.StartedAt.Equal(wantStartedAt) {
|
||||
t.Fatalf("manifest started at = %v, want %v", manifest.StartedAt, wantStartedAt)
|
||||
}
|
||||
rawInput, err := os.ReadFile(roots.input)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
wantSessionID, err := resolvePromptSessionID("", "test/input", rawInput)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if sessionID, ok := manifest.Metadata["session_id"]; !ok || sessionID != wantSessionID {
|
||||
t.Fatalf("manifest session = %#v, want %q; metadata = %#v", sessionID, wantSessionID, manifest.Metadata)
|
||||
}
|
||||
var invocation debugbundle.Invocation
|
||||
readStateTestSummaryJSON(t, debugPath, "invocation.json", &invocation)
|
||||
if invocation.RunID != runID || !invocation.StartedAt.Equal(wantStartedAt) {
|
||||
t.Fatalf("debug invocation identity = %#v, want run %q at %v", invocation, runID, wantStartedAt)
|
||||
}
|
||||
if invocation.SessionID != wantSessionID {
|
||||
t.Fatalf("debug invocation session = %q, want %q", invocation.SessionID, wantSessionID)
|
||||
}
|
||||
report := readStateTestRunReport(t, debugPath)
|
||||
if !report.Succeeded || report.RunID != runID || report.PipelineID != "sample" || report.OutputPath != outputPath || report.DebugPath != debugPath || report.OutputCount != 1 || report.RejectedCount != 0 || report.WarningCount != 0 || report.ValidationStatus != "approved" {
|
||||
t.Fatalf("success report = %#v", report)
|
||||
@@ -343,6 +357,45 @@ func TestRunUsesOneInjectedIdentityForDebugOutputAndManifest(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestRunCheckpointReuseRequiresSameEffectiveSession(t *testing.T) {
|
||||
roots := newStateTestRoots(t)
|
||||
harness := newStateTestHarness()
|
||||
run := func(extra ...string) stateTestResult {
|
||||
args := []string{"run", "sample", "--config", roots.config, "--input", roots.input, "--chunk_cache", "bypass"}
|
||||
args = append(args, extra...)
|
||||
var stdout, stderr bytes.Buffer
|
||||
return stateTestResult{code: RunWithOptions(args, &stdout, &stderr, harness.options()), stdout: stdout.String(), stderr: stderr.String()}
|
||||
}
|
||||
|
||||
if result := run(); result.code != 0 {
|
||||
t.Fatalf("initial run code=%d stdout=%q stderr=%q", result.code, result.stdout, result.stderr)
|
||||
}
|
||||
harness.mu.Lock()
|
||||
initialExtractCalls := harness.extractCalls
|
||||
harness.mu.Unlock()
|
||||
if initialExtractCalls != 1 {
|
||||
t.Fatalf("initial extract calls = %d, want 1", initialExtractCalls)
|
||||
}
|
||||
if result := run("--resume"); result.code != 0 {
|
||||
t.Fatalf("same-session resume code=%d stdout=%q stderr=%q", result.code, result.stdout, result.stderr)
|
||||
}
|
||||
harness.mu.Lock()
|
||||
reusedExtractCalls := harness.extractCalls
|
||||
harness.mu.Unlock()
|
||||
if reusedExtractCalls != initialExtractCalls {
|
||||
t.Fatalf("extract calls after same-session resume = %d, want %d", reusedExtractCalls, initialExtractCalls)
|
||||
}
|
||||
if result := run("--resume", "--session-id", "different-session"); result.code != 0 {
|
||||
t.Fatalf("different-session resume code=%d stdout=%q stderr=%q", result.code, result.stdout, result.stderr)
|
||||
}
|
||||
harness.mu.Lock()
|
||||
differentSessionExtractCalls := harness.extractCalls
|
||||
harness.mu.Unlock()
|
||||
if differentSessionExtractCalls != initialExtractCalls+1 {
|
||||
t.Fatalf("extract calls after different-session resume = %d, want %d", differentSessionExtractCalls, initialExtractCalls+1)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRunWritesTerminalArtifactsForResolutionPipelineAndOutputFailures(t *testing.T) {
|
||||
for _, tc := range []struct {
|
||||
name string
|
||||
|
||||
@@ -179,6 +179,30 @@ func TestWriteInvocationPreservesReasoningEffortOverrideStates(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestWriteInvocationOmitsEmptySessionID(t *testing.T) {
|
||||
for _, sessionID := range []string{"", "session-123"} {
|
||||
bundle, err := Allocate(t.TempDir(), testBundleRunID, time.Unix(0, 42))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := bundle.Summary().WriteInvocation(Invocation{Operation: "run", SessionID: sessionID}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
data, err := os.ReadFile(filepath.Join(bundle.SummaryRoot(), ArtifactInvocationMetadata))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
var payload map[string]any
|
||||
if err := json.Unmarshal(data, &payload); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
value, found := payload["session_id"]
|
||||
if found != (sessionID != "") || (found && value != sessionID) {
|
||||
t.Fatalf("session found=%t value=%#v, want found=%t value=%q; JSON=%s", found, value, sessionID != "", sessionID, data)
|
||||
}
|
||||
}
|
||||
}
|
||||
func TestSummaryWriterInternalWritesConfineArtifacts(t *testing.T) {
|
||||
bundle, err := Allocate(t.TempDir(), testBundleRunID, time.Unix(0, 42))
|
||||
if err != nil {
|
||||
|
||||
@@ -40,6 +40,7 @@ type Invocation struct {
|
||||
OnlyLanes []string `json:"only_lanes,omitempty"`
|
||||
ChunkCacheOverride string `json:"chunk_cache_override,omitempty"`
|
||||
ReasoningEffortOverride *string `json:"reasoning_effort_override,omitempty"`
|
||||
SessionID string `json:"session_id,omitempty"`
|
||||
RunID string `json:"run_id"`
|
||||
StartedAt time.Time `json:"started_at"`
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user