#if canImport(Darwin)
import Darwin
#else
import Glibc
#endif
import Foundation
import Testing
@testable import CodexBarCore

private final class FakeAntigravityProcessHandle: AntigravityCLIProcessHandle, @unchecked Sendable {
    private let lock = NSLock()
    let pid: pid_t
    var descendants: [pid_t]
    private var running: Bool
    private let terminateRootStopsProcess: Bool
    private var assignedProcessGroup: pid_t?
    private var events: [String] = []
    private var drainOutputChunks: [Data] = []

    init(pid: pid_t, running: Bool = true, descendants: [pid_t] = [], terminateRootStopsProcess: Bool = true) {
        self.pid = pid
        self.running = running
        self.descendants = descendants
        self.terminateRootStopsProcess = terminateRootStopsProcess
    }

    var isRunning: Bool {
        self.lock.lock()
        let value = self.running
        self.events.append("isRunning:\(value)")
        self.lock.unlock()
        return value
    }

    var processGroup: pid_t? {
        self.lock.lock()
        let value = self.assignedProcessGroup
        self.lock.unlock()
        return value
    }

    func assignProcessGroup() -> pid_t? {
        self.lock.lock()
        self.assignedProcessGroup = self.pid
        self.events.append("assignProcessGroup")
        self.lock.unlock()
        return self.pid
    }

    func sendExit() throws {
        self.append("sendExit")
    }

    func closePTY() {
        self.append("closePTY")
    }

    func terminateRoot() {
        self.lock.lock()
        if self.terminateRootStopsProcess {
            self.running = false
        }
        self.events.append("terminateRoot")
        self.lock.unlock()
    }

    func killRoot() {
        self.lock.lock()
        self.running = false
        self.events.append("killRoot")
        self.lock.unlock()
    }

    func descendantPIDs() -> [pid_t] {
        self.lock.lock()
        let value = self.descendants
        self.events.append("descendantPIDs")
        self.lock.unlock()
        return value
    }

    func terminateTree(signal: Int32, knownDescendants _: [pid_t]) {
        self.lock.lock()
        if signal == SIGKILL {
            self.running = false
        }
        self.events.append("terminateTree:\(signal)")
        self.lock.unlock()
    }

    func killDescendants(_ descendants: [pid_t]) {
        self.append("killDescendants:\(descendants.map(String.init).joined(separator: ","))")
    }

    func drainOutput() -> Data {
        self.lock.lock()
        self.events.append("drainOutput")
        let output = self.drainOutputChunks.isEmpty ? Data() : self.drainOutputChunks.removeFirst()
        self.lock.unlock()
        return output
    }

    func enqueueDrainOutput(_ output: Data) {
        self.lock.lock()
        self.drainOutputChunks.append(output)
        self.lock.unlock()
    }

    func snapshotEvents() -> [String] {
        self.lock.lock()
        let value = self.events
        self.lock.unlock()
        return value
    }

    private func append(_ event: String) {
        self.lock.lock()
        self.events.append(event)
        self.lock.unlock()
    }
}

private final class FakeAntigravityProcessLauncher: AntigravityCLIProcessLaunching, @unchecked Sendable {
    private let lock = NSLock()
    private var nextPID: pid_t
    private var launchError: Error?
    private var launchedBinaries: [String] = []
    private var terminateRootStopsProcess = true
    private var handles: [FakeAntigravityProcessHandle] = []

    init(nextPID: pid_t = 1) {
        self.nextPID = nextPID
    }

    func launch(binary: String) throws -> any AntigravityCLIProcessHandle {
        self.lock.lock()
        defer { self.lock.unlock() }
        if let launchError {
            throw launchError
        }
        let handle = FakeAntigravityProcessHandle(
            pid: self.nextPID,
            descendants: [self.nextPID + 100],
            terminateRootStopsProcess: self.terminateRootStopsProcess)
        self.nextPID += 1
        self.launchedBinaries.append(binary)
        self.handles.append(handle)
        return handle
    }

    func setLaunchError(_ error: Error?) {
        self.lock.lock()
        self.launchError = error
        self.lock.unlock()
    }

    func setTerminateRootStopsProcess(_ value: Bool) {
        self.lock.lock()
        self.terminateRootStopsProcess = value
        self.lock.unlock()
    }

    func launchedBinarySnapshot() -> [String] {
        self.lock.lock()
        let value = self.launchedBinaries
        self.lock.unlock()
        return value
    }

    func handleSnapshot() -> [FakeAntigravityProcessHandle] {
        self.lock.lock()
        let value = self.handles
        self.lock.unlock()
        return value
    }
}

private final class FakeAntigravityIdentityProvider: AntigravityCLIProcessIdentityProviding, @unchecked Sendable {
    private let lock = NSLock()
    private var identities: [pid_t: AntigravityCLIProcessIdentity] = [:]

    func setIdentity(pid: pid_t, executablePath: String, startEpoch: TimeInterval) {
        self.lock.lock()
        self.identities[pid] = AntigravityCLIProcessIdentity(executablePath: executablePath, startEpoch: startEpoch)
        self.lock.unlock()
    }

    func removeIdentity(pid: pid_t) {
        self.lock.lock()
        self.identities[pid] = nil
        self.lock.unlock()
    }

    func identity(for pid: pid_t) -> AntigravityCLIProcessIdentity? {
        self.lock.lock()
        let value = self.identities[pid]
        self.lock.unlock()
        return value
    }
}

private final class MemoryAntigravitySessionRecordStore: AntigravityCLISessionRecordStoring, @unchecked Sendable {
    private let lock = NSLock()
    private var records: [AntigravityCLISessionRecord]
    private let failSaves: Bool
    private var saves = 0
    private var removes = 0

    init(record: AntigravityCLISessionRecord? = nil, failSaves: Bool = false) {
        self.records = record.map { [$0] } ?? []
        self.failSaves = failSaves
    }

    func load() throws -> [AntigravityCLISessionRecord] {
        self.lock.lock()
        let value = self.records
        self.lock.unlock()
        return value
    }

    func save(_ record: AntigravityCLISessionRecord) throws {
        self.lock.lock()
        guard !self.failSaves else {
            self.lock.unlock()
            throw CocoaError(.fileWriteNoPermission)
        }
        self.records.removeAll { existing in
            if let existingOwnerPID = existing.ownerPID,
               let recordOwnerPID = record.ownerPID,
               let existingOwnerPath = existing.ownerExecutablePath,
               let recordOwnerPath = record.ownerExecutablePath,
               let existingOwnerStart = existing.ownerStartEpoch,
               let recordOwnerStart = record.ownerStartEpoch
            {
                return existingOwnerPID == recordOwnerPID &&
                    existingOwnerPath == recordOwnerPath &&
                    abs(existingOwnerStart - recordOwnerStart) < 0.001
            }
            return existing.pid == record.pid &&
                existing.executablePath == record.executablePath &&
                abs(existing.startEpoch - record.startEpoch) < 0.001
        }
        self.records.append(record)
        self.saves += 1
        self.lock.unlock()
    }

    func remove(_ record: AntigravityCLISessionRecord) throws {
        self.lock.lock()
        self.records.removeAll {
            $0.pid == record.pid &&
                $0.executablePath == record.executablePath &&
                abs($0.startEpoch - record.startEpoch) < 0.001
        }
        self.removes += 1
        self.lock.unlock()
    }

    func snapshot() -> AntigravityCLISessionRecord? {
        self.lock.lock()
        let value = self.records.first
        self.lock.unlock()
        return value
    }

    func snapshots() -> [AntigravityCLISessionRecord] {
        self.lock.lock()
        let value = self.records
        self.lock.unlock()
        return value
    }

    var saveCount: Int {
        self.lock.lock()
        let value = self.saves
        self.lock.unlock()
        return value
    }

    var removeCount: Int {
        self.lock.lock()
        let value = self.removes
        self.lock.unlock()
        return value
    }
}

private final class MemoryAntigravitySessionLaunchLock: AntigravityCLISessionLaunchLocking, @unchecked Sendable {
    private let lock = NSLock()

    func withLock<T>(_ operation: () throws -> T) throws -> T {
        self.lock.lock()
        defer { self.lock.unlock() }
        return try operation()
    }
}

private struct FailingAntigravitySessionLaunchLock: AntigravityCLISessionLaunchLocking {
    func withLock<T>(_: () throws -> T) throws -> T {
        throw CocoaError(.fileWriteNoPermission)
    }
}

private final class AntigravitySessionTerminationRecorder: @unchecked Sendable {
    private let lock = NSLock()
    private var events: [(pid: pid_t, group: pid_t?, signal: Int32, descendants: [pid_t])] = []

    func append(pid: pid_t, group: pid_t?, signal: Int32, descendants: [pid_t]) {
        self.lock.lock()
        self.events.append((pid: pid, group: group, signal: signal, descendants: descendants))
        self.lock.unlock()
    }

    func snapshot() -> [(pid: pid_t, group: pid_t?, signal: Int32, descendants: [pid_t])] {
        self.lock.lock()
        let value = self.events
        self.lock.unlock()
        return value
    }
}

private final class AntigravityRegistryRecorder: @unchecked Sendable {
    private let lock = NSLock()
    private var shouldRegister = true
    private var registered: [pid_t] = []
    private var unregistered: [pid_t] = []
    private var groups: [pid_t: pid_t?] = [:]

    func setShouldRegister(_ value: Bool) {
        self.lock.lock()
        self.shouldRegister = value
        self.lock.unlock()
    }

    func register(pid: pid_t, _: String) -> Bool {
        self.lock.lock()
        defer { self.lock.unlock() }
        guard self.shouldRegister else { return false }
        self.registered.append(pid)
        return true
    }

    func update(pid: pid_t, group: pid_t?) {
        self.lock.lock()
        self.groups[pid] = group
        self.lock.unlock()
    }

    func unregister(pid: pid_t) {
        self.lock.lock()
        self.unregistered.append(pid)
        self.lock.unlock()
    }

    func registeredSnapshot() -> [pid_t] {
        self.lock.lock()
        let value = self.registered
        self.lock.unlock()
        return value
    }

    func unregisteredSnapshot() -> [pid_t] {
        self.lock.lock()
        let value = self.unregistered
        self.lock.unlock()
        return value
    }
}

private final class AntigravityLaunchReservationRecorder: @unchecked Sendable {
    private let lock = NSLock()
    private var beginCount = 0
    private var endCount = 0

    func begin() -> Bool {
        self.lock.lock()
        self.beginCount += 1
        self.lock.unlock()
        return true
    }

    func end() {
        self.lock.lock()
        self.endCount += 1
        self.lock.unlock()
    }

    func counts() -> (begin: Int, end: Int) {
        self.lock.lock()
        let counts = (begin: self.beginCount, end: self.endCount)
        self.lock.unlock()
        return counts
    }
}

private final class AntigravityManualSleeper: @unchecked Sendable {
    private let lock = NSLock()
    private var continuations: [CheckedContinuation<Void, Error>] = []

    func sleep(_: UInt64) async throws {
        try await withCheckedThrowingContinuation { continuation in
            self.lock.lock()
            self.continuations.append(continuation)
            self.lock.unlock()
        }
    }

    func resumeAll() {
        self.lock.lock()
        let continuations = self.continuations
        self.continuations.removeAll()
        self.lock.unlock()

        for continuation in continuations {
            continuation.resume()
        }
    }

    func waitForSleeps(_ expectedCount: Int) async {
        for _ in 0..<200 {
            if self.pendingSleepCount >= expectedCount { return }
            try? await Task.sleep(nanoseconds: 1_000_000)
        }
        Issue.record("Timed out waiting for \(expectedCount) sleep continuation(s)")
    }

    private var pendingSleepCount: Int {
        self.lock.lock()
        let count = self.continuations.count
        self.lock.unlock()
        return count
    }
}

struct AntigravityCLISessionTests {
    @Test
    func `reuses alive process for same binary`() async throws {
        let fixture = self.makeFixture()
        fixture.identity.setIdentity(pid: 10, executablePath: "/bin/agy", startEpoch: 100)

        let firstPID = try await fixture.session.beginProbe(binary: "/bin/agy")
        await fixture.session.finishProbe(success: true, resetAfterFetch: false)
        let secondPID = try await fixture.session.beginProbe(binary: "/bin/agy")
        await fixture.session.finishProbe(success: true, resetAfterFetch: false)

        #expect(firstPID == 10)
        #expect(secondPID == 10)
        #expect(fixture.launcher.launchedBinarySnapshot() == ["/bin/agy"])
        #expect(fixture.launchReservations.counts().begin == 1)
        #expect(fixture.launchReservations.counts().end == 1)
    }

    @Test
    func `relaunches when binary changes`() async throws {
        let fixture = self.makeFixture()
        fixture.identity.setIdentity(pid: 10, executablePath: "/bin/agy", startEpoch: 100)
        fixture.identity.setIdentity(pid: 11, executablePath: "/new/agy", startEpoch: 101)

        _ = try await fixture.session.beginProbe(binary: "/bin/agy")
        await fixture.session.finishProbe(success: true, resetAfterFetch: false)
        let secondPID = try await fixture.session.beginProbe(binary: "/new/agy")
        await fixture.session.finishProbe(success: true, resetAfterFetch: false)

        #expect(secondPID == 11)
        #expect(fixture.launcher.launchedBinarySnapshot() == ["/bin/agy", "/new/agy"])
        #expect(fixture.registry.unregisteredSnapshot() == [10])
    }

    @Test
    func `replacement launch waits for in progress teardown`() async throws {
        let fixture = self.makeFixture(
            manualSleep: true,
            terminationGracePeriod: 1,
            terminateRootStopsProcess: false)
        fixture.identity.setIdentity(pid: 10, executablePath: "/old/agy", startEpoch: 100)
        fixture.identity.setIdentity(pid: 11, executablePath: "/new/agy", startEpoch: 101)

        _ = try await fixture.session.beginProbe(binary: "/old/agy")
        await fixture.session.finishProbe(success: true, resetAfterFetch: false)

        let firstReplacement = Task {
            try await fixture.session.beginProbe(binary: "/new/agy")
        }
        // Two sleeps register here: the lingering idle-timer sleep (armed by the prior finishProbe;
        // the fake sleeper does not honor cancellation) and the teardown grace-period sleep. Wait for
        // both before resuming — waiting for only one lets resumeAll() fire before the grace sleep
        // parks, stranding it so teardown never completes and the suite hangs to the 120s timeout.
        await fixture.sleeper?.waitForSleeps(2)

        let secondReplacement = Task {
            try await fixture.session.beginProbe(binary: "/new/agy")
        }
        await Task.yield()
        #expect(fixture.launcher.launchedBinarySnapshot() == ["/old/agy"])

        fixture.launcher.handleSnapshot().first?.killRoot()
        fixture.sleeper?.resumeAll()
        let firstPID = try await firstReplacement.value
        let secondPID = try await secondReplacement.value
        await fixture.session.finishProbe(success: true, resetAfterFetch: false)
        await fixture.session.finishProbe(success: true, resetAfterFetch: false)

        #expect(firstPID == 11)
        #expect(secondPID == 11)
        #expect(fixture.launcher.launchedBinarySnapshot() == ["/old/agy", "/new/agy"])
    }

    @Test
    func `replacement waits for active probe before relaunching`() async throws {
        let fixture = self.makeFixture()
        fixture.identity.setIdentity(pid: 10, executablePath: "/old/agy", startEpoch: 100)
        fixture.identity.setIdentity(pid: 11, executablePath: "/new/agy", startEpoch: 101)

        let firstPID = try await fixture.session.beginProbe(binary: "/old/agy")
        let replacement = Task {
            try await fixture.session.beginProbe(binary: "/new/agy")
        }
        await Task.yield()
        await Task.yield()

        #expect(firstPID == 10)
        #expect(fixture.launcher.launchedBinarySnapshot() == ["/old/agy"])
        #expect(fixture.registry.unregisteredSnapshot().isEmpty)

        await fixture.session.finishProbe(success: true, resetAfterFetch: false)
        let secondPID = try await replacement.value
        await fixture.session.finishProbe(success: true, resetAfterFetch: false)

        #expect(secondPID == 11)
        #expect(fixture.launcher.launchedBinarySnapshot() == ["/old/agy", "/new/agy"])
        #expect(fixture.registry.unregisteredSnapshot() == [10])
    }

    @Test
    func `queued replacement hard stops a signed out process`() async throws {
        let fixture = self.makeFixture()
        fixture.identity.setIdentity(pid: 10, executablePath: "/old/agy", startEpoch: 100)
        fixture.identity.setIdentity(pid: 11, executablePath: "/new/agy", startEpoch: 101)

        _ = try await fixture.session.beginProbe(binary: "/old/agy")
        let replacement = Task {
            try await fixture.session.beginProbe(binary: "/new/agy")
        }
        for _ in 0..<100 where await fixture.session.activeProbeCountForTesting < 2 {
            await Task.yield()
        }
        #expect(await fixture.session.activeProbeCountForTesting == 2)

        await fixture.session.finishProbe(success: false, resetAfterFetch: true, forceTerminate: true)
        let replacementPID = try await replacement.value
        let oldEvents = try #require(fixture.launcher.handleSnapshot().first).snapshotEvents()
        #expect(await fixture.session.lastStopReasonForTesting == "authentication required")
        await fixture.session.finishProbe(success: true, resetAfterFetch: true)

        #expect(replacementPID == 11)
        #expect(!oldEvents.contains("sendExit"))
        #expect(oldEvents.contains("terminateRoot"))
    }

    @Test
    func `replacement ignores queued starters while waiting for active probe`() async throws {
        let fixture = self.makeFixture()
        fixture.identity.setIdentity(pid: 10, executablePath: "/old/agy", startEpoch: 100)
        fixture.identity.setIdentity(pid: 11, executablePath: "/new/agy", startEpoch: 101)

        _ = try await fixture.session.beginProbe(binary: "/old/agy")
        let firstReplacement = Task {
            try await fixture.session.beginProbe(binary: "/new/agy")
        }
        let secondReplacement = Task {
            try await fixture.session.beginProbe(binary: "/new/agy")
        }
        await Task.yield()
        await Task.yield()

        #expect(fixture.launcher.launchedBinarySnapshot() == ["/old/agy"])

        await fixture.session.finishProbe(success: true, resetAfterFetch: false)
        let didLaunchReplacement = await self.waitForLaunches(fixture.launcher, count: 2)
        #expect(didLaunchReplacement)
        if !didLaunchReplacement {
            await fixture.session.finishProbe(success: true, resetAfterFetch: false)
        }

        let firstPID = try await firstReplacement.value
        let secondPID = try await secondReplacement.value
        await fixture.session.finishProbe(success: true, resetAfterFetch: false)
        await fixture.session.finishProbe(success: true, resetAfterFetch: false)

        #expect(firstPID == 11)
        #expect(secondPID == 11)
        #expect(fixture.launcher.launchedBinarySnapshot() == ["/old/agy", "/new/agy"])
    }

    @Test
    func `relaunches when existing process is dead`() async throws {
        let fixture = self.makeFixture()
        fixture.identity.setIdentity(pid: 10, executablePath: "/bin/agy", startEpoch: 100)
        fixture.identity.setIdentity(pid: 11, executablePath: "/bin/agy", startEpoch: 101)

        _ = try await fixture.session.beginProbe(binary: "/bin/agy")
        await fixture.session.finishProbe(success: true, resetAfterFetch: false)
        fixture.launcher.handleSnapshot().first?.terminateRoot()
        let secondPID = try await fixture.session.beginProbe(binary: "/bin/agy")
        await fixture.session.finishProbe(success: true, resetAfterFetch: false)

        #expect(secondPID == 11)
        #expect(fixture.launcher.launchedBinarySnapshot() == ["/bin/agy", "/bin/agy"])
    }

    @Test
    func `pty launcher creates dedicated process group before returning`() throws {
        let launcher = AntigravityPTYProcessLauncher()
        let handle = try launcher.launch(binary: "/bin/cat")
        defer {
            handle.killRoot()
            handle.terminateTree(signal: SIGKILL, knownDescendants: [])
            handle.closePTY()
        }

        #expect(handle.processGroup == handle.pid)
        #expect(getpgid(handle.pid) == handle.pid)
    }

    @Test
    func `pty launcher resets termination signals for child process`() {
        var signals = AntigravityPTYProcessLauncher.defaultSignalsForSpawn()

        #expect(sigismember(&signals, SIGINT) == 1)
        #expect(sigismember(&signals, SIGTERM) == 1)
        #expect(sigismember(&signals, SIGHUP) == 1)
    }

    @Test
    func `pty launcher retries transient text busy spawn errors`() {
        var attempts = 0

        let result = AntigravityPTYProcessLauncher.spawnWithTextBusyRetry(retryDelay: 0) {
            attempts += 1
            return attempts < 3 ? ETXTBSY : 0
        }

        #expect(result == 0)
        #expect(attempts == 3)
    }

    @Test
    func `pty launcher does not retry other spawn errors`() {
        var attempts = 0

        let result = AntigravityPTYProcessLauncher.spawnWithTextBusyRetry(retryDelay: 0) {
            attempts += 1
            return EACCES
        }

        #expect(result == EACCES)
        #expect(attempts == 1)
    }

    @Test
    func `pty launcher uses home and closes unrelated descriptors`() throws {
        let tempDirectory = FileManager.default.temporaryDirectory
            .appendingPathComponent("antigravity-spawn-\(UUID().uuidString)", isDirectory: true)
        try FileManager.default.createDirectory(at: tempDirectory, withIntermediateDirectories: true)
        defer { try? FileManager.default.removeItem(at: tempDirectory) }

        let inheritedSourceFD = open("/dev/null", O_RDONLY)
        guard inheritedSourceFD >= 0 else {
            Issue.record("Failed to open descriptor fixture")
            return
        }
        defer { close(inheritedSourceFD) }
        let inheritedFD = fcntl(inheritedSourceFD, F_DUPFD, 200)
        guard inheritedFD >= 200 else {
            Issue.record("Failed to duplicate descriptor fixture")
            return
        }
        defer { close(inheritedFD) }

        let outputURL = tempDirectory.appendingPathComponent("result.txt")
        let script = """
        pwd > \(outputURL.path)
        if [ -e /dev/fd/\(inheritedFD) ] || [ -e /proc/self/fd/\(inheritedFD) ]; then
          echo inherited >> \(outputURL.path)
        else
          echo closed >> \(outputURL.path)
        fi
        """

        let handle = try AntigravityPTYProcessLauncher().launch(
            binary: "/bin/sh",
            arguments: ["-c", script])
        defer {
            handle.killRoot()
            handle.terminateTree(signal: SIGKILL, knownDescendants: [])
            handle.closePTY()
        }

        for _ in 0..<200 {
            if FileManager.default.fileExists(atPath: outputURL.path),
               let output = try? String(contentsOf: outputURL, encoding: .utf8)
            {
                let lines = output
                    .split(separator: "\n")
                    .map(String.init)
                if lines.count >= 2, output.hasSuffix("\n") { break }
            }
            Thread.sleep(forTimeInterval: 0.01)
        }
        let lines = try String(contentsOf: outputURL, encoding: .utf8)
            .split(separator: "\n")
            .map(String.init)
        #expect(lines == [NSHomeDirectory(), "closed"])
    }

    @Test
    func `spawned PTY drain is bounded per call`() throws {
        let temp = FileManager.default.temporaryDirectory
            .appendingPathComponent("antigravity-drain-\(UUID().uuidString)")
        defer { try? FileManager.default.removeItem(at: temp) }
        try Data(repeating: 1, count: 8192 * 65).write(to: temp)
        let primaryFD = open(temp.path, O_RDONLY)
        guard primaryFD >= 0 else {
            Issue.record("Failed to open temporary drain input")
            return
        }
        let secondaryFD = open("/dev/null", O_RDONLY)
        guard secondaryFD >= 0 else {
            close(primaryFD)
            Issue.record("Failed to open /dev/null")
            return
        }
        let handle = AntigravitySpawnedPTYProcessHandle(
            pid: getpid(),
            processGroup: getpgrp(),
            primaryFD: primaryFD,
            primaryHandle: FileHandle(fileDescriptor: primaryFD, closeOnDealloc: true),
            secondaryHandle: FileHandle(fileDescriptor: secondaryFD, closeOnDealloc: true))
        defer { handle.closePTY() }

        let output = handle.drainOutput()

        #expect(lseek(primaryFD, 0, SEEK_CUR) == off_t(8192 * 64))
        #expect(output.count == 8192 * 64)
    }

    @Test
    func `session keeps one rolling PTY buffer across concurrent probes`() async throws {
        let fixture = self.makeFixture()
        fixture.identity.setIdentity(pid: 10, executablePath: "/bin/agy", startEpoch: 100)

        _ = try await fixture.session.beginProbe(binary: "/bin/agy")
        _ = try await fixture.session.beginProbe(binary: "/bin/agy")
        let handle = try #require(fixture.launcher.handleSnapshot().first)
        handle.enqueueDrainOutput(Data([0xE2, 0x96]))
        let first = await fixture.session.drainOutput()
        handle.enqueueDrainOutput(Data([0x84]) + Data("Select login method:".utf8))
        let second = await fixture.session.drainOutput()
        let third = await fixture.session.drainOutput()

        #expect(first == Data([0xE2, 0x96]))
        #expect(AntigravityCLIHTTPSFetchStrategy.containsAuthenticationPrompt(second))
        #expect(third == second)

        await fixture.session.finishProbe(success: false, resetAfterFetch: true, forceTerminate: true)
        await fixture.session.finishProbe(success: false, resetAfterFetch: false)
    }

    @Test
    func `authentication prompt matcher tolerates prompt casing and spacing`() {
        #expect(AntigravityCLIHTTPSFetchStrategy.containsAuthenticationPrompt(
            Data("select  LOGIN\nmethod :".utf8)))
        #expect(AntigravityCLIHTTPSFetchStrategy.containsAuthenticationPrompt(
            Data("Select login method:".utf8)))
        #expect(!AntigravityCLIHTTPSFetchStrategy.containsAuthenticationPrompt(
            Data("You are currently not signed in".utf8)))
    }

    @Test
    func `session returns complete new output before retaining only its tail`() async throws {
        let fixture = self.makeFixture()
        fixture.identity.setIdentity(pid: 10, executablePath: "/bin/agy", startEpoch: 100)

        _ = try await fixture.session.beginProbe(binary: "/bin/agy")
        let handle = try #require(fixture.launcher.handleSnapshot().first)
        let prompt = Data("Select login method:".utf8)
        let oversizedRedraw = prompt + Data(repeating: 0x20, count: 8192)
        handle.enqueueDrainOutput(oversizedRedraw)

        let searchableOutput = await fixture.session.drainOutput()
        let retainedTail = await fixture.session.drainOutput()

        #expect(searchableOutput == oversizedRedraw)
        #expect(AntigravityCLIHTTPSFetchStrategy.containsAuthenticationPrompt(searchableOutput))
        #expect(AntigravityCLIHTTPSFetchStrategy.containsAuthenticationPrompt(retainedTail))

        await fixture.session.finishProbe(success: false, resetAfterFetch: true, forceTerminate: true)
    }

    @Test
    func `registration failure tears down launched process`() async {
        let fixture = self.makeFixture()
        fixture.registry.setShouldRegister(false)

        await #expect(throws: AntigravityCLISession.SessionError.self) {
            _ = try await fixture.session.beginProbe(binary: "/bin/agy")
        }

        let handle = fixture.launcher.handleSnapshot().first
        #expect(handle?.isRunning == false)
        #expect(handle?.snapshotEvents().contains("sendExit") == false)
        #expect(handle?.snapshotEvents().contains("closePTY") == true)
        #expect(handle?.snapshotEvents().contains("terminateRoot") == true)
        #expect(fixture.registry.registeredSnapshot().isEmpty)
    }

    @Test
    func `launch remains usable when coordination lock is unavailable`() async throws {
        let fixture = self.makeFixture(launchLock: FailingAntigravitySessionLaunchLock())
        fixture.identity.setIdentity(pid: 10, executablePath: "/bin/agy", startEpoch: 100)

        let pid = try await fixture.session.beginProbe(binary: "/bin/agy")
        await fixture.session.finishProbe(success: true, resetAfterFetch: true)

        #expect(pid == 10)
        #expect(fixture.store.snapshot() == nil)
    }

    @Test
    func `launch remains usable when ownership record cannot be saved`() async throws {
        let store = MemoryAntigravitySessionRecordStore(failSaves: true)
        let fixture = self.makeFixture(store: store)
        fixture.identity.setIdentity(pid: 10, executablePath: "/bin/agy", startEpoch: 100)

        let pid = try await fixture.session.beginProbe(binary: "/bin/agy")
        await fixture.session.finishProbe(success: true, resetAfterFetch: true)

        #expect(pid == 10)
        #expect(store.snapshot() == nil)
    }

    @Test
    func `idle window tears down warm process`() async throws {
        let fixture = self.makeFixture(idleWindow: 0.05, manualSleep: true)
        fixture.identity.setIdentity(pid: 10, executablePath: "/bin/agy", startEpoch: 100)

        _ = try await fixture.session.beginProbe(binary: "/bin/agy")
        await fixture.session.finishProbe(success: true, resetAfterFetch: false)
        await fixture.sleeper?.waitForSleeps(1)
        fixture.sleeper?.resumeAll()
        await self.waitUntilStopped(fixture.session)

        #expect(await fixture.session.isRunning == false)
        #expect(fixture.registry.unregisteredSnapshot() == [10])
        #expect(fixture.store.snapshot() == nil)
    }

    @Test
    func `host idle window extends the default session lifetime`() async throws {
        let fixture = self.makeFixture(idleWindow: 180)
        fixture.identity.setIdentity(pid: 10, executablePath: "/bin/agy", startEpoch: 100)

        _ = try await fixture.session.beginProbe(binary: "/bin/agy", idleWindow: 360)
        await fixture.session.finishProbe(success: true, resetAfterFetch: false)

        #expect(await fixture.session.idleWindowForTesting == 360)
    }

    @Test
    func `active probe prevents idle teardown until finish`() async throws {
        let fixture = self.makeFixture(idleWindow: 0.05, manualSleep: true)
        fixture.identity.setIdentity(pid: 10, executablePath: "/bin/agy", startEpoch: 100)

        _ = try await fixture.session.beginProbe(binary: "/bin/agy")
        await fixture.session.finishProbe(success: true, resetAfterFetch: false)
        _ = try await fixture.session.beginProbe(binary: "/bin/agy")
        await fixture.sleeper?.waitForSleeps(1)
        fixture.sleeper?.resumeAll()
        await Task.yield()

        #expect(await fixture.session.isRunning)
        await fixture.session.finishProbe(success: true, resetAfterFetch: false)
        await fixture.sleeper?.waitForSleeps(1)
        fixture.sleeper?.resumeAll()
        await self.waitUntilStopped(fixture.session)
        #expect(await fixture.session.isRunning == false)
    }

    @Test
    func `manual reset waits for active probe to finish`() async throws {
        let fixture = self.makeFixture()
        fixture.identity.setIdentity(pid: 10, executablePath: "/bin/agy", startEpoch: 100)

        _ = try await fixture.session.beginProbe(binary: "/bin/agy")
        await fixture.session.reset()
        #expect(await fixture.session.isRunning)
        await fixture.session.finishProbe(success: true, resetAfterFetch: false)

        #expect(await fixture.session.isRunning == false)
        #expect(fixture.registry.unregisteredSnapshot() == [10])
    }

    @Test
    func `reset reaps persisted stale session when no in memory process exists`() async {
        let store = MemoryAntigravitySessionRecordStore(record: AntigravityCLISessionRecord(
            pid: 777,
            requestedBinaryPath: "/bin/agy",
            executablePath: "/bin/agy",
            startEpoch: 42,
            processGroup: 777))
        let fixture = self.makeFixture(store: store)
        fixture.identity.setIdentity(pid: 777, executablePath: "/bin/agy", startEpoch: 42)

        await fixture.session.reset()

        let terminations = fixture.terminations.snapshot()
        #expect(terminations.map(\.signal) == [SIGTERM, SIGKILL])
        #expect(terminations.allSatisfy { $0.pid == 777 && $0.group == 777 })
        #expect(fixture.store.snapshot() == nil)
    }

    @Test
    func `reset preserves persisted session owned by another live process`() async {
        let store = MemoryAntigravitySessionRecordStore(record: AntigravityCLISessionRecord(
            pid: 777,
            requestedBinaryPath: "/bin/agy",
            executablePath: "/bin/agy",
            startEpoch: 42,
            processGroup: 777,
            ownerPID: 900,
            ownerExecutablePath: "/Applications/CodexBar.app/Contents/MacOS/CodexBar",
            ownerStartEpoch: 10))
        let fixture = self.makeFixture(store: store, currentProcessID: 901)
        fixture.identity.setIdentity(pid: 777, executablePath: "/bin/agy", startEpoch: 42)
        fixture.identity.setIdentity(
            pid: 900,
            executablePath: "/Applications/CodexBar.app/Contents/MacOS/CodexBar",
            startEpoch: 10)

        await fixture.session.reset()

        #expect(fixture.terminations.snapshot().isEmpty)
        #expect(fixture.store.snapshot() != nil)
    }

    @Test
    func `launch tracks an independent session while another process is live`() async throws {
        let protectedRecord = AntigravityCLISessionRecord(
            pid: 777,
            requestedBinaryPath: "/bin/agy",
            executablePath: "/bin/agy",
            startEpoch: 42,
            processGroup: 777,
            ownerPID: 900,
            ownerExecutablePath: "/Applications/CodexBar.app/Contents/MacOS/CodexBar",
            ownerStartEpoch: 10)
        let store = MemoryAntigravitySessionRecordStore(record: protectedRecord)
        let fixture = self.makeFixture(store: store, currentProcessID: 901)
        fixture.identity.setIdentity(pid: 777, executablePath: "/bin/agy", startEpoch: 42)
        fixture.identity.setIdentity(
            pid: 900,
            executablePath: "/Applications/CodexBar.app/Contents/MacOS/CodexBar",
            startEpoch: 10)
        fixture.identity.setIdentity(pid: 10, executablePath: "/bin/agy", startEpoch: 100)
        fixture.identity.setIdentity(pid: 901, executablePath: "/app/codexbar", startEpoch: 20)

        _ = try await fixture.session.beginProbe(binary: "/bin/agy")

        #expect(fixture.launcher.launchedBinarySnapshot() == ["/bin/agy"])
        #expect(fixture.terminations.snapshot().isEmpty)
        #expect(fixture.store.saveCount == 1)
        #expect(Set(fixture.store.snapshots().map(\.pid)) == [10, 777])

        await fixture.session.finishProbe(success: true, resetAfterFetch: true)
        #expect(fixture.store.snapshot() == protectedRecord)
    }

    @Test
    func `different binary tracks an independent session while another process is live`() async throws {
        let protectedRecord = AntigravityCLISessionRecord(
            pid: 777,
            requestedBinaryPath: "/bin/agy",
            executablePath: "/bin/agy",
            startEpoch: 42,
            processGroup: 777,
            ownerPID: 900,
            ownerExecutablePath: "/Applications/CodexBar.app/Contents/MacOS/CodexBar",
            ownerStartEpoch: 10)
        let store = MemoryAntigravitySessionRecordStore(record: protectedRecord)
        let fixture = self.makeFixture(store: store, currentProcessID: 901)
        fixture.identity.setIdentity(pid: 777, executablePath: "/bin/agy", startEpoch: 42)
        fixture.identity.setIdentity(
            pid: 900,
            executablePath: "/Applications/CodexBar.app/Contents/MacOS/CodexBar",
            startEpoch: 10)
        fixture.identity.setIdentity(pid: 10, executablePath: "/new/agy", startEpoch: 100)
        fixture.identity.setIdentity(pid: 901, executablePath: "/app/codexbar", startEpoch: 20)

        _ = try await fixture.session.beginProbe(binary: "/new/agy")

        #expect(fixture.launcher.launchedBinarySnapshot() == ["/new/agy"])
        #expect(fixture.store.saveCount == 1)
        #expect(Set(fixture.store.snapshots().map(\.pid)) == [10, 777])

        await fixture.session.finishProbe(success: true, resetAfterFetch: true)
        #expect(fixture.store.snapshot() == protectedRecord)
    }

    @Test
    func `concurrent hosts atomically track independent sessions`() async {
        let store = MemoryAntigravitySessionRecordStore()
        let launchLock = MemoryAntigravitySessionLaunchLock()
        let identity = FakeAntigravityIdentityProvider()
        let firstLauncher = FakeAntigravityProcessLauncher(nextPID: 10)
        let secondLauncher = FakeAntigravityProcessLauncher(nextPID: 20)
        identity.setIdentity(pid: 10, executablePath: "/bin/agy", startEpoch: 100)
        identity.setIdentity(pid: 20, executablePath: "/bin/agy", startEpoch: 200)
        identity.setIdentity(pid: 900, executablePath: "/app/CodexBar", startEpoch: 1)
        identity.setIdentity(pid: 901, executablePath: "/app/codexbar", startEpoch: 2)
        let first = self.makeFixture(
            launcher: firstLauncher,
            identity: identity,
            store: store,
            launchLock: launchLock,
            currentProcessID: 900)
        let second = self.makeFixture(
            launcher: secondLauncher,
            identity: identity,
            store: store,
            launchLock: launchLock,
            currentProcessID: 901)

        async let firstStarted = Self.beginPersistentSession(first.session)
        async let secondStarted = Self.beginPersistentSession(second.session)
        let results = await [firstStarted, secondStarted]

        #expect(!results.contains(false))
        #expect(firstLauncher.launchedBinarySnapshot().count + secondLauncher.launchedBinarySnapshot().count == 2)
        #expect(Set(store.snapshots().map(\.pid)) == [10, 20])

        await first.session.reset()
        await second.session.reset()
        #expect(store.snapshots().isEmpty)
    }
}

extension AntigravityCLISessionTests {
    @Test
    func `warm reuse reaps a crashed peer session`() async throws {
        let store = MemoryAntigravitySessionRecordStore()
        let launchLock = MemoryAntigravitySessionLaunchLock()
        let identity = FakeAntigravityIdentityProvider()
        let firstLauncher = FakeAntigravityProcessLauncher(nextPID: 10)
        let secondLauncher = FakeAntigravityProcessLauncher(nextPID: 20)
        identity.setIdentity(pid: 10, executablePath: "/bin/agy", startEpoch: 100)
        identity.setIdentity(pid: 20, executablePath: "/bin/agy", startEpoch: 200)
        identity.setIdentity(pid: 900, executablePath: "/app/CodexBar", startEpoch: 1)
        identity.setIdentity(pid: 901, executablePath: "/app/codexbar", startEpoch: 2)
        let first = self.makeFixture(
            launcher: firstLauncher,
            identity: identity,
            store: store,
            launchLock: launchLock,
            currentProcessID: 900)
        let second = self.makeFixture(
            launcher: secondLauncher,
            identity: identity,
            store: store,
            launchLock: launchLock,
            currentProcessID: 901)
        #expect(await Self.beginPersistentSession(first.session))
        #expect(await Self.beginPersistentSession(second.session))

        identity.removeIdentity(pid: 901)
        _ = try await first.session.beginProbe(binary: "/bin/agy")
        await first.session.finishProbe(success: true, resetAfterFetch: false)

        #expect(first.terminations.snapshot().map(\.pid) == [20, 20])
        #expect(store.snapshots().map(\.pid) == [10])

        await first.session.reset()
        await second.session.reset()
    }

    @Test
    func `file store migrates legacy record and preserves independent owners`() throws {
        let directory = FileManager.default.temporaryDirectory
            .appendingPathComponent("CodexBarAntigravitySessionTests-\(UUID().uuidString)", isDirectory: true)
        let fileURL = directory.appendingPathComponent("agy-session.json")
        defer { try? FileManager.default.removeItem(at: directory) }
        try FileManager.default.createDirectory(at: directory, withIntermediateDirectories: true)

        let legacy = AntigravityCLISessionRecord(
            pid: 10,
            requestedBinaryPath: "/bin/agy",
            executablePath: "/bin/agy",
            startEpoch: 100,
            processGroup: 10,
            ownerPID: 900,
            ownerExecutablePath: "/app/CodexBar",
            ownerStartEpoch: 1)
        try JSONEncoder().encode(legacy).write(to: fileURL)
        let store = AntigravityFileCLISessionRecordStore(fileURL: fileURL)
        #expect(try store.load() == [legacy])

        let second = AntigravityCLISessionRecord(
            pid: 20,
            requestedBinaryPath: "/bin/agy",
            executablePath: "/bin/agy",
            startEpoch: 200,
            processGroup: 20,
            ownerPID: 901,
            ownerExecutablePath: "/app/codexbar",
            ownerStartEpoch: 2)
        try store.save(second)
        #expect(try Set(store.load().map(\.pid)) == [10, 20])

        try store.remove(legacy)
        #expect(try store.load() == [second])

        try Data("{".utf8).write(to: fileURL)
        try store.save(legacy)
        #expect(try store.load() == [legacy])
    }

    @Test
    func `session reaps stale owner before launch`() async throws {
        let protectedRecord = AntigravityCLISessionRecord(
            pid: 777,
            requestedBinaryPath: "/bin/agy",
            executablePath: "/bin/agy",
            startEpoch: 42,
            processGroup: 777,
            ownerPID: 900,
            ownerExecutablePath: "/Applications/CodexBar.app/Contents/MacOS/CodexBar",
            ownerStartEpoch: 10)
        let store = MemoryAntigravitySessionRecordStore(record: protectedRecord)
        let fixture = self.makeFixture(store: store, currentProcessID: 901)
        fixture.identity.setIdentity(pid: 777, executablePath: "/bin/agy", startEpoch: 42)
        fixture.identity.setIdentity(
            pid: 900,
            executablePath: "/Applications/CodexBar.app/Contents/MacOS/CodexBar",
            startEpoch: 10)
        fixture.identity.setIdentity(
            pid: 901,
            executablePath: "/Applications/CodexBar.app/Contents/MacOS/CodexBar",
            startEpoch: 20)
        fixture.identity.setIdentity(pid: 10, executablePath: "/bin/agy", startEpoch: 100)

        fixture.identity.removeIdentity(pid: 900)
        let secondPID = try await fixture.session.beginProbe(binary: "/bin/agy")
        let record = fixture.store.snapshot()
        await fixture.session.finishProbe(success: true, resetAfterFetch: true)
        #expect(secondPID == 10)
        #expect(fixture.launcher.launchedBinarySnapshot() == ["/bin/agy"])
        #expect(fixture.terminations.snapshot().map(\.pid) == [777, 777])
        #expect(fixture.store.saveCount == 1)
        #expect(record?.pid == 10)
        #expect(record?.ownerPID == 901)
    }

    @Test
    func `reset rechecks protected persisted session after owner exits`() async {
        let store = MemoryAntigravitySessionRecordStore(record: AntigravityCLISessionRecord(
            pid: 777,
            requestedBinaryPath: "/bin/agy",
            executablePath: "/bin/agy",
            startEpoch: 42,
            processGroup: 777,
            ownerPID: 900,
            ownerExecutablePath: "/Applications/CodexBar.app/Contents/MacOS/CodexBar",
            ownerStartEpoch: 10))
        let fixture = self.makeFixture(store: store, currentProcessID: 901)
        fixture.identity.setIdentity(pid: 777, executablePath: "/bin/agy", startEpoch: 42)
        fixture.identity.setIdentity(
            pid: 900,
            executablePath: "/Applications/CodexBar.app/Contents/MacOS/CodexBar",
            startEpoch: 10)

        await fixture.session.reset()
        fixture.identity.removeIdentity(pid: 900)
        await fixture.session.reset()

        let terminations = fixture.terminations.snapshot()
        #expect(terminations.map(\.signal) == [SIGTERM, SIGKILL])
        #expect(terminations.allSatisfy { $0.pid == 777 && $0.group == 777 })
        #expect(fixture.store.snapshot() == nil)
    }

    @Test
    func `teardown preserves record written by another live session`() async throws {
        let fixture = self.makeFixture()
        fixture.identity.setIdentity(pid: 10, executablePath: "/bin/agy", startEpoch: 100)

        _ = try await fixture.session.beginProbe(binary: "/bin/agy")
        let otherRecord = AntigravityCLISessionRecord(
            pid: 777,
            requestedBinaryPath: "/other/agy",
            executablePath: "/other/agy",
            startEpoch: 42,
            processGroup: 777)
        try fixture.store.save(otherRecord)
        await fixture.session.finishProbe(success: true, resetAfterFetch: true)

        #expect(fixture.store.snapshot() == otherRecord)
    }

    @Test
    func `force killed process is polled again so the child can be reaped`() async throws {
        let fixture = self.makeFixture(terminateRootStopsProcess: false)
        fixture.identity.setIdentity(pid: 10, executablePath: "/bin/agy", startEpoch: 100)
        fixture.identity.setIdentity(pid: 900, executablePath: "/app/CodexBar", startEpoch: 1)

        _ = try await fixture.session.beginProbe(binary: "/bin/agy")
        await fixture.session.finishProbe(success: true, resetAfterFetch: true)

        let events = fixture.launcher.handleSnapshot().first?.snapshotEvents() ?? []
        guard let killIndex = events.firstIndex(of: "terminateTree:\(SIGKILL)") else {
            Issue.record("Expected SIGKILL during teardown")
            return
        }
        #expect(events.dropFirst(killIndex + 1).contains("isRunning:false"))
    }

    @Test
    func `one shot CLI reset tears down after fetch`() async throws {
        let fixture = self.makeFixture()
        fixture.identity.setIdentity(pid: 10, executablePath: "/bin/agy", startEpoch: 100)

        _ = try await fixture.session.beginProbe(binary: "/bin/agy")
        await fixture.session.finishProbe(success: true, resetAfterFetch: true)

        #expect(await fixture.session.isRunning == false)
        #expect(fixture.registry.unregisteredSnapshot() == [10])
    }

    @Test
    func `one shot CLI reset is deferred until all active probes finish`() async throws {
        let fixture = self.makeFixture()
        fixture.identity.setIdentity(pid: 10, executablePath: "/bin/agy", startEpoch: 100)

        _ = try await fixture.session.beginProbe(binary: "/bin/agy")
        _ = try await fixture.session.beginProbe(binary: "/bin/agy")

        await fixture.session.finishProbe(success: true, resetAfterFetch: true)
        #expect(await fixture.session.isRunning)

        await fixture.session.finishProbe(success: true, resetAfterFetch: false)

        #expect(await fixture.session.isRunning == false)
        #expect(fixture.registry.unregisteredSnapshot() == [10])
    }

    @Test
    func `authentication reset never writes interactive exit input`() async throws {
        let fixture = self.makeFixture()
        fixture.identity.setIdentity(pid: 10, executablePath: "/bin/agy", startEpoch: 100)

        _ = try await fixture.session.beginProbe(binary: "/bin/agy")
        await fixture.session.finishProbe(success: false, resetAfterFetch: true, forceTerminate: true)

        let events = try #require(fixture.launcher.handleSnapshot().first).snapshotEvents()
        #expect(!events.contains("sendExit"))
        #expect(events.contains("closePTY"))
        #expect(events.contains("terminateRoot"))
        #expect(await fixture.session.isRunning == false)
    }

    @Test
    func `failed reset never writes interactive exit input`() async throws {
        let fixture = self.makeFixture()
        fixture.identity.setIdentity(pid: 10, executablePath: "/bin/agy", startEpoch: 100)

        _ = try await fixture.session.beginProbe(binary: "/bin/agy")
        await fixture.session.finishProbe(success: false, resetAfterFetch: true)

        let events = try #require(fixture.launcher.handleSnapshot().first).snapshotEvents()
        #expect(!events.contains("sendExit"))
        #expect(events.contains("terminateRoot"))
        #expect(await fixture.session.isRunning == false)
    }

    @Test
    func `concurrent success preserves a failed probes deferred hard reset`() async throws {
        let fixture = self.makeFixture()
        fixture.identity.setIdentity(pid: 10, executablePath: "/bin/agy", startEpoch: 100)

        _ = try await fixture.session.beginProbe(binary: "/bin/agy")
        _ = try await fixture.session.beginProbe(binary: "/bin/agy")
        await fixture.session.finishProbe(success: false, resetAfterFetch: true)
        #expect(await fixture.session.isRunning)

        await fixture.session.finishProbe(success: true, resetAfterFetch: false)

        let events = try #require(fixture.launcher.handleSnapshot().first).snapshotEvents()
        #expect(!events.contains("sendExit"))
        #expect(events.contains("terminateRoot"))
        #expect(await fixture.session.isRunning == false)
    }

    @Test
    func `idle timeout hard stops a previously failed process`() async throws {
        let fixture = self.makeFixture(idleWindow: 0.05, manualSleep: true)
        fixture.identity.setIdentity(pid: 10, executablePath: "/bin/agy", startEpoch: 100)

        _ = try await fixture.session.beginProbe(binary: "/bin/agy")
        await fixture.session.finishProbe(success: false, resetAfterFetch: false)
        await fixture.sleeper?.waitForSleeps(1)
        fixture.sleeper?.resumeAll()
        await self.waitUntilStopped(fixture.session)

        let events = try #require(fixture.launcher.handleSnapshot().first).snapshotEvents()
        #expect(!events.contains("sendExit"))
        #expect(events.contains("terminateRoot"))
    }

    @Test
    func `repeated probe failures relaunch session`() async throws {
        let fixture = self.makeFixture(failureRelaunchThreshold: 2)
        fixture.identity.setIdentity(pid: 10, executablePath: "/bin/agy", startEpoch: 100)
        fixture.identity.setIdentity(pid: 11, executablePath: "/bin/agy", startEpoch: 101)

        _ = try await fixture.session.beginProbe(binary: "/bin/agy")
        await fixture.session.finishProbe(success: false, resetAfterFetch: false)
        _ = try await fixture.session.beginProbe(binary: "/bin/agy")
        await fixture.session.finishProbe(success: false, resetAfterFetch: false)
        let relaunchedPID = try await fixture.session.beginProbe(binary: "/bin/agy")
        await fixture.session.finishProbe(success: true, resetAfterFetch: false)

        #expect(relaunchedPID == 11)
        #expect(fixture.launcher.launchedBinarySnapshot() == ["/bin/agy", "/bin/agy"])
        #expect(await fixture.session.failureCountForTesting == 0)
    }

    @Test
    func `session reset reasons distinguish authentication from unhealthy probes`() {
        #expect(AntigravityCLISession.resetCause(
            authenticationRequired: true,
            resetAfterFetch: true,
            shouldForceStopUnhealthy: true).message == "authentication required")
        #expect(AntigravityCLISession.resetCause(
            authenticationRequired: false,
            resetAfterFetch: true,
            shouldForceStopUnhealthy: true).message == "unhealthy CLI HTTPS session")
        #expect(AntigravityCLISession.resetCause(
            authenticationRequired: false,
            resetAfterFetch: true,
            shouldForceStopUnhealthy: false).message == "one-shot CLI fetch")
        #expect(AntigravityCLISession.resetCause(
            authenticationRequired: false,
            resetAfterFetch: false,
            shouldForceStopUnhealthy: false).message == "deferred reset")
    }

    @Test
    func `deferred unhealthy reset preserves its cause after a concurrent success`() async throws {
        let fixture = self.makeFixture(failureRelaunchThreshold: 1)
        fixture.identity.setIdentity(pid: 10, executablePath: "/bin/agy", startEpoch: 100)

        _ = try await fixture.session.beginProbe(binary: "/bin/agy")
        _ = try await fixture.session.beginProbe(binary: "/bin/agy")
        await fixture.session.finishProbe(success: false, resetAfterFetch: false)
        await fixture.session.finishProbe(success: true, resetAfterFetch: false)

        #expect(await fixture.session.lastStopReasonForTesting == "unhealthy CLI HTTPS session")
    }

    @Test
    func `deferred authentication reset preserves its cause after a concurrent success`() async throws {
        let fixture = self.makeFixture()
        fixture.identity.setIdentity(pid: 10, executablePath: "/bin/agy", startEpoch: 100)

        _ = try await fixture.session.beginProbe(binary: "/bin/agy")
        _ = try await fixture.session.beginProbe(binary: "/bin/agy")
        await fixture.session.finishProbe(success: false, resetAfterFetch: true, forceTerminate: true)
        await fixture.session.finishProbe(success: true, resetAfterFetch: false)

        #expect(await fixture.session.lastStopReasonForTesting == "authentication required")
    }

    @Test
    func `success resets failure counter`() async throws {
        let fixture = self.makeFixture(failureRelaunchThreshold: 2)
        fixture.identity.setIdentity(pid: 10, executablePath: "/bin/agy", startEpoch: 100)

        _ = try await fixture.session.beginProbe(binary: "/bin/agy")
        await fixture.session.finishProbe(success: false, resetAfterFetch: false)
        _ = try await fixture.session.beginProbe(binary: "/bin/agy")
        await fixture.session.finishProbe(success: true, resetAfterFetch: false)
        _ = try await fixture.session.beginProbe(binary: "/bin/agy")
        await fixture.session.finishProbe(success: false, resetAfterFetch: false)

        #expect(fixture.launcher.launchedBinarySnapshot() == ["/bin/agy"])
        #expect(await fixture.session.failureCountForTesting == 1)
    }

    @Test
    func `matching persisted stale process is reaped when resolved binary changed`() async throws {
        let store = MemoryAntigravitySessionRecordStore(record: AntigravityCLISessionRecord(
            pid: 777,
            requestedBinaryPath: "/old/agy",
            executablePath: "/old/agy",
            startEpoch: 42,
            processGroup: 777))
        let fixture = self.makeFixture(store: store)
        fixture.identity.setIdentity(pid: 777, executablePath: "/old/agy", startEpoch: 42)
        fixture.identity.setIdentity(pid: 10, executablePath: "/new/agy", startEpoch: 100)

        _ = try await fixture.session.beginProbe(binary: "/new/agy")
        await fixture.session.finishProbe(success: true, resetAfterFetch: false)

        let terminations = fixture.terminations.snapshot()
        #expect(terminations.map(\.signal) == [SIGTERM, SIGKILL])
        #expect(terminations.allSatisfy { $0.pid == 777 && $0.group == 777 })
    }

    @Test
    func `matching persisted stale process is reaped before launch`() async throws {
        let store = MemoryAntigravitySessionRecordStore(record: AntigravityCLISessionRecord(
            pid: 777,
            requestedBinaryPath: "/bin/agy",
            executablePath: "/bin/agy",
            startEpoch: 42,
            processGroup: 777))
        let fixture = self.makeFixture(store: store)
        fixture.identity.setIdentity(pid: 777, executablePath: "/bin/agy", startEpoch: 42)
        fixture.identity.setIdentity(pid: 10, executablePath: "/bin/agy", startEpoch: 100)

        _ = try await fixture.session.beginProbe(binary: "/bin/agy")
        await fixture.session.finishProbe(success: true, resetAfterFetch: false)

        let terminations = fixture.terminations.snapshot()
        #expect(terminations.map(\.signal) == [SIGTERM, SIGKILL])
        #expect(terminations.allSatisfy { $0.pid == 777 && $0.group == 777 })
    }

    @Test
    func `non matching persisted process is not reaped`() async throws {
        let store = MemoryAntigravitySessionRecordStore(record: AntigravityCLISessionRecord(
            pid: 777,
            requestedBinaryPath: "/bin/agy",
            executablePath: "/bin/agy",
            startEpoch: 42,
            processGroup: 777))
        let fixture = self.makeFixture(store: store)
        fixture.identity.setIdentity(pid: 777, executablePath: "/usr/bin/other", startEpoch: 42)
        fixture.identity.setIdentity(pid: 10, executablePath: "/bin/agy", startEpoch: 100)

        _ = try await fixture.session.beginProbe(binary: "/bin/agy")
        await fixture.session.finishProbe(success: true, resetAfterFetch: false)

        #expect(fixture.terminations.snapshot().isEmpty)
    }

    private func waitForLaunches(_ launcher: FakeAntigravityProcessLauncher, count: Int) async -> Bool {
        for _ in 0..<200 {
            if launcher.launchedBinarySnapshot().count >= count { return true }
            try? await Task.sleep(nanoseconds: 1_000_000)
        }
        return false
    }

    private func waitUntilStopped(_ session: AntigravityCLISession) async {
        for _ in 0..<200 {
            let running = await session.isRunning
            if !running { return }
            await Task.yield()
        }
        Issue.record("Timed out waiting for Antigravity CLI session to stop")
    }

    private static func beginPersistentSession(_ session: AntigravityCLISession) async -> Bool {
        do {
            _ = try await session.beginProbe(binary: "/bin/agy")
            await session.finishProbe(success: true, resetAfterFetch: false)
            return true
        } catch {
            return false
        }
    }

    private struct Fixture {
        let session: AntigravityCLISession
        let launcher: FakeAntigravityProcessLauncher
        let identity: FakeAntigravityIdentityProvider
        let store: MemoryAntigravitySessionRecordStore
        let terminations: AntigravitySessionTerminationRecorder
        let registry: AntigravityRegistryRecorder
        let launchReservations: AntigravityLaunchReservationRecorder
        let sleeper: AntigravityManualSleeper?
    }

    private func makeFixture(
        launcher suppliedLauncher: FakeAntigravityProcessLauncher? = nil,
        identity suppliedIdentity: FakeAntigravityIdentityProvider? = nil,
        store: MemoryAntigravitySessionRecordStore = MemoryAntigravitySessionRecordStore(),
        launchLock: any AntigravityCLISessionLaunchLocking = MemoryAntigravitySessionLaunchLock(),
        idleWindow: TimeInterval = 3600,
        failureRelaunchThreshold: Int = 2,
        manualSleep: Bool = false,
        terminationGracePeriod: TimeInterval = 0,
        terminateRootStopsProcess: Bool = true,
        currentProcessID: pid_t = 900) -> Fixture
    {
        let launcher = suppliedLauncher ?? FakeAntigravityProcessLauncher(nextPID: 10)
        launcher.setTerminateRootStopsProcess(terminateRootStopsProcess)
        let identity = suppliedIdentity ?? FakeAntigravityIdentityProvider()
        let terminations = AntigravitySessionTerminationRecorder()
        let registry = AntigravityRegistryRecorder()
        let launchReservations = AntigravityLaunchReservationRecorder()
        let sleeper = manualSleep ? AntigravityManualSleeper() : nil
        let session = AntigravityCLISession(dependencies: AntigravityCLISession.Dependencies(
            launcher: launcher,
            identityProvider: identity,
            recordStore: store,
            launchLock: launchLock,
            beginAppShutdownTrackedLaunch: { launchReservations.begin() },
            endAppShutdownTrackedLaunch: { launchReservations.end() },
            registerForAppShutdown: { pid, binary in registry.register(pid: pid, binary) },
            updateAppShutdownProcessGroup: { pid, group in registry.update(pid: pid, group: group) },
            unregisterForAppShutdown: { pid in registry.unregister(pid: pid) },
            descendantPIDs: { pid in [pid + 1, pid + 2] },
            terminateProcessTree: { pid, group, signal, descendants in
                terminations.append(pid: pid, group: group, signal: signal, descendants: descendants)
            },
            currentProcessID: { currentProcessID },
            now: Date.init,
            sleep: { nanoseconds in
                if let sleeper {
                    try await sleeper.sleep(nanoseconds)
                } else {
                    try await Task.sleep(nanoseconds: nanoseconds)
                }
            },
            idleWindow: idleWindow,
            failureRelaunchThreshold: failureRelaunchThreshold,
            terminationGracePeriod: terminationGracePeriod))
        return Fixture(
            session: session,
            launcher: launcher,
            identity: identity,
            store: store,
            terminations: terminations,
            registry: registry,
            launchReservations: launchReservations,
            sleeper: sleeper)
    }
}
