package agent

import (
	"os"
	"path/filepath"
	"sync"
	"testing"
	"time"

	"github.com/stretchr/testify/require"

	"github.com/tidbcloud/AgentTeam/desktop/internal/agent/drivercontract"
	"github.com/tidbcloud/AgentTeam/desktop/internal/agent/runtimeconfig"
	"github.com/tidbcloud/AgentTeam/desktop/internal/consts"
	"github.com/tidbcloud/AgentTeam/desktop/internal/types"
)

// piProcessEventRecorder records translated events observed by the Process.
type piProcessEventRecorder struct {
	mu     sync.Mutex
	events []drivercontract.ParsedEvent
}

func (r *piProcessEventRecorder) record(event drivercontract.ParsedEvent) {
	r.mu.Lock()
	r.events = append(r.events, event)
	r.mu.Unlock()
}

func (r *piProcessEventRecorder) snapshot() []drivercontract.ParsedEvent {
	r.mu.Lock()
	defer r.mu.Unlock()
	return append([]drivercontract.ParsedEvent(nil), r.events...)
}

func (r *piProcessEventRecorder) count(kind string) int {
	n := 0
	for _, e := range r.snapshot() {
		if e.Kind == kind {
			n++
		}
	}
	return n
}

// TestPiProcessStaysBusyUntilAgentSettled drives a real Process with the Pi
// driver and a fake pi binary, proving the Process remains busy (not idle)
// through a Pi turn_end/agent_end and only becomes idle after the single
// authoritative agent_settled root TurnEnd.
func TestPiProcessStaysBusyUntilAgentSettled(t *testing.T) {
	usage := piFakeUsage(10, 5, 0, 15)
	scenario := []piFakeScript{
		piFakePromptScript(
			piFakeAgentStart(),
			piFakeTurnStart(),
			piFakeAssistantStart(nil, nil),
			piFakeTextDelta(0, "Hello", usage),
			piFakeAssistantEnd([]map[string]any{piFakeTextBlock("Hello")}, usage),
			piFakeTurnEnd([]map[string]any{piFakeTextBlock("Hello")}, usage),
			piFakeAgentEnd(false),
			piFakeAgentSettled(),
		),
	}
	binary, _ := writeFakePiRPCModeBinary(t, scenario)

	cfg := drivercontract.LaunchConfig{
		AgentID:         "pi-process-busy",
		RuntimeType:     runtimeconfig.TypePi,
		Binary:          binary,
		WorkDir:         t.TempDir(),
		RuntimeStateDir: t.TempDir(),
		PermissionMode:  "full_access",
		Model:           "gpt-5.5",
		Env:             map[string]string{},
	}
	cfg = testLaunchConfig(cfg, testLaunchSurfaces{Prompt: "hello"})
	recorder := &piProcessEventRecorder{}
	process, err := NewProcess(t.Context(), &PiRPCDriver{}, cfg, ProcessOptions{
		OnEvent: recorder.record,
	})
	require.NoError(t, err)
	t.Cleanup(func() {
		process.Stop()
		_ = process.WaitForExit(5 * time.Second)
	})

	// Wait for the Process to settle (agent_settled -> root TurnEnd drains the
	// Process, then RuntimeIdle is projected).
	require.Eventually(t, func() bool {
		return recorder.count(consts.ParsedEventKindRuntimeIdle) >= 1
	}, 30*time.Second, 50*time.Millisecond)
	require.Equal(t, 1, recorder.count(consts.ParsedEventKindRuntimeIdle), "RuntimeIdle projected from agent_settled")
	require.True(t, process.IsIdle(), "Process idle after agent_settled root TurnEnd")
	require.Zero(t, recorder.count(consts.ParsedEventKindRuntimeCompacting), "ordinary Pi turns must not trigger generic idle compaction")
	require.Equal(t, 1, recorder.count(consts.ParsedEventKindText), "streaming text once")

	// The authoritative root-start metadata must appear exactly once.
	rootStarts := 0
	for _, e := range recorder.snapshot() {
		if e.Kind == consts.ParsedEventKindThinking && e.Metadata != nil {
			if v, _ := e.Metadata[codexAuthoritativeTurnStartMetadataKey].(bool); v {
				rootStarts++
			}
		}
	}
	require.Equal(t, 1, rootStarts, "authoritative root-start metadata emitted once")
}

// TestPiProcessInterruptingReplacement verifies an interrupting replacement
// through the Process: a second message is delivered as a fresh root run, the
// replacement's text and authoritative start are projected, and the Process
// settles idle.
func TestPiProcessInterruptingReplacement(t *testing.T) {
	usage := piFakeUsage(10, 5, 0, 15)
	gen1 := []map[string]any{piFakeTextBlock("gen1")}
	gen2 := []map[string]any{piFakeTextBlock("gen2")}
	scenario := []piFakeScript{
		piFakePromptScript(
			piFakeAgentStart(),
			piFakeTurnStart(),
			piFakeAssistantStart(nil, nil),
			piFakeTextDelta(0, "gen1", usage),
			piFakeAssistantEnd(gen1, usage),
			piFakeTurnEnd(gen1, usage),
			piFakeAgentEnd(false),
			piFakeAgentSettled(),
		),
		piFakePromptScript(
			// The interrupting replacement's prompt carries the runtime-input
			// marker; acknowledge it so the delivery ownership resolves.
			piFakeUserMessage("interrupt-replacement", "second\n\n<!-- agentteam-runtime-input:interrupt-replacement -->"),
			piFakeUserMessageEnd("interrupt-replacement", "second\n\n<!-- agentteam-runtime-input:interrupt-replacement -->"),
			piFakeAgentStart(),
			piFakeTurnStart(),
			piFakeAssistantStart(nil, nil),
			piFakeTextDelta(0, "gen2", usage),
			piFakeAssistantEnd(gen2, usage),
			piFakeTurnEnd(gen2, usage),
			piFakeAgentEnd(false),
			piFakeAgentSettled(),
		),
		// The Process may re-deliver the interrupt message once if its ownership
		// ack races the inbox retry; absorb it with a settling turn.
		piFakePromptScript(
			piFakeAgentStart(),
			piFakeTurnStart(),
			piFakeAssistantStart(nil, nil),
			piFakeTextDelta(0, "gen2", usage),
			piFakeAssistantEnd(gen2, usage),
			piFakeTurnEnd(gen2, usage),
			piFakeAgentEnd(false),
			piFakeAgentSettled(),
		),
	}
	binary, _ := writeFakePiRPCModeBinary(t, scenario)
	cfg := testLaunchConfig(drivercontract.LaunchConfig{AgentID: "pi-process-interrupt", RuntimeType: runtimeconfig.TypePi, Binary: binary, WorkDir: t.TempDir(), RuntimeStateDir: t.TempDir(), PermissionMode: "full_access", Model: "gpt-5.5", Env: map[string]string{}}, testLaunchSurfaces{Prompt: "first"})
	recorder := &piProcessEventRecorder{}
	process, err := NewProcess(t.Context(), &PiRPCDriver{}, cfg, ProcessOptions{
		OnEvent: recorder.record,
	})
	require.NoError(t, err)
	t.Cleanup(func() {
		process.Stop()
		_ = process.WaitForExit(5 * time.Second)
	})

	// Wait for gen1 to fully settle (its RuntimeIdle projected).
	require.Eventually(t, func() bool {
		return recorder.count(consts.ParsedEventKindRuntimeIdle) >= 1
	}, 15*time.Second, 50*time.Millisecond)
	require.Equal(t, 1, countKinds(recorder, consts.ParsedEventKindText, "gen1"), "gen1 text projected")
	require.True(t, process.IsIdle(), "Process idle after gen1 settlement")

	// Interrupting replacement after gen1 settled: delivered as a new root run.
	interruptDone := make(chan error, 1)
	go func() {
		_, err := process.InterruptAndDeliver(t.Context(), types.InboxMessage{
			MessageID: "interrupt-replacement",
			Target:    "#pi-test:thread",
			Content:   "second",
		})
		interruptDone <- err
	}()

	// Wait for the replacement to settle and the Process to become idle again.
	require.Eventually(t, func() bool {
		return countKinds(recorder, consts.ParsedEventKindText, "gen2") >= 1
	}, 15*time.Second, 50*time.Millisecond)
	require.Eventually(t, func() bool {
		return process.IsIdle()
	}, 15*time.Second, 50*time.Millisecond)
	select {
	case err := <-interruptDone:
		require.NoError(t, err, "interrupt replacement delivered")
	case <-time.After(5 * time.Second):
		t.Fatal("interrupt replacement did not complete")
	}

	// The replacement's authoritative start is projected (one per admitted run).
	rootStarts := 0
	for _, e := range recorder.snapshot() {
		if e.Kind == consts.ParsedEventKindThinking && e.Metadata != nil {
			if v, _ := e.Metadata[codexAuthoritativeTurnStartMetadataKey].(bool); v {
				rootStarts++
			}
		}
	}
	require.GreaterOrEqual(t, rootStarts, 2, "one authoritative start per generation")
	require.Zero(t, recorder.count(consts.ParsedEventKindRuntimeCompacting), "consecutive Pi turns must not trigger generic idle compaction")
}

// TestPiRPCGenerationFenceRejectsStaleTerminal proves the driver tags a settled
// generation's TurnEnd with its completed turn id and that Process's
// runtimeBoundarySuperseded rejects it once a replacement generation is active.
func TestPiRPCGenerationFenceRejectsStaleTerminal(t *testing.T) {
	d := &PiRPCDriver{}
	d.mu.Lock()
	d.sessionID = "sess-fence"
	d.rootRunGeneration = 1
	d.rootRunTurnID = "pi-gen-1"
	d.rootRunActive = true
	d.rootRunSettled = make(chan struct{})
	d.mu.Unlock()

	// Settle gen1 (root inactive), then admit gen2 as the replacement.
	d.mu.Lock()
	d.rootRunActive = false
	d.mu.Unlock()
	// gen2 admitted: beginRootRun increments to gen2 and activates it.
	d.beginRootRun()
	require.Equal(t, "pi-gen-2", d.CurrentActiveTurnID())

	// Build the gen1 terminal events exactly as emitTerminal would.
	gen1TurnID := "pi-gen-1"
	terminalMeta := map[string]any{runtimeCompletedTurnIDKey: gen1TurnID}

	// The gen1 TurnEnd must be superseded: completedTurnID != activeTurnID.
	turnEnd := drivercontract.ParsedEvent{
		Kind:       consts.ParsedEventKindTurnEnd,
		SessionID:  "sess-fence",
		StopReason: "end_turn",
		Metadata:   terminalMeta,
	}
	// The gen1 RuntimeIdle must be superseded: an active turn exists.
	idle := drivercontract.ParsedEvent{Kind: consts.ParsedEventKindRuntimeIdle, SessionID: "sess-fence", Metadata: terminalMeta}

	// Drive these through a minimal Process to exercise runtimeBoundarySuperseded.
	process := &Process{}
	process.driver = d
	require.True(t, process.runtimeBoundarySuperseded(0, turnEnd), "gen1 TurnEnd superseded by gen2")
	require.True(t, process.runtimeBoundarySuperseded(0, idle), "gen1 RuntimeIdle superseded by gen2")
}

func countKinds(r *piProcessEventRecorder, kind, text string) int {
	n := 0
	for _, e := range r.snapshot() {
		if e.Kind == kind && (text == "" || e.Text == text) {
			n++
		}
	}
	return n
}

// TestPiProcessBusySteerDelayedAckThroughProcess verifies a busy steer
// through the Process: the delivery returns when the steer RPC is accepted (not
// when the marker is acknowledged), and the turn settles after the delayed marker.
func TestPiProcessBusySteerDelayedAckThroughProcess(t *testing.T) {
	dir := t.TempDir()
	barrier := filepath.Join(dir, "release")
	marker := "<!-- agentteam-runtime-input:proc-steer -->"
	usage := piFakeUsage(1, 1, 0, 2)
	scenario := []piFakeScript{
		piFakePromptScript(
			piFakeAgentStart(),
			piFakeTurnStart(),
		),
		piFakeSteerScript(
			piFakeUserMessage("proc-steer", "steer\n\n"+marker),
			piFakeUserMessageEnd("proc-steer", "steer\n\n"+marker),
			piFakeAssistantEnd([]map[string]any{piFakeTextBlock("busy")}, usage),
			piFakeTurnEnd([]map[string]any{piFakeTextBlock("busy")}, usage),
			piFakeAgentEnd(false),
			piFakeAgentSettled(),
		).withWaitFor(barrier),
	}
	binary, _ := writeFakePiRPCModeBinary(t, scenario)
	cfg := testLaunchConfig(drivercontract.LaunchConfig{AgentID: "pi-process-steer", RuntimeType: runtimeconfig.TypePi, Binary: binary, WorkDir: t.TempDir(), RuntimeStateDir: t.TempDir(), PermissionMode: "full_access", Model: "gpt-5.5", Env: map[string]string{}}, testLaunchSurfaces{Prompt: "start"})
	recorder := &piProcessEventRecorder{}
	process, err := NewProcess(t.Context(), &PiRPCDriver{}, cfg, ProcessOptions{
		OnEvent: recorder.record,
	})
	require.NoError(t, err)
	t.Cleanup(func() {
		process.Stop()
		_ = process.WaitForExit(5 * time.Second)
	})

	// Wait for the process to be active (root run admitted).
	require.Eventually(t, func() bool {
		return process.GetSessionID() != ""
	}, 10*time.Second, 20*time.Millisecond)

	// Deliver the busy steer through Process. It must return when the steer RPC
	// is accepted, NOT when the (barrier-delayed) marker is acknowledged.
	deliverDone := make(chan error, 1)
	go func() {
		deliverDone <- process.DeliverForHandoff(t.Context(), types.InboxMessage{
			MessageID: "proc-steer-msg",
			Target:    "#pi-test:thread",
			Content:   "steer now",
		})
	}()
	select {
	case err := <-deliverDone:
		require.NoError(t, err, "Process busy steer returns on RPC acceptance")
	case <-time.After(10 * time.Second):
		t.Fatal("Process busy steer did not return before the marker acked")
	}

	// The marker is still delayed: release it and wait for the turn to settle.
	require.NoError(t, os.WriteFile(barrier, []byte("go"), 0o600))
	require.Eventually(t, func() bool {
		return recorder.count(consts.ParsedEventKindRuntimeIdle) >= 1
	}, 20*time.Second, 50*time.Millisecond)
	require.True(t, process.IsIdle(), "Process idle after steer turn settles")
}

func TestPiProcessRestoredExistingJSONLWithoutMarkerFailsClosedFIFO(t *testing.T) {
	stateDir := t.TempDir()
	sessionID := "00000000-0000-0000-0000-000000000001"
	sessionDir := filepath.Join(stateDir, ".pi-sessions")
	require.NoError(t, os.MkdirAll(sessionDir, 0o700))
	// This is a readable appendable snapshot from before q1's user marker. It
	// is deliberately not a negative fence: the replacement must not replay q1.
	require.NoError(t, os.WriteFile(
		filepath.Join(sessionDir, "2026_"+sessionID+".jsonl"),
		[]byte(`{"type":"session","id":"`+sessionID+`"}`+"\n"),
		0o600,
	))

	usage := piFakeUsage(2, 1, 0, 3)
	scenario := []piFakeScript{
		piFakePromptScript(
			piFakeUserMessage("q2", "q2\n\n<!-- agentteam-runtime-input:q2 -->"),
			piFakeUserMessageEnd("q2", "q2\n\n<!-- agentteam-runtime-input:q2 -->"),
			piFakeAssistantEnd([]map[string]any{piFakeTextBlock("q2 reply")}, usage),
			piFakeTurnEnd([]map[string]any{piFakeTextBlock("q2 reply")}, usage),
			piFakeAgentEnd(false),
			piFakeAgentSettled(),
		),
	}
	success := true
	scenario = append([]piFakeScript{{Cmd: "get_state", Resp: &success, Data: map[string]any{"sessionId": sessionID}}}, scenario...)
	binary, _ := writeFakePiRPCModeBinary(t, scenario)
	cfg := drivercontract.LaunchConfig{
		AgentID:         "pi-restored-existing-jsonl",
		RuntimeType:     runtimeconfig.TypePi,
		Binary:          binary,
		WorkDir:         t.TempDir(),
		RuntimeStateDir: stateDir,
		SessionID:       sessionID,
		PermissionMode:  "full_access",
		Model:           "gpt-5.5",
		DisableRecovery: true,
		Env:             map[string]string{},
	}
	recorder := &piProcessEventRecorder{}
	process, err := NewProcess(t.Context(), &PiRPCDriver{}, cfg, ProcessOptions{
		OnEvent: recorder.record,
	})
	require.NoError(t, err)
	t.Cleanup(func() {
		process.Stop()
		_ = process.WaitForExit(5 * time.Second)
	})
	process.pendingReconcileMaxAttempts = 2
	process.pendingReconcileSuccessGrace = time.Millisecond
	process.inboxRetryDelay = func(int) time.Duration { return time.Millisecond }

	acceptedAt := time.Now().Add(-time.Minute).UTC()
	store := &memoryDeliveryOwnershipStore{records: map[string]DeliveryOwnershipRecord{
		"q1": {
			Message:        types.InboxMessage{MessageID: "q1", TargetKey: "thread-1", TargetSeq: 1, Target: "#ops", Content: "q1 must never replay"},
			Input:          drivercontract.RuntimeInput{ID: "q1"},
			SessionID:      sessionID,
			State:          DeliveryOwnershipInflight,
			InflightReason: DeliveryInflightDispatching,
			AcceptedAt:     acceptedAt,
		},
		"q2": {
			Message:    types.InboxMessage{MessageID: "q2", TargetKey: "thread-1", TargetSeq: 2, Target: "#ops", Content: "q2 advances after q1 terminal"},
			Input:      drivercontract.RuntimeInput{ID: "q2"},
			SessionID:  sessionID,
			State:      DeliveryOwnershipQueued,
			AcceptedAt: acceptedAt.Add(time.Second),
		},
	}}
	require.NoError(t, process.SetDeliveryOwnershipStore(store))

	require.Eventually(t, func() bool {
		q1, q1OK := store.record("q1")
		q2, q2OK := store.record("q2")
		return q1OK && q1.IsFailed() &&
			q1.FailureCode == "runtime_input_reconciliation_exhausted" &&
			q2OK && q2.IsAcked()
	}, 10*time.Second, 10*time.Millisecond)

	// The connected Pi driver, not a result-map stub, performed q1
	// reconciliation. No q1 prompt is present in the fake scenario, and q2
	// only reaches Pi after q1's durable failed tombstone releases FIFO.
	require.Equal(t, 0, countKinds(recorder, consts.ParsedEventKindText, "q1 must never replay"))
	require.Equal(t, 1, countKinds(recorder, consts.ParsedEventKindText, "q2 reply"))

	// A late q1 marker is terminal-monotonic positive evidence. It upgrades
	// the failed tombstone but cannot reopen a wake or replay q1 after q2.
	sessionPath := filepath.Join(sessionDir, "2026_"+sessionID+".jsonl")
	file, err := os.OpenFile(sessionPath, os.O_APPEND|os.O_WRONLY, 0o600)
	require.NoError(t, err)
	_, err = file.WriteString(`{"type":"message","message":{"role":"user","content":"q1\n\n<!-- agentteam-runtime-input:q1 -->"}}` + "\n")
	require.NoError(t, err)
	require.NoError(t, file.Close())
	driver, ok := process.driver.(*PiRPCDriver)
	require.True(t, ok)
	got, err := driver.ReconcileRuntimeInput(t.Context(), "q1", sessionID)
	require.NoError(t, err)
	require.Equal(t, RuntimeInputReconciliationAcked, got)
	require.True(t, process.handleRuntimeInputAcked(drivercontract.ParsedEvent{
		Kind:           consts.ParsedEventKindRuntimeInputAcked,
		RuntimeInputID: "q1",
		SessionID:      sessionID,
	}))
	require.Eventually(t, func() bool {
		q1, ok := store.record("q1")
		return ok && q1.IsAcked()
	}, time.Second, 10*time.Millisecond)
	require.Equal(t, 1, countKinds(recorder, consts.ParsedEventKindText, "q2 reply"))
}

func TestPiProcessRetriesLiveIdlePromptAcceptedWithoutMarker(t *testing.T) {
	usage := piFakeUsage(2, 1, 0, 3)
	scenario := []piFakeScript{
		// Pi acknowledges preflight but then reports idle with no in-memory user
		// entry: the first mutation is authoritatively absent.
		piFakePromptScript(),
		piFakeStateScript(false),
		piFakeEntriesScript(),
		// Process returns the same durable body to queued and retries it once.
		piFakePromptScript(
			piFakeAgentStart(),
			piFakeUserMessage("q1", "q1 body\n\n<!-- agentteam-runtime-input:q1 -->"),
			piFakeUserMessageEnd("q1", "q1 body\n\n<!-- agentteam-runtime-input:q1 -->"),
			piFakeAssistantEnd([]map[string]any{piFakeTextBlock("q1 reply")}, usage),
			piFakeTurnEnd([]map[string]any{piFakeTextBlock("q1 reply")}, usage),
			piFakeAgentEnd(false),
			piFakeAgentSettled(),
		),
	}
	binary, _ := writeFakePiRPCModeBinary(t, scenario)
	cfg := drivercontract.LaunchConfig{
		AgentID:         "pi-live-idle-retry",
		RuntimeType:     runtimeconfig.TypePi,
		Binary:          binary,
		WorkDir:         t.TempDir(),
		RuntimeStateDir: t.TempDir(),
		PermissionMode:  "full_access",
		Model:           "gpt-5.5",
		Env:             map[string]string{},
	}
	recorder := &piProcessEventRecorder{}
	process, err := NewProcess(t.Context(), &PiRPCDriver{}, cfg, ProcessOptions{OnEvent: recorder.record})
	require.NoError(t, err)
	t.Cleanup(func() {
		process.Stop()
		_ = process.WaitForExit(5 * time.Second)
	})
	process.pendingReconcileSuccessGrace = time.Millisecond
	process.inboxRetryDelay = func(int) time.Duration { return time.Millisecond }
	store := &memoryDeliveryOwnershipStore{records: make(map[string]DeliveryOwnershipRecord)}
	require.NoError(t, process.SetDeliveryOwnershipStore(store))
	require.Eventually(t, func() bool {
		return recorder.count(consts.ParsedEventKindSessionInit) >= 1 &&
			process.GetSessionID() != "" && process.IsIdle()
	}, 5*time.Second, 10*time.Millisecond)

	require.NoError(t, process.DeliverForHandoff(t.Context(), types.InboxMessage{
		MessageID: "q1", Target: "#ops", Content: "q1 body",
	}))
	require.Eventually(t, func() bool {
		record, ok := store.record("q1")
		return ok && record.IsAcked()
	}, 10*time.Second, 10*time.Millisecond)
	require.Equal(t, 1, countKinds(recorder, consts.ParsedEventKindText, "q1 reply"),
		"the durable body is acked exactly once after authoritative absent")
}

func TestPiProcessRemainsBusyThroughTurnEndBeforeSettled(t *testing.T) {
	usage := piFakeUsage(10, 5, 0, 15)
	scenario := []piFakeScript{
		piFakePromptScript(
			piFakeAgentStart(),
			piFakeTurnStart(),
			piFakeAssistantStart(nil, nil),
			piFakeTextDelta(0, "Hello", usage),
			piFakeAssistantEnd([]map[string]any{piFakeTextBlock("Hello")}, usage),
			piFakeTurnEnd([]map[string]any{piFakeTextBlock("Hello")}, usage),
			piFakeAgentEnd(false),
			// No agent_settled: the root run stays active.
		),
	}
	binary, _ := writeFakePiRPCModeBinary(t, scenario)
	cfg := testLaunchConfig(drivercontract.LaunchConfig{AgentID: "pi-process-busy-turn", RuntimeType: runtimeconfig.TypePi, Binary: binary, WorkDir: t.TempDir(), RuntimeStateDir: t.TempDir(), PermissionMode: "full_access", Model: "gpt-5.5", Env: map[string]string{}}, testLaunchSurfaces{Prompt: "hello"})
	recorder := &piProcessEventRecorder{}
	process, err := NewProcess(t.Context(), &PiRPCDriver{}, cfg, ProcessOptions{
		OnEvent: recorder.record,
	})
	require.NoError(t, err)
	t.Cleanup(func() {
		process.Stop()
		_ = process.WaitForExit(5 * time.Second)
	})

	// Wait for the text event (after Pi turn_end + agent_end) while no
	// agent_settled follows.
	require.Eventually(t, func() bool {
		return recorder.count(consts.ParsedEventKindText) >= 1
	}, 15*time.Second, 50*time.Millisecond)
	require.Equal(t, 1, recorder.count(consts.ParsedEventKindText), "text emitted")
	// The Process must still be busy: no root TurnEnd boundary was reached.
	require.Equal(t, 0, recorder.count(consts.ParsedEventKindRuntimeIdle), "no RuntimeIdle before agent_settled")
	require.False(t, process.IsIdle(), "Process remains busy until agent_settled")
}
