package taskloop import ( "context" "encoding/json" "io" "os" "os/exec" "path/filepath" "strings" "testing" "time" "iop/packages/go/agentconfig" "iop/packages/go/agentguard" "iop/packages/go/agentpolicy" clistatus "iop/packages/go/agentprovider/cli/status" "iop/packages/go/agentruntime" "iop/packages/go/agenttask" ) func TestProviderEnvironmentConfinesCodexRuntimeState(t *testing.T) { sourceHome := canonicalTempDir(t) for _, name := range []string{"auth.json", "config.toml"} { if err := os.WriteFile( filepath.Join(sourceHome, name), []byte(name+"\n"), 0o600, ); err != nil { t.Fatalf("write %s: %v", name, err) } } t.Setenv("CODEX_HOME", sourceHome) taskRoot := canonicalTempDir(t) binding := agenttask.ConfinementBinding{ TaskRoot: taskRoot, WorkingDir: filepath.Join(taskRoot, "view"), WritableRoots: []string{ filepath.Join(taskRoot, "view"), filepath.Join(taskRoot, "tmp"), filepath.Join(taskRoot, "cache"), }, } for _, root := range binding.WritableRoots { if err := os.MkdirAll(root, 0o700); err != nil { t.Fatalf("create writable root: %v", err) } } resolved := agentconfig.ResolvedProfile{ Provider: agentconfig.Provider{ID: "codex"}, Profile: agentconfig.Profile{Env: []string{"FIXTURE=present"}}, } environment, err := providerEnvironment( resolved, binding, binding.WorkingDir, ) if err != nil { t.Fatalf("providerEnvironment: %v", err) } values := make(map[string]string) for _, entry := range environment { key, value, found := strings.Cut(entry, "=") if found { values[key] = value } } codexHome := filepath.Join(binding.WritableRoots[2], "codex-home") if values["CODEX_HOME"] != codexHome || values["TMPDIR"] != binding.WritableRoots[1] || values["TMP"] != binding.WritableRoots[1] || values["TEMP"] != binding.WritableRoots[1] || values["PWD"] != binding.WorkingDir || values["IOP_AGENT_TASK_ROOT"] != binding.TaskRoot || values["FIXTURE"] != "present" { t.Fatalf("provider environment is not confined: %#v", values) } for _, name := range []string{"auth.json", "config.toml"} { target, err := os.Readlink(filepath.Join(codexHome, name)) if err != nil { t.Fatalf("read runtime link %s: %v", name, err) } if target != filepath.Join(sourceHome, name) { t.Fatalf("runtime link %s = %q", name, target) } } if _, err := providerEnvironment(resolved, binding, binding.WorkingDir); err != nil { t.Fatalf("idempotent providerEnvironment: %v", err) } } func TestProviderPreparesConfinedCommandAndOwnsExactStartedChild(t *testing.T) { provider, err := NewProvider(testCatalog()) if err != nil { t.Fatalf("NewProvider: %v", err) } work := agenttask.WorkRecord{ Unit: agenttask.WorkUnit{ ID: "1", Metadata: map[string]string{ "plan_path": "agent-task/m-m1/1_fixture/PLAN-test.md", "review_path": "agent-task/m-m1/1_fixture/CODE_REVIEW-test.md", }, }, AttemptID: "attempt-1", } project := agenttask.ProjectRecord{ ProjectID: "project-a", WorkspaceID: "workspace-a", } target := agenttask.ExecutionTarget{ ProviderID: "test-provider", ModelID: "test-model", ProfileID: "p1", } resolved, ok := testCatalog().ResolveProfile(target.ProfileID) if !ok { t.Fatal("test profile is missing") } target.ProfileRevision = guardProfile(resolved).Revision binding := agenttask.ConfinementBinding{ Revision: "confinement-r1", ProfileRevision: target.ProfileRevision, TaskRoot: "/tmp/task", } launch, err := provider.Prepare(context.Background(), agenttask.DispatchRequest{ Project: project, Work: work, Target: target, Permit: &agentguard.Permit{}, Confinement: fakeConfinement{binding: binding}, Workspace: agentguard.CanonicalWorkspace{ WorkingDir: "/tmp/task/view", WritableRoots: []string{"/tmp/task/view"}, }, }) if err != nil { t.Fatalf("Prepare: %v", err) } command := launch.Command() if command.Name != "provider-must-not-run-in-tests" || !strings.Contains(command.Args[len(command.Args)-1], "PLAN-test.md") || !strings.Contains(command.Args[len(command.Args)-1], "CODE_REVIEW-test.md") { t.Fatalf("prepared command = %#v", command) } if _, err := launch.BindStarted(nil); err == nil { t.Fatal("BindStarted accepted a missing confinement child") } child := exec.Command("/bin/sh", "-c", "cat >/dev/null") stdin, err := child.StdinPipe() if err != nil { t.Fatal(err) } stdout, err := child.StdoutPipe() if err != nil { t.Fatal(err) } stderr, err := child.StderrPipe() if err != nil { t.Fatal(err) } if err := child.Start(); err != nil { t.Fatalf("start deterministic fixture child: %v", err) } started := &fakeStartedConfinement{ child: child, stdin: stdin, stdout: stdout, stderr: stderr, } invocation, err := launch.BindStarted(started) if err != nil { _ = child.Process.Kill() t.Fatalf("BindStarted: %v", err) } locators := invocation.Locators() if len(locators) != 1 || locators[0].Kind != agenttask.LocatorProcess || locators[0].ProjectID != project.ProjectID || locators[0].WorkUnitID != work.Unit.ID { t.Fatalf("locators = %#v", locators) } submission, err := invocation.Wait(context.Background()) if err != nil { t.Fatalf("Wait: %v", err) } if !submission.Ready || submission.ArtifactID != artifactIdentity(work) { t.Fatalf("submission = %#v", submission) } } func TestProviderPersistsPiNativeSessionBeforeWait(t *testing.T) { providerCatalog := piTestCatalog() provider, err := NewProvider(providerCatalog) if err != nil { t.Fatalf("NewProvider: %v", err) } resolved, ok := providerCatalog.ResolveProfile("pi-headless") if !ok { t.Fatal("Pi test profile is missing") } target := agenttask.ExecutionTarget{ ProviderID: resolved.Provider.ID, ModelID: resolved.Model.ID, ProfileID: resolved.Profile.ID, ProfileRevision: guardProfile(resolved).Revision, ConfigRevision: "config-r1", Capacity: 1, } root := canonicalTempDir(t) binding := agenttask.ConfinementBinding{ Revision: "confinement-r1", IsolationID: "overlay:pi", IsolationRevision: "overlay-r1", PinnedBaseRevision: "snapshot-r1", ConfigRevision: "config-r1", GrantRevision: "grant-r1", ProfileRevision: target.ProfileRevision, BaseRoot: filepath.Join(root, "base"), RuntimeRoot: root, SnapshotRoot: filepath.Join(root, "snapshot"), TaskRoot: filepath.Join(root, "task"), WorkingDir: filepath.Join(root, "task", "view"), WritableRoots: []string{ filepath.Join(root, "task", "view"), filepath.Join(root, "task", "tmp"), filepath.Join(root, "task", "cache"), }, } project := agenttask.ProjectRecord{ProjectID: "p", WorkspaceID: "w"} work := agenttask.WorkRecord{ Unit: agenttask.WorkUnit{ ID: "1", Metadata: map[string]string{ "plan_path": "agent-task/m-m/1/PLAN-cloud-G10.md", "review_path": "agent-task/m-m/1/CODE_REVIEW-cloud-G10.md", }, }, AttemptID: "attempt-1", } launch, err := provider.Prepare(context.Background(), agenttask.DispatchRequest{ Project: project, Work: work, Target: target, Permit: &agentguard.Permit{}, Confinement: fakeConfinement{ binding: binding, }, Workspace: agentguard.CanonicalWorkspace{ WorkingDir: binding.WorkingDir, WritableRoots: append( []string(nil), binding.WritableRoots..., ), }, }) if err != nil { t.Fatalf("Prepare: %v", err) } command := launch.Command() sessionIndex := -1 for index, argument := range command.Args { if argument == "--session-id" { sessionIndex = index break } } if sessionIndex < 0 || sessionIndex+3 >= len(command.Args) || command.Args[sessionIndex+2] != "--session-dir" { t.Fatalf("Pi command has no durable session arguments: %#v", command.Args) } started, err := startFixtureCommand(context.Background(), "cat >/dev/null") if err != nil { t.Fatalf("start fixture: %v", err) } invocation, err := launch.BindStarted(started) if err != nil { _ = started.Abort() t.Fatalf("BindStarted: %v", err) } locators := invocation.Locators() if len(locators) != 2 || locators[1].Kind != agenttask.LocatorSession { t.Fatalf("Pi locators = %#v", locators) } var reference nativeSessionReference if err := json.Unmarshal([]byte(locators[1].Opaque), &reference); err != nil { t.Fatalf("decode native session locator: %v", err) } if reference.SessionID != command.Args[sessionIndex+1] || reference.SessionDirectory != command.Args[sessionIndex+3] || locators[1].Revision != digestStrings( "native-session-locator", locators[1].Opaque, ) { t.Fatalf("native session locator/command mismatch: %#v / %#v", reference, command.Args) } if _, err := invocation.Wait(context.Background()); err != nil { t.Fatalf("Wait: %v", err) } } func TestWorkerPromptPreservesActiveArtifactsForOfficialReview(t *testing.T) { plan := "agent-task/m-m/1/PLAN-cloud-G10.md" review := "agent-task/m-m/1/CODE_REVIEW-cloud-G10.md" prompt := workerPrompt(plan, review) for _, required := range []string{ plan, review, "direct implementing-worker assignment", "do not invoke a task-loop dispatcher", "Do not append a review verdict", "archive, move, rename, or delete either active artifact", } { if !strings.Contains(prompt, required) { t.Fatalf("worker prompt does not contain %q: %q", required, prompt) } } } func TestProviderFailureObservationFeedsCommonContinuationIdentity(t *testing.T) { providerCatalog := testCatalog() observer := matchingQuotaObserver{} provider, err := NewProvider(providerCatalog, observer) if err != nil { t.Fatalf("NewProvider: %v", err) } resolved, ok := providerCatalog.ResolveProfile("p1") if !ok { t.Fatal("test profile is missing") } target := agenttask.ExecutionTarget{ ProviderID: resolved.Provider.ID, ModelID: resolved.Model.ID, ProfileID: resolved.Profile.ID, ProfileRevision: guardProfile(resolved).Revision, ConfigRevision: "config-r1", Capacity: 1, } binding := agenttask.ConfinementBinding{ Revision: "confinement-r1", ProfileRevision: target.ProfileRevision, TaskRoot: "/tmp/task", } work := agenttask.WorkRecord{ Unit: agenttask.WorkUnit{ ID: "work", Metadata: map[string]string{ "plan_path": "agent-task/m/PLAN-local-G01.md", "review_path": "agent-task/m/CODE_REVIEW-local-G01.md", }, }, AttemptID: "attempt-1", } launch, err := provider.Prepare(context.Background(), agenttask.DispatchRequest{ Project: agenttask.ProjectRecord{ProjectID: "p", WorkspaceID: "w"}, Work: work, Target: target, Permit: &agentguard.Permit{}, Confinement: fakeConfinement{ binding: binding, }, Workspace: agentguard.CanonicalWorkspace{WorkingDir: "/tmp/task"}, }) if err != nil { t.Fatalf("Prepare: %v", err) } started, err := startFixtureCommand(context.Background(), "exit 3") if err != nil { t.Fatalf("start fixture: %v", err) } invocation, err := launch.BindStarted(started) if err != nil { _ = started.Abort() t.Fatalf("BindStarted: %v", err) } if _, err := invocation.Wait(context.Background()); err == nil { t.Fatal("failed provider fixture returned success") } observed, ok := invocation.(agentruntimeFailureObservedInvocation) if !ok { t.Fatal("provider invocation does not expose a normalized failure observation") } observation := observed.FailureObservation() if observation.Failure.Code != agentruntime.FailureCodeProcessExit || !observation.Failure.Retryable || observation.Quota.Adapter != target.ProviderID || observation.Quota.Target != target.ProfileID || observation.Quota.Validity != agentpolicy.ObservationValid { t.Fatalf("failure observation = %#v", observation) } } type agentruntimeFailureObservedInvocation interface { FailureObservation() agentpolicy.AttemptObservation } type matchingQuotaObserver struct{} func (matchingQuotaObserver) ObserveQuota( _ context.Context, target agenttask.ExecutionTarget, observedAt time.Time, ) (agentpolicy.QuotaObservation, error) { snapshot := clistatus.NormalizeQuotaSnapshot( target.ProviderID, target.ProfileID, nil, observedAt, nil, nil, ) return agentpolicy.NormalizeQuotaObservation(snapshot, observedAt, time.Minute), nil } type fakeConfinement struct { binding agenttask.ConfinementBinding } func (confinement fakeConfinement) Revision() string { return confinement.binding.Revision } func (confinement fakeConfinement) Binding() agenttask.ConfinementBinding { return confinement.binding } func (confinement fakeConfinement) Validate(agenttask.ConfinementBinding) error { return nil } func (confinement fakeConfinement) Start( context.Context, agenttask.ConfinementCommand, ) (agenttask.StartedConfinement, error) { panic("fixture confinement must not launch the declared provider command") } type fakeStartedConfinement struct { child *exec.Cmd stdin io.WriteCloser stdout io.ReadCloser stderr io.ReadCloser } func (started *fakeStartedConfinement) Child() *exec.Cmd { return started.child } func (started *fakeStartedConfinement) Stdin() io.WriteCloser { return started.stdin } func (started *fakeStartedConfinement) Stdout() io.ReadCloser { return started.stdout } func (started *fakeStartedConfinement) Stderr() io.ReadCloser { return started.stderr } func (started *fakeStartedConfinement) Abort() error { if started.child == nil || started.child.Process == nil { return nil } _ = started.stdin.Close() return started.child.Process.Kill() }