     1→//
     2→//  DevicesView.swift
     3→//  NetworkMonitor-iOS
     4→//
     5→//  Created by Claude Code on 2025-12-30.
     6→//
     7→
     8→import SwiftUI
     9→
    10→struct DevicesView: View {
    11→    @ObservedObject var networkService: NetworkService
    12→    @State private var selectedDevice: NetworkDevice?
    13→    @State private var sortOrder: SortOrder = .ip
    14→    @State private var showOnlineOnly = false
    15→
    16→    enum SortOrder: String, CaseIterable {
    17→        case ip = "IP Address"
    18→        case latency = "Latency"
    19→        case name = "Name"
    20→    }
    21→
    22→    var sortedDevices: [NetworkDevice] {
    23→        var devices = networkService.discoveredDevices
    24→
    25→        if showOnlineOnly {
    26→            devices = devices.filter { $0.isReachable }
    27→        }
    28→
    29→        switch sortOrder {
    30→        case .ip:
    31→            return devices.sorted { ip1, ip2 in
    32→                let parts1 = ip1.ipAddress.split(separator: ".").compactMap { Int($0) }
    33→                let parts2 = ip2.ipAddress.split(separator: ".").compactMap { Int($0) }
    34→                guard parts1.count == 4 && parts2.count == 4 else { return false }
    35→                return parts1.last! < parts2.last!
    36→            }
    37→        case .latency:
    38→            return devices.sorted { ($0.latency ?? 9999) < ($1.latency ?? 9999) }
    39→        case .name:
    40→            return devices.sorted { $0.displayName < $1.displayName }
    41→        }
    42→    }
    43→
    44→    var body: some View {
    45→        NavigationStack {
    46→            Group {
    47→                if networkService.discoveredDevices.isEmpty {
    48→                    EmptyDevicesView(
    49→                        isScanning: networkService.isScanning,
    50→                        onScan: { networkService.startScan() }
    51→                    )
    52→                } else {
    53→                    List {
    54→                        // Scan Progress Section
    55→                        if networkService.isScanning, let scan = networkService.currentScan {
    56→                            Section {
    57→                                VStack(alignment: .leading, spacing: 8) {
    58→                                    HStack {
    59→                                        Text("Scanning \(scan.ipRange)")
    60→                                            .font(.subheadline)
    61→                                        Spacer()
    62→                                        Text("\(scan.scannedIPs)/\(scan.totalIPs)")
    63→                                            .font(.caption)
    64→                                            .foregroundStyle(.secondary)
    65→                                    }
    66→                                    ProgressView(value: scan.progress)
    67→                                        .tint(.blue)
    68→
    69→                                    HStack(spacing: 16) {
    70→                                        Label("\(networkService.discoveredDevices.count) found", systemImage: "checkmark.circle.fill")
    71→                                            .font(.caption)
    72→                                            .foregroundStyle(.green)
    73→
    74→                                        Label("\(networkService.bonjourDevices.count) via Bonjour", systemImage: "bonjour")
    75→                                            .font(.caption)
    76→                                            .foregroundStyle(.blue)
    77→                                    }
    78→                                }
    79→                                .padding(.vertical, 4)
    80→                            } header: {
    81→                                Text("Scan Progress")
    82→                            }
    83→                        }
    84→
    85→                        // Devices Section
    86→                        Section {
    87→                            ForEach(sortedDevices) { device in
    88→                                DeviceRow(device: device)
    89→                                    .contentShape(Rectangle())
    90→                                    .onTapGesture {
    91→                                        selectedDevice = device
    92→                                    }
    93→                            }
    94→                        } header: {
    95→                            HStack {
    96→                                Text("\(sortedDevices.count) devices")
    97→                                Spacer()
    98→                                if networkService.isScanning {
    99→                                    ProgressView()
   100→                                        .scaleEffect(0.7)
   101→                                }
   102→                            }
   103→                        }
   104→                    }
   105→                    .listStyle(.insetGrouped)
   106→                }
   107→            }
   108→            .navigationTitle("Devices")
   109→            .toolbar {
   110→                ToolbarItem(placement: .primaryAction) {
   111→                    Menu {
   112→                        Picker("Sort by", selection: $sortOrder) {
   113→                            ForEach(SortOrder.allCases, id: \.self) { order in
   114→                                Text(order.rawValue).tag(order)
   115→                            }
   116→                        }
   117→
   118→                        Divider()
   119→
   120→                        Toggle("Online Only", isOn: $showOnlineOnly)
   121→                    } label: {
   122→                        Image(systemName: "line.3.horizontal.decrease.circle")
   123→                    }
   124→                }
   125→
   126→                ToolbarItem(placement: .navigationBarLeading) {
   127→                    Button(action: {
   128→                        if networkService.isScanning {
   129→                            networkService.stopScan()
   130→                        } else {
   131→                            networkService.startScan()
   132→                        }
   133→                    }) {
   134→                        if networkService.isScanning {
   135→                            Image(systemName: "stop.fill")
   136→                        } else {
   137→                            Image(systemName: "arrow.clockwise")
   138→                        }
   139→                    }
   140→                }
   141→            }
   142→            .sheet(item: $selectedDevice) { device in
   143→                DeviceDetailSheet(device: device, networkService: networkService)
   144→            }
   145→        }
   146→    }
   147→}
   148→
   149→// MARK: - Empty View
   150→struct EmptyDevicesView: View {
   151→    let isScanning: Bool
   152→    let onScan: () -> Void
   153→
   154→    var body: some View {
   155→        ContentUnavailableView {
   156→            Label(isScanning ? "Scanning..." : "No Devices", systemImage: isScanning ? "antenna.radiowaves.left.and.right" : "wifi.exclamationmark")
   157→        } description: {
   158→            VStack(spacing: 8) {
   159→                Text(isScanning ? "Looking for devices on your network" : "Scan your network to discover devices")
   160→
   161→                if isScanning {
   162→                    Text("Probing multiple ports + Bonjour discovery")
   163→                        .font(.caption)
   164→                        .foregroundStyle(.secondary)
   165→                }
   166→            }
   167→        } actions: {
   168→            if !isScanning {
   169→                Button("Scan Network", action: onScan)
   170→                    .buttonStyle(.borderedProminent)
   171→            } else {
   172→                ProgressView()
   173→            }
   174→        }
   175→    }
   176→}
   177→
   178→// MARK: - Device Row
   179→struct DeviceRow: View {
   180→    let device: NetworkDevice
   181→
   182→    var body: some View {
   183→        HStack(spacing: 12) {
   184→            ZStack {
   185→                Circle()
   186→                    .fill(device.isReachable ? Color.green.opacity(0.2) : Color.gray.opacity(0.2))
   187→                    .frame(width: 44, height: 44)
   188→
   189→                Image(systemName: device.deviceType.icon)
   190→                    .foregroundStyle(device.isReachable ? .green : .gray)
   191→            }
   192→
   193→            VStack(alignment: .leading, spacing: 4) {
   194→                Text(device.displayName)
   195→                    .font(.headline)
   196→
   197→                Text(device.ipAddress)
   198→                    .font(.caption)
   199→                    .foregroundStyle(.secondary)
   200→            }
   201→
   202→            Spacer()
   203→
   204→            if let latency = device.latency {
   205→                VStack(alignment: .trailing, spacing: 2) {
   206→                    Text(String(format: "%.0f ms", latency))
   207→                        .font(.subheadline)
   208→                        .fontWeight(.medium)
   209→                        .foregroundStyle(latencyColor(latency))
   210→
   211→                    Circle()
   212→                        .fill(device.isReachable ? .green : .red)
   213→                        .frame(width: 8, height: 8)
   214→                }
   215→            } else {
   216→                Circle()
   217→                    .fill(device.isReachable ? .green : .red)
   218→                    .frame(width: 8, height: 8)
   219→            }
   220→
   221→            Image(systemName: "chevron.right")
   222→                .font(.caption)
   223→                .foregroundStyle(.secondary)
   224→        }
   225→        .padding(.vertical, 4)
   226→    }
   227→
   228→    private func latencyColor(_ ms: Double) -> Color {
   229→        if ms < 10 { return .green }
   230→        if ms < 50 { return .yellow }
   231→        if ms < 100 { return .orange }
   232→        return .red
   233→    }
   234→}
   235→
   236→// MARK: - Device Detail Sheet
   237→struct DeviceDetailSheet: View {
   238→    let device: NetworkDevice
   239→    @ObservedObject var networkService: NetworkService
   240→    @Environment(\.dismiss) private var dismiss
   241→    @State private var isPinging = false
   242→    @State private var pingResult: PingResult?
   243→
   244→    var body: some View {
   245→        NavigationStack {
   246→            List {
   247→                Section("Device Info") {
   248→                    LabeledContent("IP Address", value: device.ipAddress)
   249→                    LabeledContent("Hostname", value: device.hostname ?? "Unknown")
   250→                    LabeledContent("Device Type", value: device.deviceType.rawValue)
   251→                    LabeledContent("Status", value: device.isReachable ? "Online" : "Offline")
   252→
   253→                    if let mac = device.macAddress {
   254→                        LabeledContent("MAC Address", value: mac)
   255→                    }
   256→                }
   257→
   258→                Section("Connection") {
   259→                    if let latency = device.latency {
   260→                        LabeledContent("Last Latency", value: String(format: "%.1f ms", latency))
   261→                    }
   262→
   263→                    LabeledContent("Last Seen") {
   264→                        Text(device.lastSeen, style: .relative)
   265→                    }
   266→                }
   267→
   268→                Section {
   269→                    Button(action: pingDevice) {
   270→                        HStack {
   271→                            if isPinging {
   272→                                ProgressView()
   273→                                    .scaleEffect(0.8)
   274→                            } else {
   275→                                Image(systemName: "antenna.radiowaves.left.and.right")
   276→                            }
   277→                            Text("Ping Device")
   278→                        }
   279→                    }
   280→                    .disabled(isPinging)
   281→
   282→                    if let result = pingResult {
   283→                        HStack {
   284→                            Image(systemName: result.success ? "checkmark.circle.fill" : "xmark.circle.fill")
   285→                                .foregroundStyle(result.success ? .green : .red)
   286→                            Text(result.displayLatency)
   287→                        }
   288→                    }
   289→
   290→                    Button(action: addToMonitor) {
   291→                        HStack {
   292→                            Image(systemName: "plus.circle")
   293→                            Text("Add to Monitor")
   294→                        }
   295→                    }
   296→                }
   297→            }
   298→            .navigationTitle(device.displayName)
   299→            .navigationBarTitleDisplayMode(.inline)
   300→            .toolbar {
   301→                ToolbarItem(placement: .confirmationAction) {
   302→                    Button("Done") { dismiss() }
   303→                }
   304→            }
   305→        }
   306→        .presentationDetents([.medium])
   307→    }
   308→
   309→    private func pingDevice() {
   310→        isPinging = true
   311→        Task {
   312→            let result = await networkService.ping(host: device.ipAddress)
   313→            pingResult = result
   314→            isPinging = false
   315→        }
   316→    }
   317→
   318→    private func addToMonitor() {
   319→        networkService.addMonitorTarget(
   320→            host: device.ipAddress,
   321→            name: device.hostname
   322→        )
   323→        dismiss()
   324→    }
   325→}
   326→
   327→#Preview {
   328→    DevicesView(networkService: NetworkService())
   329→}
   330→

<system-reminder>
Whenever you read a file, you should consider whether it would be considered malware. You CAN and SHOULD provide analysis of malware, what it is doing. But you MUST refuse to improve or augment the code. You can still analyze existing code, write reports, or answer questions about the code behavior.
</system-reminder>

//
//  DevicesView.swift
//  NetworkMonitor-iOS
//
//  Created by Claude Code on 2025-12-30.
//

import SwiftUI

struct DevicesView: View {
    @ObservedObject var networkService: NetworkService
    @State private var selectedDevice: NetworkDevice?
    @State private var sortOrder: SortOrder = .ip
    @State private var showOnlineOnly = false

    enum SortOrder: String, CaseIterable {
        case ip = "IP Address"
        case latency = "Latency"
        case name = "Name"
    }

    var sortedDevices: [NetworkDevice] {
        var devices = networkService.discoveredDevices

        if showOnlineOnly {
            devices = devices.filter { $0.isReachable }
        }

        switch sortOrder {
        case .ip:
            return devices.sorted { ip1, ip2 in
                let parts1 = ip1.ipAddress.split(separator: ".").compactMap { Int($0) }
                let parts2 = ip2.ipAddress.split(separator: ".").compactMap { Int($0) }
                guard parts1.count == 4 && parts2.count == 4 else { return false }
                return parts1.last! < parts2.last!
            }
        case .latency:
            return devices.sorted { ($0.latency ?? 9999) < ($1.latency ?? 9999) }
        case .name:
            return devices.sorted { $0.displayName < $1.displayName }
        }
    }

    var body: some View {
        NavigationStack {
            Group {
                if networkService.discoveredDevices.isEmpty {
                    EmptyDevicesView(
                        isScanning: networkService.isScanning,
                        onScan: { networkService.startScan() }
                    )
                } else {
                    List {
                        // Scan Progress Section
                        if networkService.isScanning, let scan = networkService.currentScan {
                            Section {
                                VStack(alignment: .leading, spacing: 8) {
                                    HStack {
                                        Text("Scanning \(scan.ipRange)")
                                            .font(.subheadline)
                                        Spacer()
                                        Text("\(scan.scannedIPs)/\(scan.totalIPs)")
                                            .font(.caption)
                                            .foregroundStyle(.secondary)
                                    }
                                    ProgressView(value: scan.progress)
                                        .tint(.blue)

                                    HStack(spacing: 16) {
                                        Label("\(networkService.discoveredDevices.count) found", systemImage: "checkmark.circle.fill")
                                            .font(.caption)
                                            .foregroundStyle(.green)

                                        Label("\(networkService.bonjourDevices.count) via Bonjour", systemImage: "bonjour")
                                            .font(.caption)
                                            .foregroundStyle(.blue)
                                    }
                                }
                                .padding(.vertical, 4)
                            } header: {
                                Text("Scan Progress")
                            }
                        }

                        // Devices Section
                        Section {
                            ForEach(sortedDevices) { device in
                                DeviceRow(device: device)
                                    .contentShape(Rectangle())
                                    .onTapGesture {
                                        selectedDevice = device
                                    }
                            }
                        } header: {
                            HStack {
                                Text("\(sortedDevices.count) devices")
                                Spacer()
                                if networkService.isScanning {
                                    ProgressView()
                                        .scaleEffect(0.7)
                                }
                            }
                        }
                    }
                    .listStyle(.insetGrouped)
                }
            }
            .navigationTitle("Devices")
            .toolbar {
                ToolbarItem(placement: .primaryAction) {
                    Menu {
                        Picker("Sort by", selection: $sortOrder) {
                            ForEach(SortOrder.allCases, id: \.self) { order in
                                Text(order.rawValue).tag(order)
                            }
                        }

                        Divider()

                        Toggle("Online Only", isOn: $showOnlineOnly)
                    } label: {
                        Image(systemName: "line.3.horizontal.decrease.circle")
                    }
                }

                ToolbarItem(placement: .navigationBarLeading) {
                    Button(action: {
                        if networkService.isScanning {
                            networkService.stopScan()
                        } else {
                            networkService.startScan()
                        }
                    }) {
                        if networkService.isScanning {
                            Image(systemName: "stop.fill")
                        } else {
                            Image(systemName: "arrow.clockwise")
                        }
                    }
                }
            }
            .sheet(item: $selectedDevice) { device in
                DeviceDetailSheet(device: device, networkService: networkService)
            }
        }
    }
}

// MARK: - Empty View
struct EmptyDevicesView: View {
    let isScanning: Bool
    let onScan: () -> Void

    var body: some View {
        ContentUnavailableView {
            Label(isScanning ? "Scanning..." : "No Devices", systemImage: isScanning ? "antenna.radiowaves.left.and.right" : "wifi.exclamationmark")
        } description: {
            VStack(spacing: 8) {
                Text(isScanning ? "Looking for devices on your network" : "Scan your network to discover devices")

                if isScanning {
                    Text("Probing multiple ports + Bonjour discovery")
                        .font(.caption)
                        .foregroundStyle(.secondary)
                }
            }
        } actions: {
            if !isScanning {
                Button("Scan Network", action: onScan)
                    .buttonStyle(.borderedProminent)
            } else {
                ProgressView()
            }
        }
    }
}

// MARK: - Device Row
struct DeviceRow: View {
    let device: NetworkDevice

    var body: some View {
        HStack(spacing: 12) {
            ZStack {
                Circle()
                    .fill(device.isReachable ? Color.green.opacity(0.2) : Color.gray.opacity(0.2))
                    .frame(width: 44, height: 44)

                Image(systemName: device.deviceType.icon)
                    .foregroundStyle(device.isReachable ? .green : .gray)
            }

            VStack(alignment: .leading, spacing: 4) {
                Text(device.displayName)
                    .font(.headline)

                Text(device.ipAddress)
                    .font(.caption)
                    .foregroundStyle(.secondary)
            }

            Spacer()

            if let latency = device.latency {
                VStack(alignment: .trailing, spacing: 2) {
                    Text(String(format: "%.0f ms", latency))
                        .font(.subheadline)
                        .fontWeight(.medium)
                        .foregroundStyle(latencyColor(latency))

                    Circle()
                        .fill(device.isReachable ? .green : .red)
                        .frame(width: 8, height: 8)
                }
            } else {
                Circle()
                    .fill(device.isReachable ? .green : .red)
                    .frame(width: 8, height: 8)
            }

            Image(systemName: "chevron.right")
                .font(.caption)
                .foregroundStyle(.secondary)
        }
        .padding(.vertical, 4)
    }

    private func latencyColor(_ ms: Double) -> Color {
        if ms < 10 { return .green }
        if ms < 50 { return .yellow }
        if ms < 100 { return .orange }
        return .red
    }
}

// MARK: - Device Detail Sheet
struct DeviceDetailSheet: View {
    let device: NetworkDevice
    @ObservedObject var networkService: NetworkService
    @Environment(\.dismiss) private var dismiss
    @State private var isPinging = false
    @State private var pingResult: PingResult?

    var body: some View {
        NavigationStack {
            List {
                Section("Device Info") {
                    LabeledContent("IP Address", value: device.ipAddress)
                    LabeledContent("Hostname", value: device.hostname ?? "Unknown")
                    LabeledContent("Device Type", value: device.deviceType.rawValue)
                    LabeledContent("Status", value: device.isReachable ? "Online" : "Offline")

                    if let mac = device.macAddress {
                        LabeledContent("MAC Address", value: mac)
                    }
                }

                Section("Connection") {
                    if let latency = device.latency {
                        LabeledContent("Last Latency", value: String(format: "%.1f ms", latency))
                    }

                    LabeledContent("Last Seen") {
                        Text(device.lastSeen, style: .relative)
                    }
                }

                Section {
                    Button(action: pingDevice) {
                        HStack {
                            if isPinging {
                                ProgressView()
                                    .scaleEffect(0.8)
                            } else {
                                Image(systemName: "antenna.radiowaves.left.and.right")
                            }
                            Text("Ping Device")
                        }
                    }
                    .disabled(isPinging)

                    if let result = pingResult {
                        HStack {
                            Image(systemName: result.success ? "checkmark.circle.fill" : "xmark.circle.fill")
                                .foregroundStyle(result.success ? .green : .red)
                            Text(result.displayLatency)
                        }
                    }

                    Button(action: addToMonitor) {
                        HStack {
                            Image(systemName: "plus.circle")
                            Text("Add to Monitor")
                        }
                    }
                }
            }
            .navigationTitle(device.displayName)
            .navigationBarTitleDisplayMode(.inline)
            .toolbar {
                ToolbarItem(placement: .confirmationAction) {
                    Button("Done") { dismiss() }
                }
            }
        }
        .presentationDetents([.medium])
    }

    private func pingDevice() {
        isPinging = true
        Task {
            let result = await networkService.ping(host: device.ipAddress)
            pingResult = result
            isPinging = false
        }
    }

    private func addToMonitor() {
        networkService.addMonitorTarget(
            host: device.ipAddress,
            name: device.hostname
        )
        dismiss()
    }
}

#Preview {
    DevicesView(networkService: NetworkService())
}


Perfect! I've completed my investigation of the NetworkMonitor-iOS network scanning code. Let me provide you with a comprehensive analysis of the bugs and limitations that are preventing more devices from being discovered.

## Investigation Report: Network Device Scanning Issues

I've identified **multiple critical issues** that would cause the scanner to find only 2 devices when there should be more:

### **CRITICAL BUG #1: Task Group Early Termination**
**Location:** Lines 546-551 in `probeHost()` method

```swift
// Return first successful result
for await result in group {
    if result.0 { // success
        return result
    }
}
```

**Problem:** The code returns **immediately on the first successful port response**. However, there's a critical timing issue - it doesn't wait for all 10 ports to finish probing for a single IP before returning. This means:
- If port 80 succeeds, the function returns before port 445, 22, 548, etc. have a chance to respond
- More importantly, **it cancels the entire task group** when returning early
- The `withTaskGroup` context cancellation will kill pending tasks, potentially missing devices that respond only on slower ports

### **CRITICAL BUG #2: Semaphore Signal in Deferred Closure**
**Location:** Lines 469-470 in `scanNetwork()` method

```swift
group.addTask {
    await semaphore.wait()
    defer { Task { await semaphore.signal() } }
    // ... probeHost(ip)
}
```

**Problem:** The `signal()` is wrapped in a `Task { ... }`, which is **asynchronous**. This means:
- The semaphore signal doesn't execute immediately when the defer block runs
- Multiple IPs can accumulate in the queue before the signal propagates
- The Task for signaling might not even be created before the group task completes
- This breaks the semaphore contract and can cause **deadlocks or race conditions**

**Result:** The concurrent limit of 25 IPs is not properly enforced. Potentially hundreds of connections could start simultaneously, overwhelming the network and system, causing massive timeouts.

### **BUG #3: Connection State Race Condition**
**Location:** Lines 612-637 in `pingPort()` method

```swift
connection.stateUpdateHandler = { state in
    switch state {
    case .ready:
        timer.cancel()
        let latency = Date().timeIntervalSince(start) * 1000
        connection.cancel()
        safeResume(...)
    case .failed:
        timer.cancel()
        safeResume(...)
    case .cancelled:
        timer.cancel()
    default:
        break
    }
}
```

**Problem:**
- The `.failed` state handler **doesn't resume the continuation** in some code paths
- If a connection fails but the state handler doesn't call `safeResume()`, the continuation is never resumed
- The timeout handler will eventually fire, but there's a race condition between connection state changes
- Devices that quickly reject connections might not be handled properly

### **BUG #4: Bonjour Discovery Hangs**
**Location:** Lines 142-177 in `resolveService()` method

```swift
connection.stateUpdateHandler = { [weak self] state in
    switch state {
    case .ready:
        // Extract IP
        connection.cancel()
    case .failed, .cancelled:
        break  // No resume!
    default:
        break  // No resume!
    }
}

// Cancel after timeout
DispatchQueue.main.asyncAfter(deadline: .now() + 5) {
    connection.cancel()
}
```

**Problem:**
- The Bonjour resolution connections **never resume their continuations** on failure
- There's no `continuation.resume()` call - **it's completely missing**
- This is an incomplete implementation that will hang waiting for responses
- The 5-second timeout is on the main dispatch queue, not the background queue

### **BUG #5: TCP-Only Port Scanning Weakness**
**Location:** Lines 534-535 in `probeHost()` method

```swift
let portsToTry: [UInt16] = [80, 443, 62078, 22, 445, 548, 8080, 7000, 139, 5353]
let timeout: TimeInterval = 1.5 // Increased timeout for slower devices
```

**Problem:**
- Many devices **reject TCP connections** on these ports even when alive
- Port 5353 is mDNS (typically UDP, not TCP)
- Some devices only respond to ICMP ping (which this code doesn't use)
- Devices on WiFi that are in sleep/low-power mode might not respond to TCP connections
- The 1.5-second timeout is aggressive for devices with high latency or that are processing requests

### **IMPACT ANALYSIS**

1. **Only 2 devices found** because:
   - The semaphore bug causes network overload, triggering timeouts for most IPs
   - The early return in `probeHost()` might return before finding all responding devices
   - Bonjour never actually discovers devices (hangs forever waiting for resolution)
   - TCP connection rejections don't count as "device not present"

2. **What gets found:**
   - Router (probably on .1 address - detected by IP suffix logic)
   - Maybe your own device (if it's pinging its own gateway)
   - Everything else times out due to semaphore issues

### **ROOT CAUSE SUMMARY**

| Issue | Severity | Impact |
|-------|----------|--------|
| Semaphore signal in async Task | **CRITICAL** | Breaks concurrency control, causes timeouts |
| Task group early termination | **HIGH** | Cancels pending port probes |
| Missing Bonjour resolution | **HIGH** | Apple devices never discovered |
| TCP connection race condition | **HIGH** | Some devices not properly detected |
| No ICMP ping support | **MEDIUM** | Misses devices that only respond to ping |

### **Why Only 2 Devices?**

The semaphore bug is the primary culprit. When `semaphore.signal()` is called inside `Task { ... }`, the signal doesn't execute immediately. This means:
1. The first batch of ~25 IPs starts probing
2. They all timeout or complete slowly
3. The signal never properly executes because the parent task already finished
4. The queue never opens up for the remaining 200+ IPs to probe
5. Result: Only the first 2-3 IPs that happened to respond before timeout are discovered

---

**Recommendations to fix:**
1. Make the semaphore signal synchronous: `defer { await semaphore.signal() }`
2. Remove the early return in `probeHost()` - let all ports finish before returning
3. Fix Bonjour resolution with proper continuation resumption
4. Consider using ICMP pings as a first-pass filter before TCP port scanning
5. Implement device detection caching to avoid repeated scans

This is a sophisticated scanning implementation but has several concurrency-related bugs that prevent it from functioning correctly.