package service

import (
	"context"
	"errors"
	"sync"
	"testing"
	"time"
)

type channelFakeExecutor struct {
	fn func(context.Context, SingleRequestRequest, SingleRequestController) error
}

func (f *channelFakeExecutor) ExecuteSingleRequest(ctx context.Context, req SingleRequestRequest, ctrl SingleRequestController) error {
	if f.fn != nil {
		return f.fn(ctx, req, ctrl)
	}
	return nil
}

func createTestBinding(t *testing.T) *SingleRequestBinding {
	t.Helper()
	binding, err := NewSingleRequestBinding(
		"test-model", "workspace-ref-123",
		SingleRequestStageBinding{Model: "gemini-3.6-flash", Options: map[string]any{"reasoning_effort": "high"}},
		SingleRequestStageBinding{Model: "ornith-fast"},
		SingleRequestStageBinding{Model: "gemini-3.6-flash", Options: map[string]any{"reasoning_effort": "high"}},
		SingleRequestLimits{WallClockMS: 60000, StageTimeoutMS: 10000, MaxToolIterations: 10, MaxOutputBytes: 1048576},
	)
	if err != nil {
		t.Fatalf("NewSingleRequestBinding: %v", err)
	}
	return binding
}

func testEnvelope(requestID string, sequence uint64, stage SingleRequestState) SingleRequestEnvelope {
	return SingleRequestEnvelope{RequestID: requestID, Sequence: sequence, Stage: stage}
}

func submitToFinalizing(req SingleRequestRequest, ctrl SingleRequestController, result *SingleRequestResult) error {
	for sequence, stage := range []SingleRequestState{
		SingleRequestStatePlanning,
		SingleRequestStateWorking,
		SingleRequestStateReviewing,
		SingleRequestStateFinalizing,
	} {
		env := testEnvelope(req.RequestID, uint64(sequence+1), stage)
		if stage == SingleRequestStateFinalizing {
			env.Result = result
		}
		if err := ctrl.SubmitEnvelope(env); err != nil {
			return err
		}
	}
	return nil
}

func waitForState(t *testing.T, handle SingleRequestExecution, want SingleRequestState) {
	t.Helper()
	deadline := time.After(2 * time.Second)
	ticker := time.NewTicker(time.Millisecond)
	defer ticker.Stop()
	for {
		if handle.State() == want {
			return
		}
		select {
		case <-deadline:
			t.Fatalf("state=%s, want %s", handle.State(), want)
		case <-ticker.C:
		}
	}
}

func waitForExecution(t *testing.T, handle SingleRequestExecution) (SingleRequestResult, error) {
	t.Helper()
	type outcome struct {
		result SingleRequestResult
		err    error
	}
	done := make(chan outcome, 1)
	go func() {
		result, err := handle.Wait()
		done <- outcome{result: result, err: err}
	}()
	select {
	case outcome := <-done:
		return outcome.result, outcome.err
	case <-time.After(2 * time.Second):
		t.Fatal("Wait did not return")
		return SingleRequestResult{}, nil
	}
}

func startTestExecution(t *testing.T, executor SingleRequestExecutor) SingleRequestExecution {
	t.Helper()
	handle, err := startSingleRequest(context.Background(), executor, SingleRequestRequest{
		RequestID: "request-test",
		Binding:   createTestBinding(t),
		Prompt:    "complete the private task",
	})
	if err != nil {
		t.Fatalf("StartSingleRequest: %v", err)
	}
	return handle
}

func TestSingleRequestExecutorUnavailable(t *testing.T) {
	_, err := (&Service{}).StartSingleRequest(context.Background(), SingleRequestRequest{
		RequestID: "request-unavailable",
		Binding:   createTestBinding(t),
	})
	if !errors.Is(err, ErrSingleRequestExecutorUnavailable) {
		t.Fatalf("error=%v, want ErrSingleRequestExecutorUnavailable", err)
	}
}

func TestSingleRequestExecutorCannotMutateAdmission(t *testing.T) {
	result := &SingleRequestResult{Output: "accepted result"}
	executor := &channelFakeExecutor{fn: func(_ context.Context, req SingleRequestRequest, ctrl SingleRequestController) error {
		req.Binding.Plan.Options["reasoning_effort"] = "low"
		ctrl.Binding().Review.Options["reasoning_effort"] = "low"
		if err := submitToFinalizing(req, ctrl, result); err != nil {
			return err
		}
		result.Output = "executor-mutated result"
		return nil
	}}

	callerBinding := createTestBinding(t)
	handle, err := startSingleRequest(context.Background(), executor, SingleRequestRequest{
		RequestID: "request-test",
		Binding:   callerBinding,
		Prompt:    "complete the private task",
	})
	if err != nil {
		t.Fatalf("StartSingleRequest: %v", err)
	}
	callerBinding.Plan.Options["reasoning_effort"] = "caller-mutated"
	waitForState(t, handle, SingleRequestStateFinalizing)
	if got := handle.Binding().Plan.Options["reasoning_effort"]; got != "high" {
		t.Fatalf("executor mutated retained binding: %v", got)
	}
	if err := handle.AcknowledgeTerminal(true); err != nil {
		t.Fatalf("AcknowledgeTerminal: %v", err)
	}
	got, err := waitForExecution(t, handle)
	if err != nil || got.Output != "accepted result" {
		t.Fatalf("Wait=(%q, %v), want accepted immutable result", got.Output, err)
	}
}

func TestSingleRequestRejectsExecutorCompletedEnvelope(t *testing.T) {
	executor := &channelFakeExecutor{fn: func(_ context.Context, req SingleRequestRequest, ctrl SingleRequestController) error {
		if err := submitToFinalizing(req, ctrl, &SingleRequestResult{Output: "candidate"}); err != nil {
			return err
		}
		return ctrl.SubmitEnvelope(testEnvelope(req.RequestID, 5, SingleRequestStateCompleted))
	}}
	handle := startTestExecution(t, executor)
	_, err := waitForExecution(t, handle)
	if !errors.Is(err, ErrSingleRequestInvalidState) {
		t.Fatalf("Wait error=%v, want invalid state", err)
	}
	if got := handle.State(); got != SingleRequestStateFailed {
		t.Fatalf("state=%s, want failed", got)
	}
}

func TestSingleRequestExecutorExitFailsClosed(t *testing.T) {
	for name, executor := range map[string]SingleRequestExecutor{
		"no envelopes": &channelFakeExecutor{},
		"mid-stage": &channelFakeExecutor{fn: func(_ context.Context, req SingleRequestRequest, ctrl SingleRequestController) error {
			return ctrl.SubmitEnvelope(testEnvelope(req.RequestID, 1, SingleRequestStatePlanning))
		}},
	} {
		t.Run(name, func(t *testing.T) {
			handle := startTestExecution(t, executor)
			_, err := waitForExecution(t, handle)
			if !errors.Is(err, ErrSingleRequestFailed) {
				t.Fatalf("Wait error=%v, want ErrSingleRequestFailed", err)
			}
			if got := handle.State(); got != SingleRequestStateFailed {
				t.Fatalf("state=%s, want failed", got)
			}
		})
	}
}

func TestSingleRequestEnvelopeOrderingFailsClosed(t *testing.T) {
	tests := map[string]func(SingleRequestRequest, SingleRequestController) error{
		"missing sequence": func(req SingleRequestRequest, ctrl SingleRequestController) error {
			return ctrl.SubmitEnvelope(SingleRequestEnvelope{RequestID: req.RequestID, Stage: SingleRequestStatePlanning})
		},
		"duplicate sequence": func(req SingleRequestRequest, ctrl SingleRequestController) error {
			if err := ctrl.SubmitEnvelope(testEnvelope(req.RequestID, 1, SingleRequestStatePlanning)); err != nil {
				return err
			}
			return ctrl.SubmitEnvelope(testEnvelope(req.RequestID, 1, SingleRequestStateWorking))
		},
		"reordered sequence": func(req SingleRequestRequest, ctrl SingleRequestController) error {
			if err := ctrl.SubmitEnvelope(testEnvelope(req.RequestID, 2, SingleRequestStatePlanning)); err != nil {
				return err
			}
			return ctrl.SubmitEnvelope(testEnvelope(req.RequestID, 1, SingleRequestStateWorking))
		},
		"mismatched saved stage": func(req SingleRequestRequest, ctrl SingleRequestController) error {
			if err := ctrl.SubmitEnvelope(testEnvelope(req.RequestID, 1, SingleRequestStatePlanning)); err != nil {
				return err
			}
			tool := testEnvelope(req.RequestID, 2, SingleRequestStateInternalTool)
			tool.SavedStage = SingleRequestStateWorking
			return ctrl.SubmitEnvelope(tool)
		},
		"duplicate internal tool": func(req SingleRequestRequest, ctrl SingleRequestController) error {
			if err := ctrl.SubmitEnvelope(testEnvelope(req.RequestID, 1, SingleRequestStatePlanning)); err != nil {
				return err
			}
			tool := testEnvelope(req.RequestID, 2, SingleRequestStateInternalTool)
			tool.SavedStage = SingleRequestStatePlanning
			if err := ctrl.SubmitEnvelope(tool); err != nil {
				return err
			}
			tool.Sequence = 3
			return ctrl.SubmitEnvelope(tool)
		},
	}
	for name, submit := range tests {
		t.Run(name, func(t *testing.T) {
			handle := startTestExecution(t, &channelFakeExecutor{fn: func(_ context.Context, req SingleRequestRequest, ctrl SingleRequestController) error {
				return submit(req, ctrl)
			}})
			_, err := waitForExecution(t, handle)
			if !errors.Is(err, ErrSingleRequestInvalidSequence) && !errors.Is(err, ErrSingleRequestInvalidState) {
				t.Fatalf("Wait error=%v, want sequence or state failure", err)
			}
		})
	}
}

func TestSingleRequestFinalCandidateRequired(t *testing.T) {
	tests := map[string]func(SingleRequestRequest, SingleRequestController) error{
		"nil finalizing candidate": func(req SingleRequestRequest, ctrl SingleRequestController) error {
			for sequence, stage := range []SingleRequestState{
				SingleRequestStatePlanning,
				SingleRequestStateWorking,
				SingleRequestStateReviewing,
				SingleRequestStateFinalizing,
			} {
				if err := ctrl.SubmitEnvelope(testEnvelope(req.RequestID, uint64(sequence+1), stage)); err != nil {
					return err
				}
			}
			return nil
		},
		"stale earlier-stage candidate": func(req SingleRequestRequest, ctrl SingleRequestController) error {
			env := testEnvelope(req.RequestID, 1, SingleRequestStatePlanning)
			env.Result = &SingleRequestResult{Output: "stale candidate"}
			return ctrl.SubmitEnvelope(env)
		},
	}
	for name, submit := range tests {
		t.Run(name, func(t *testing.T) {
			handle := startTestExecution(t, &channelFakeExecutor{fn: func(_ context.Context, req SingleRequestRequest, ctrl SingleRequestController) error {
				return submit(req, ctrl)
			}})
			_, err := waitForExecution(t, handle)
			if !errors.Is(err, ErrSingleRequestInvalidState) {
				t.Fatalf("Wait error=%v, want ErrSingleRequestInvalidState", err)
			}
			if got := handle.State(); got != SingleRequestStateFailed {
				t.Fatalf("state=%s, want failed", got)
			}
		})
	}
}

func TestSingleRequestAcknowledgementRequiresFinalCandidate(t *testing.T) {
	executor := &channelFakeExecutor{fn: func(ctx context.Context, _ SingleRequestRequest, _ SingleRequestController) error {
		<-ctx.Done()
		return ctx.Err()
	}}
	handle := startTestExecution(t, executor)
	internal := handle.(*singleRequestHandle)
	internal.mu.Lock()
	internal.state = SingleRequestStateFinalizing
	internal.mu.Unlock()

	err := handle.AcknowledgeTerminal(true)
	if !errors.Is(err, ErrSingleRequestInvalidState) {
		t.Fatalf("AcknowledgeTerminal error=%v, want ErrSingleRequestInvalidState", err)
	}
	if got := handle.State(); got != SingleRequestStateFailed {
		t.Fatalf("state=%s, want failed", got)
	}
	if _, err := waitForExecution(t, handle); !errors.Is(err, ErrSingleRequestInvalidState) {
		t.Fatalf("Wait error=%v, want ErrSingleRequestInvalidState", err)
	}
}

func TestSingleRequestProgressRedactionAndFinalCandidateDelivery(t *testing.T) {
	ready := make(chan struct{})
	release := make(chan struct{})
	executor := &channelFakeExecutor{fn: func(_ context.Context, req SingleRequestRequest, ctrl SingleRequestController) error {
		planning := testEnvelope(req.RequestID, 1, SingleRequestStatePlanning)
		planning.Message = "raw executor secret"
		if err := ctrl.SubmitEnvelope(planning); err != nil {
			return err
		}
		close(ready)
		<-release
		if err := ctrl.SubmitEnvelope(testEnvelope(req.RequestID, 2, SingleRequestStateWorking)); err != nil {
			return err
		}
		if err := ctrl.SubmitEnvelope(testEnvelope(req.RequestID, 3, SingleRequestStateReviewing)); err != nil {
			return err
		}
		final := testEnvelope(req.RequestID, 4, SingleRequestStateFinalizing)
		final.Message = "raw executor secret"
		final.Result = &SingleRequestResult{Output: "raw executor result"}
		return ctrl.SubmitEnvelope(final)
	}}
	handle := startTestExecution(t, executor)
	<-ready

	// Saturate the ordinary lane before the final candidate is emitted.
	internal := handle.(*singleRequestHandle)
	internal.mu.Lock()
	for range 100 {
		internal.notifyProgressLocked(SingleRequestProgress{RequestID: internal.req.RequestID, Message: "filler"}, false)
	}
	internal.mu.Unlock()
	close(release)
	waitForState(t, handle, SingleRequestStateFinalizing)

	seenFinalizingCandidate := false
	for len(internal.progressCh) > 0 {
		progress := <-internal.progressCh
		if progress.Message == "raw executor secret" || progress.Err != nil {
			t.Fatalf("progress leaked executor-controlled data: %#v", progress)
		}
		if progress.Result == nil {
			if progress.Stage == SingleRequestStateFinalizing {
				t.Fatal("finalizing progress did not include a final candidate")
			}
			continue
		}
		if progress.Stage != SingleRequestStateFinalizing {
			t.Fatalf("non-finalizing progress exposed a result: %#v", progress)
		}
		if progress.Result.Output != "raw executor result" {
			t.Fatalf("finalizing result=%q, want final candidate", progress.Result.Output)
		}
		progress.Result.Output = "surface-mutated result"
		seenFinalizingCandidate = true
	}
	if !seenFinalizingCandidate {
		t.Fatal("finalizing candidate was dropped after ordinary progress saturation")
	}
	if err := handle.AcknowledgeTerminal(true); err != nil {
		t.Fatalf("AcknowledgeTerminal: %v", err)
	}
	result, err := waitForExecution(t, handle)
	if err != nil {
		t.Fatalf("Wait: %v", err)
	}
	if result.Output != "raw executor result" {
		t.Fatalf("Wait result=%q, want immutable final candidate", result.Output)
	}
}

func TestSingleRequestTerminalRaces(t *testing.T) {
	for i := 0; i < 20; i++ {
		release := make(chan struct{})
		executor := &channelFakeExecutor{fn: func(ctx context.Context, req SingleRequestRequest, ctrl SingleRequestController) error {
			if err := submitToFinalizing(req, ctrl, &SingleRequestResult{Output: "candidate"}); err != nil {
				return err
			}
			select {
			case <-release:
				return nil
			case <-ctx.Done():
				return ctx.Err()
			}
		}}
		handle := startTestExecution(t, executor)
		waitForState(t, handle, SingleRequestStateFinalizing)

		var wg sync.WaitGroup
		wg.Add(4)
		go func() { defer wg.Done(); _ = handle.AcknowledgeTerminal(true) }()
		go func() { defer wg.Done(); _ = handle.AcknowledgeTerminal(false) }()
		go func() { defer wg.Done(); handle.Cancel() }()
		go func() {
			defer wg.Done()
			_ = handle.SubmitEnvelope(SingleRequestEnvelope{RequestID: "request-test", Sequence: 5, Stage: SingleRequestStateFailed, Err: errors.New("executor failure")})
		}()
		wg.Wait()
		close(release)
		if _, err := waitForExecution(t, handle); err == nil && handle.State() != SingleRequestStateCompleted {
			t.Fatalf("non-completed terminal state must retain an error")
		}

		terminalCount := 0
		for progress := range handle.Progress() {
			if progress.Terminal != nil {
				terminalCount++
			}
		}
		if terminalCount != 1 {
			t.Fatalf("terminal disposition progress count=%d, want exactly one", terminalCount)
		}
	}
}

func TestSingleRequestRequestWallClockBudgetDisposition(t *testing.T) {
	for iteration := 0; iteration < 40; iteration++ {
		binding := createTestBinding(t)
		binding.Limits.WallClockMS = 10
		binding.Limits.StageTimeoutMS = 10
		executorReturned := make(chan struct{})
		executor := &channelFakeExecutor{fn: func(ctx context.Context, _ SingleRequestRequest, _ SingleRequestController) error {
			defer close(executorReturned)
			<-ctx.Done()
			return ctx.Err()
		}}
		handle, err := startSingleRequest(context.Background(), executor, SingleRequestRequest{
			RequestID: "request-budget",
			Binding:   binding,
			Prompt:    "exercise the immutable wall-clock budget",
		})
		if err != nil {
			t.Fatalf("iteration=%d StartSingleRequest: %v", iteration, err)
		}

		terminalCount := 0
		var terminal SingleRequestTerminalDisposition
		for progress := range handle.Progress() {
			if progress.Terminal != nil {
				terminalCount++
				terminal = *progress.Terminal
			}
		}
		result, waitErr := waitForExecution(t, handle)
		want := SingleRequestTerminalDisposition{Kind: SingleRequestTerminalError, ErrorClass: SingleRequestTerminalErrorBudget}
		if !errors.Is(waitErr, ErrSingleRequestInternalToolBudget) || result.Output != "" || terminal != want || terminalCount != 1 {
			t.Fatalf("iteration=%d Wait=(%+v, %v) terminal=%+v count=%d, want budget/1", iteration, result, waitErr, terminal, terminalCount)
		}
		if handle.State() != SingleRequestStateFailed {
			t.Fatalf("iteration=%d state=%s, want failed", iteration, handle.State())
		}
		select {
		case <-executorReturned:
		default:
			t.Fatalf("iteration=%d executor remained active after Wait", iteration)
		}
	}
}

func TestSingleRequestTerminalDispositionValidationAndLegacyNormalization(t *testing.T) {
	for _, invalid := range []SingleRequestTerminalDisposition{
		{},
		{Kind: SingleRequestTerminalEndTurn, ErrorClass: SingleRequestTerminalErrorProvider},
		{Kind: SingleRequestTerminalError},
		{Kind: SingleRequestTerminalError, ErrorClass: "raw-private-value"},
		{Kind: "unknown"},
	} {
		if invalid.Validate() == nil {
			t.Fatalf("invalid disposition accepted: %+v", invalid)
		}
	}

	handle := startTestExecution(t, &channelFakeExecutor{fn: func(_ context.Context, req SingleRequestRequest, ctrl SingleRequestController) error {
		return submitToFinalizing(req, ctrl, &SingleRequestResult{Output: "legacy output"})
	}})
	waitForState(t, handle, SingleRequestStateFinalizing)
	var terminal *SingleRequestTerminalDisposition
	for terminal == nil {
		progress := <-handle.Progress()
		if progress.Stage == SingleRequestStateFinalizing {
			terminal = progress.Terminal
			if progress.Result == nil || progress.Result.Terminal.Kind != SingleRequestTerminalEndTurn {
				t.Fatalf("legacy result was not normalized: %+v", progress)
			}
		}
	}
	terminal.Kind = SingleRequestTerminalLength
	if err := handle.AcknowledgeTerminal(true); err != nil {
		t.Fatalf("AcknowledgeTerminal: %v", err)
	}
	result, err := waitForExecution(t, handle)
	if err != nil || result.Terminal.Kind != SingleRequestTerminalEndTurn {
		t.Fatalf("Wait=(%+v, %v), want immutable end_turn", result, err)
	}
}

func TestSingleRequestTerminalDispositionEarlyLengthOnly(t *testing.T) {
	t.Run("length from planning", func(t *testing.T) {
		handle := startTestExecution(t, &channelFakeExecutor{fn: func(_ context.Context, req SingleRequestRequest, ctrl SingleRequestController) error {
			if err := ctrl.SubmitEnvelope(testEnvelope(req.RequestID, 1, SingleRequestStatePlanning)); err != nil {
				return err
			}
			return ctrl.SubmitEnvelope(SingleRequestEnvelope{
				RequestID: req.RequestID,
				Sequence:  2,
				Stage:     SingleRequestStateFinalizing,
				Result:    &SingleRequestResult{Terminal: SingleRequestTerminalDisposition{Kind: SingleRequestTerminalLength}},
			})
		}})
		terminalCount := 0
		for progress := range handle.Progress() {
			if progress.Terminal != nil {
				terminalCount++
				if progress.Stage != SingleRequestStateFinalizing || progress.Terminal.Kind != SingleRequestTerminalLength {
					t.Fatalf("progress=%+v, want planning length final", progress)
				}
				if err := handle.AcknowledgeTerminal(true); err != nil {
					t.Fatalf("AcknowledgeTerminal: %v", err)
				}
			}
		}
		result, err := waitForExecution(t, handle)
		if err != nil || result.Terminal.Kind != SingleRequestTerminalLength || terminalCount != 1 {
			t.Fatalf("Wait=(%+v, %v), terminals=%d", result, err, terminalCount)
		}
	})

	t.Run("end turn cannot skip work and review", func(t *testing.T) {
		handle := startTestExecution(t, &channelFakeExecutor{fn: func(_ context.Context, req SingleRequestRequest, ctrl SingleRequestController) error {
			if err := ctrl.SubmitEnvelope(testEnvelope(req.RequestID, 1, SingleRequestStatePlanning)); err != nil {
				return err
			}
			return ctrl.SubmitEnvelope(SingleRequestEnvelope{
				RequestID: req.RequestID,
				Sequence:  2,
				Stage:     SingleRequestStateFinalizing,
				Result:    &SingleRequestResult{Output: "invalid early success", Terminal: SingleRequestTerminalDisposition{Kind: SingleRequestTerminalEndTurn}},
			})
		}})
		var terminal *SingleRequestTerminalDisposition
		for progress := range handle.Progress() {
			if progress.Terminal != nil {
				terminal = progress.Terminal
			}
		}
		result, err := waitForExecution(t, handle)
		want := SingleRequestTerminalDisposition{Kind: SingleRequestTerminalError, ErrorClass: SingleRequestTerminalErrorValidation}
		if !errors.Is(err, ErrSingleRequestInvalidState) || result.Output != "" || terminal == nil || *terminal != want {
			t.Fatalf("Wait=(%+v, %v), terminal=%+v, want validation failure", result, err, terminal)
		}
	})
}

func TestSingleRequestTerminalDispositionFailureAndCancelPropagation(t *testing.T) {
	tests := []struct {
		name     string
		stage    SingleRequestState
		terminal SingleRequestTerminalDisposition
	}{
		{name: "provider", stage: SingleRequestStateFailed, terminal: SingleRequestTerminalDisposition{Kind: SingleRequestTerminalError, ErrorClass: SingleRequestTerminalErrorProvider}},
		{name: "timeout", stage: SingleRequestStateFailed, terminal: SingleRequestTerminalDisposition{Kind: SingleRequestTerminalError, ErrorClass: SingleRequestTerminalErrorTimeout}},
		{name: "budget", stage: SingleRequestStateFailed, terminal: SingleRequestTerminalDisposition{Kind: SingleRequestTerminalError, ErrorClass: SingleRequestTerminalErrorBudget}},
		{name: "repetition", stage: SingleRequestStateFailed, terminal: SingleRequestTerminalDisposition{Kind: SingleRequestTerminalError, ErrorClass: SingleRequestTerminalErrorRepetition}},
		{name: "malformed", stage: SingleRequestStateFailed, terminal: SingleRequestTerminalDisposition{Kind: SingleRequestTerminalError, ErrorClass: SingleRequestTerminalErrorMalformed}},
		{name: "context", stage: SingleRequestStateFailed, terminal: SingleRequestTerminalDisposition{Kind: SingleRequestTerminalError, ErrorClass: SingleRequestTerminalErrorContext}},
		{name: "cancel", stage: SingleRequestStateCancelled, terminal: SingleRequestTerminalDisposition{Kind: SingleRequestTerminalCancelled}},
	}
	for _, test := range tests {
		t.Run(test.name, func(t *testing.T) {
			handle := startTestExecution(t, &channelFakeExecutor{fn: func(_ context.Context, req SingleRequestRequest, ctrl SingleRequestController) error {
				if err := ctrl.SubmitEnvelope(testEnvelope(req.RequestID, 1, SingleRequestStatePlanning)); err != nil {
					return err
				}
				env := testEnvelope(req.RequestID, 2, test.stage)
				env.Terminal = &test.terminal
				if test.stage == SingleRequestStateFailed {
					env.Err = errors.New("private provider detail")
				}
				return ctrl.SubmitEnvelope(env)
			}})
			_, _ = waitForExecution(t, handle)
			var got *SingleRequestTerminalDisposition
			for progress := range handle.Progress() {
				if progress.Stage == test.stage {
					got = progress.Terminal
					if progress.Err != nil || progress.Result != nil {
						t.Fatalf("terminal progress leaked internal data: %+v", progress)
					}
				}
			}
			if got == nil || *got != test.terminal {
				t.Fatalf("terminal=%+v, want %+v", got, test.terminal)
			}
		})
	}
}

type terminalDispositionCleanupFailure struct{}

func (terminalDispositionCleanupFailure) CleanupWorkspace(context.Context, *SingleRequestWorkspaceBinding, string) error {
	return errors.New("private cleanup detail")
}

func TestSingleRequestTerminalDispositionCleanupConversionBeforeFreeze(t *testing.T) {
	handle := startTestExecution(t, &channelFakeExecutor{fn: func(_ context.Context, req SingleRequestRequest, ctrl SingleRequestController) error {
		internal := ctrl.(*singleRequestHandle)
		internal.mu.Lock()
		internal.toolLoop.opened = true
		internal.toolLoop.lifecycle = terminalDispositionCleanupFailure{}
		internal.mu.Unlock()
		return submitToFinalizing(req, ctrl, &SingleRequestResult{
			Output:   "must not escape",
			Terminal: SingleRequestTerminalDisposition{Kind: SingleRequestTerminalLength},
		})
	}})
	result, err := waitForExecution(t, handle)
	if !errors.Is(err, ErrSingleRequestWorkspaceCleanup) || result.Output != "" {
		t.Fatalf("Wait=(%+v, %v), want cleanup failure without partial result", result, err)
	}
	var got *SingleRequestTerminalDisposition
	for progress := range handle.Progress() {
		if progress.Stage == SingleRequestStateFailed {
			got = progress.Terminal
		}
	}
	want := SingleRequestTerminalDisposition{Kind: SingleRequestTerminalError, ErrorClass: SingleRequestTerminalErrorWorkspaceCleanup}
	if got == nil || *got != want {
		t.Fatalf("cleanup terminal=%+v, want %+v", got, want)
	}
}

func TestSingleRequestTerminalDispositionPostFreezeWinnerStability(t *testing.T) {
	handle := startTestExecution(t, &channelFakeExecutor{fn: func(_ context.Context, req SingleRequestRequest, ctrl SingleRequestController) error {
		return submitToFinalizing(req, ctrl, &SingleRequestResult{Output: "candidate", Terminal: SingleRequestTerminalDisposition{Kind: SingleRequestTerminalEndTurn}})
	}})
	waitForState(t, handle, SingleRequestStateFinalizing)
	for {
		progress := <-handle.Progress()
		if progress.Stage != SingleRequestStateFinalizing {
			continue
		}
		if progress.Terminal == nil || progress.Terminal.Kind != SingleRequestTerminalEndTurn {
			t.Fatalf("finalizing terminal=%+v", progress.Terminal)
		}
		progress.Terminal.Kind = SingleRequestTerminalLength
		break
	}
	if err := handle.AcknowledgeTerminal(false); err != nil {
		t.Fatalf("AcknowledgeTerminal(false): %v", err)
	}
	_, err := waitForExecution(t, handle)
	if !errors.Is(err, ErrSingleRequestFailed) {
		t.Fatalf("Wait error=%v, want endpoint failure", err)
	}
	internal := handle.(*singleRequestHandle)
	internal.mu.Lock()
	frozen := cloneSingleRequestTerminal(internal.terminal)
	internal.mu.Unlock()
	if frozen == nil || frozen.Kind != SingleRequestTerminalEndTurn {
		t.Fatalf("frozen terminal changed after acknowledgement failure: %+v", frozen)
	}
	for progress := range handle.Progress() {
		if progress.Terminal != nil {
			t.Fatalf("post-freeze acknowledgement emitted a second terminal: %+v", progress)
		}
	}
}
