// swift-tools-version: 6.2
import Foundation
import PackageDescription

// Build channel, selected at manifest-evaluation time via the NUCLEIC_CHANNEL
// environment variable (dev | canary | beta | rc | stable); defaults to `dev`. It drives two
// things in lockstep:
//   • the app executable's PRODUCT name — which becomes the on-disk binary and
//     therefore the OS process name (Activity Monitor / `ps`):
//        dev -> nucleic-local   canary -> nucleic-canary   beta -> nucleic-beta
//        rc -> nucleic-rc   stable -> nucleic
//   • a compile-time define (`NUCLEIC_STABLE/RC/BETA/CANARY/DEV`) read by NucleicApp for its build
//     identity + warning banner (red local / yellow canary / blue beta / gold release-candidate /
//     none stable), and by NucleicCore's `ContainerManager` to suffix non-release container names.
// Branch mapping (see BUILD.md): dev builds from `dev`, canary from `canary`, beta from `staging`,
// rc from `rc`, stable from `main`.
// TailscaleKit (the embedded tsnet node behind the Tailnet sync transport) has no SwiftPM
// distribution — it's an Xcode framework built from a pinned libtailscale commit by
// scripts/build-tailscalekit.sh into third_party/TailscaleKit/ (untracked, ~100 MB). The
// binary target is declared only when the artifact exists so a fresh clone builds without
// Go/Xcode legwork; NucleicTailnet compiles either way (`#if canImport(TailscaleKit)`) and
// reports "not built in" at runtime when the framework is absent.
let tailscaleKitAvailable = FileManager.default.fileExists(
    atPath: Context.packageDirectory + "/third_party/TailscaleKit/TailscaleKit.xcframework")

let nucleicChannel = (Context.environment["NUCLEIC_CHANNEL"] ?? "dev").lowercased()
let (appProductName, channelDefine): (String, String) = {
    switch nucleicChannel {
    case "stable", "prod", "release": return ("nucleic", "NUCLEIC_STABLE")
    case "rc", "candidate": return ("nucleic-rc", "NUCLEIC_RC")
    case "beta": return ("nucleic-beta", "NUCLEIC_BETA")
    case "canary": return ("nucleic-canary", "NUCLEIC_CANARY")
    default: return ("nucleic-local", "NUCLEIC_DEV")
    }
}()

// Shipped channels drop the Swift 6 dynamic actor-isolation checks (SE-0423) from the app
// target. These are diagnostic-only traps the compiler inserts where a `@MainActor` closure
// crosses a nonisolated boundary (every SwiftUI `@ViewBuilder` closure); they change no
// behavior when they pass. Canary build 889 crashed *inside* one of these checks
// (RootView's NavigationSplitView sidebar closure → swift_task_isCurrentExecutorWithFlags →
// swift_getObjectType, a pointer-auth trap on a corrupt executor ref handed over by the
// macOS 27 beta runtime) — the check itself was the only casualty, so shipped builds skip
// it rather than crash the user. Dev builds keep the checks: there they catch real
// isolation bugs at the source instead of as heisencrashes later.
let appDisableIsolationChecks: [SwiftSetting] =
    channelDefine == "NUCLEIC_DEV" ? [] : [.unsafeFlags(["-disable-dynamic-actor-isolation"])]

// The Linux build (docs/COVALENCE_RUNNER.md §9, docs/CLOUD_RUNTIME.md Phase 2): the headless
// `nucleicd` runner host compiles for Linux with the Apple-only surface gated out — no SwiftUI
// app or spikes, no Apple containerization (the cloud container IS the sandbox;
// `RunSpec.container` stays nil), CryptoKit backed by swift-crypto inside NucleicProtocol.
// `#if os(Linux)` here runs at manifest-evaluation time on the build machine. Most Apple-only
// *package dependencies* stay declared on Linux so dependency resolution (and Package.resolved)
// is identical on every platform; their products are simply never linked there. The exception is
// Sparkle: SwiftPM clones EVERY declared package during resolution regardless of platform
// conditions on its products, and Sparkle is a large repo the Linux runner-container build can
// never use (its only consumer, NucleicApp, isn't built here). So it's dropped from the Linux
// dependency list below (`onLinux ? [] : [...]`) to keep the `nucleicd` cross-build lean —
// Package.resolved parity is worth less than not fetching a heavy, unusable dependency.
#if os(Linux)
let onLinux = true
#else
let onLinux = false
#endif

// The Windows port (docs/WINDOWS_PORT.md): the same headless shape as the Linux leg — a
// `nucleic-hostd` host process owns NucleicCore while a WinUI 3 renderer speaks the sync
// protocol over loopback — so `nonDarwin` gates everything the two non-Apple platforms share:
// no SwiftUI app / spikes / Sparkle, and NucleicProtocol's swift-crypto + NIO legs. Windows
// differs from Linux in one place: it COMPILES the container-policy layer
// (`Container/ContainerManager.swift`, retargeted onto the `SandboxEngine` protocol) because
// the wslc broker supplies a real sandbox mechanism there, where Linux keeps the
// LinuxSupport.swift stub (the cloud container IS the sandbox).
#if os(Windows)
let onWindows = true
#else
let onWindows = false
#endif
let nonDarwin = onLinux || onWindows

// M0 CI leg (docs/WINDOWS_PORT.md §13): `NUCLEIC_WINDOWS_PROTOCOL_ONLY=1` narrows the
// Windows manifest to the wire layer — NucleicProtocol + its tests — because `swift test`
// builds every declared target, and NucleicCore doesn't compile on Windows until the §12
// item 7–9 shims land. The flag is manifest-eval-time env, like NUCLEIC_CHANNEL, and is set
// only by `windows/build.ps1 -Target protocol` (the core leg runs WITHOUT it, so
// the full-core gap list stays visible in CI as the expected-failing leg).
let windowsProtocolOnly =
    onWindows && Context.environment["NUCLEIC_WINDOWS_PROTOCOL_ONLY"] == "1"

// The iPhone app's transcript projection (pure Foundation + NucleicProtocol model
// folding), compiled here as a host-testable library. The iOS app target compiles these
// same sources directly via its file-system-synchronized group; this target exists so the
// incremental projector's prefix-equivalence suite (a required safety net —
// docs/TRANSCRIPT_INCREMENTAL_PROJECTION.md) runs under plain `swift test`, where the
// Xcode project has no test target. `sources` is an explicit allowlist: the folder's
// other files are SwiftUI views this library must not build. Declared out-of-line because
// three platform-gated lists include it: Darwin host testing, the Windows renderer DLL, and
// Linux's DLL/projection CI contract (docs/WINDOWS_PORT.md §6).
let projectionTarget: Target = .target(
    name: "NucleicRemoteProjection",
    dependencies: ["NucleicProtocol"],
    path: "ios/NucleicRemote/NucleicRemote/Views/Transcript",
    // The folder's other files are SwiftUI views compiled only by the Xcode iOS app
    // target; excluding them keeps this host-testable library building just the two
    // pure-projection sources and silences SwiftPM's "unhandled files" warning. Add
    // any new sibling view here (or it re-triggers the warning).
    exclude: [
        "AskUserQuestionResultCard.swift",
        "ExitPlanModeCard.swift",
        "GitCommitCard.swift",
        "HostCommandSummary.swift",
        "HostExecCard.swift",
        "MarkdownText.swift",
        "SandboxToolDisplay.swift",
        "ToolGroupRow.swift",
    ],
    sources: [
        "TranscriptProjection.swift",
        "IncrementalTranscriptProjection.swift",
        "TranscriptProjectionABI.swift",
    ])

// The macOS cockpit + its build plugin and the de-risking spikes do not compile (or make sense)
// on Linux. This list also owns the Darwin declaration of the iOS projection library; Linux
// declares that pure target separately for the DLL contract tests below.
let darwinOnlyTargets: [Target] = [
    .executableTarget(
        name: "NucleicApp",
        dependencies: [
            "NucleicCore",
            "NucleicPowerProtocol",
            .product(name: "SwiftTerm", package: "SwiftTerm"),
            // Auto-update for direct (non-App-Store) distribution. macOS-only.
            .product(name: "Sparkle", package: "Sparkle"),
        ],
        // Keep the precompiled Core ML directory opaque. `.process` descends into Core ML
        // artifacts and tries to synthesize model bindings; the runtime classifier deliberately
        // loads the checked-in `.mlmodelc` lazily on its own actor instead.
        resources: [.copy("Resources/PurposeClassifier")],
        swiftSettings: [.define(channelDefine)] + appDisableIsolationChecks,
        // FoundationModels ships in macOS 26, but its Swift ABI is still shifting between the
        // 26/27 beta seeds: a symbol present in the SDK we build against (e.g.
        // `Generable.promptRepresentation`, emitted by the `@Generable` guided-generation code
        // in AppleIntelligence.swift) can be ABSENT from an older seed's framework. Strong-linked
        // (the Swift autolink default), that makes dyld abort BEFORE main() on any Mac whose seed
        // lacks the symbol — "Symbol not found … FoundationModels", EXC_CRASH / Namespace DYLD —
        // even though every use is already gated behind `#available` + `model.isAvailable`, since
        // those runtime guards never get to run. Weak-linking binds a missing symbol to NULL at
        // load instead of aborting, so the app always launches and the guards do their job (the
        // soft-AI features fall back to their heuristics). Verify the flag took with
        // `otool -l <binary> | grep -A3 FoundationModels` → expect LC_LOAD_WEAK_DYLIB.
        linkerSettings: [
            .unsafeFlags(["-Xlinker", "-weak_framework", "-Xlinker", "FoundationModels"]),
        ],
        plugins: ["EmbedGitCommit"]),
    .target(
        name: "NucleicPowerProtocol",
        swiftSettings: [.define(channelDefine)]),
    .executableTarget(
        name: "NucleicPowerHelper",
        dependencies: ["NucleicPowerProtocol"],
        exclude: ["Info.plist"],
        linkerSettings: [
            .unsafeFlags([
                "-Xlinker", "-sectcreate",
                "-Xlinker", "__TEXT",
                "-Xlinker", "__info_plist",
                "-Xlinker", Context.packageDirectory + "/Sources/NucleicPowerHelper/Info.plist",
            ]),
        ]),
    // Prebuild plugin: stamps the built-from commit into a generated `GitCommit.swift`
    // at build time, so the hash is baked into the binary (correct even on a
    // packaged beta / rc / stable archive, where git isn't present at runtime).
    .plugin(name: "EmbedGitCommit", capability: .buildTool()),
    // Adapter-contract test stubs (fed to NucleicCoreTests). Darwin-only: they lean on
    // Foundation surfaces corelibs doesn't ship (`FileHandle.bytes`).
    .executableTarget(name: "fake-claude", dependencies: ["NucleicCore"]),
    .executableTarget(name: "fake-grok", dependencies: ["NucleicCore"]),
    .testTarget(
        name: "NucleicCoreTests",
        dependencies: ["NucleicCore", "NucleicTailnet"],
        resources: [.copy("Fixtures")],
        // The TailscaleKit binary framework is staged at the build-products root, which
        // executables reach via their default rpath but .xctest bundles do not (their
        // rpaths point at PackageFrameworks + the bundle's own Frameworks dir). Walk the
        // three levels from Contents/MacOS/<binary> back up to the products dir.
        linkerSettings: [.unsafeFlags(["-Xlinker", "-rpath", "-Xlinker", "@loader_path/../../.."])]
    ),
    .testTarget(
        name: "NucleicAppTests",
        dependencies: ["NucleicApp", "NucleicCore"],
        resources: [.copy("Fixtures")],
        // NucleicApp links Sparkle. Test bundles need to walk back from
        // Contents/MacOS/<test> to SwiftPM's products directory to find the framework.
        linkerSettings: [.unsafeFlags(["-Xlinker", "-rpath", "-Xlinker", "@loader_path/../../.."])]),
    .executableTarget(name: "nucleic-spike", dependencies: ["NucleicCore"]),
    // Standalone de-risking spike for the container rework: drives the real `ContainerEngine`
    // end-to-end — pulls an image from a registry, boots the VM, execs a process, and streams
    // its stdout through `ContainerizedProcessHandle` (the agent's path). Run signed with
    // signing/spike.entitlements; not shipped.
    .executableTarget(
        name: "container-spike",
        dependencies: [
            "NucleicCore",
            .product(name: "Containerization", package: "containerization"),
            .product(name: "ContainerizationOCI", package: "containerization"),
        ]),
    // Standalone de-risking spike for the macOS-VM rework: drives the real `MacVMEngine`
    // end-to-end on Apple's `Virtualization` framework — clone the golden base, boot the guest,
    // `ssh` a command in, and stream stdout (the `mac_vm_exec` agent path); `NUCLEIC_MACVM_BUILD_BASE=1`
    // runs the one-time macOS install instead. Run signed with signing/spike.entitlements; not shipped.
    .executableTarget(name: "macvm-spike", dependencies: ["NucleicCore"]),
    // Standalone de-risking + ops harness for the MDM plane (docs/MACOS_VM_MDM.md §7 P0): stands
    // up the real `NucleicMDMServer` with a fresh local CA, emits the enrollment profile + CA cert
    // + `mdm-enroll.sh` to a directory, queues the notification-fix profiles, and logs every
    // protocol exchange — used to validate the load-bearing assumptions (no-APNs drain, UAMDM,
    // SCEP interop) against one throwaway guest. Not shipped.
    .executableTarget(name: "mdm-spike", dependencies: ["NucleicMDM"]),
    projectionTarget,
    .testTarget(
        name: "NucleicRemoteProjectionTests",
        dependencies: ["NucleicRemoteProjection", "NucleicProtocol"]),
]

// The Windows shipping surface (docs/WINDOWS_PORT.md §10): the headless hostd host and the
// renderer's C-ABI protocol DLL. The DLL target is also declared on Linux below as a CI compile
// contract, but its dynamic product and hostd remain Windows-only.
let protocolCTarget: Target = .target(
    name: "NucleicProtocolC",
    dependencies: ["NucleicProtocol", "NucleicRemoteProjection"],
    // The C header consumed by the C# P/Invoke layer, not compiled by SwiftPM.
    exclude: ["include"],
    swiftSettings: [.define(channelDefine)])

let windowsOnlyTargets: [Target] = [
    .executableTarget(
        name: "nucleic-hostd",
        dependencies: ["NucleicCore"],
        swiftSettings: [.define(channelDefine)]),
    protocolCTarget,
    projectionTarget,
]

// Compile the renderer ABI on Linux as a CI contract even though the dynamic product ships only
// on Windows. This catches Swift/C boundary and projection regressions on every ordinary Linux
// test run instead of waiting for the scarcer Windows runner.
let linuxProtocolCTargets: [Target] = onLinux ? [
    projectionTarget,
    protocolCTarget,
    .testTarget(
        name: "NucleicProtocolCTests",
        dependencies: [
            "NucleicProtocolC",
            "NucleicProtocol",
            "NucleicRemoteProjection",
            .product(name: "NIOCore", package: "swift-nio"),
            .product(name: "NIOPosix", package: "swift-nio"),
        ]),
    .testTarget(
        name: "NucleicRemoteProjectionTests",
        dependencies: ["NucleicRemoteProjection", "NucleicProtocol"]),
] : []

// The platform-gated list concatenations below are hoisted OUT of the `Package(...)` call:
// inlined, they push the initializer expression past the manifest type-checker's budget
// ("unable to type-check this expression in reasonable time" — hit first on the Linux
// toolchain), so each list gets its own explicitly-typed `let` and the call site only
// concatenates typed arrays.
let coreProducts: [Product] = [
        .library(name: "NucleicProtocol", targets: ["NucleicProtocol"]),
        // The Tailnet transport (embedded tsnet node + fd-backed FrameChannel). A separate
        // product because the iPhone client links it directly (NucleicCore is host-only).
        .library(name: "NucleicTailnet", targets: ["NucleicTailnet"]),
        .library(name: "NucleicCore", targets: ["NucleicCore"]),
        // Headless mesh client for runner smoke tests (docs/COVALENCE_RUNNER.md §0.2 item 2):
        // pairs from a pairing code, dials the relay, starts a chat, answers approvals over the
        // wire. NucleicProtocol-only, so it builds everywhere — incl. the static musl target.
        .executable(name: "nucleic-smoke", targets: ["nucleic-smoke"]),
        // NAT-traversal test harness (Covalence direct, SYNC §3.3): drives the real STUN +
        // punch core (`NucleicProtocol/Sync/Direct`) so `scripts/nat-sim` can exercise it
        // across simulated NATs (netns + nftables masquerade). NucleicProtocol-only, so it
        // builds everywhere the punch core does — including inside the sim's network namespaces.
        .executable(name: "nucleic-punch-harness", targets: ["nucleic-punch-harness"]),
]

// `nucleicd` is the Covalence *runner* host — a Mac or a Linux runner container. Windows has
// its own headless host (`nucleic-hostd`), and nucleicd's control endpoint is a BSD-socket
// listener with a `DispatchSource` signal handler, neither of which exists there. Excluding it
// from the Windows manifest is what keeps `swift test` (which builds every declared target) from
// having to compile a daemon that platform will never run.
let nonWindowsProducts: [Product] = [
    // The headless Covalence runner host (docs/COVALENCE_RUNNER.md §9): the same NucleicCore
    // runtime as the app, no UI. Runs on a spare Mac (self-host v0) AND on Linux — the
    // runner-container daemon.
    .executable(name: "nucleicd", targets: ["nucleicd"]),
]

let nonWindowsTargets: [Target] = [
    .executableTarget(name: "nucleicd", dependencies: ["NucleicCore"]),
]

let windowsOnlyProducts: [Product] = [
    // The Windows headless host (docs/WINDOWS_PORT.md §11): hostd owns AppStore + SyncHost;
    // the WinUI 3 renderer is a client over loopback.
    .executable(name: "nucleic-hostd", targets: ["nucleic-hostd"]),
    // The renderer's protocol client (docs/WINDOWS_PORT.md §6): NucleicProtocol + the
    // transcript projection behind a flat C ABI, P/Invoked by the WinUI app.
    .library(name: "NucleicProtocolC", type: .dynamic, targets: ["NucleicProtocolC"]),
]

let darwinOnlyProducts: [Product] = [
        // Product (binary / process) name varies by channel — see `appProductName`.
        .executable(name: appProductName, targets: ["NucleicApp"]),
        .executable(name: "nucleic-power-helper", targets: ["NucleicPowerHelper"]),
        // Adapter-contract test stubs (fed to NucleicCoreTests). Darwin-only: they lean on
        // Foundation surfaces corelibs doesn't ship (`FileHandle.bytes`), and the core test
        // suite they feed runs on Darwin.
        .executable(name: "fake-claude", targets: ["fake-claude"]),
        .executable(name: "fake-grok", targets: ["fake-grok"]),
        .executable(name: "nucleic-spike", targets: ["nucleic-spike"]),
        .executable(name: "container-spike", targets: ["container-spike"]),
        .executable(name: "macvm-spike", targets: ["macvm-spike"]),
        .executable(name: "mdm-spike", targets: ["mdm-spike"]),
]

let packageDependencies: [Package.Dependency] = [
        .package(url: "https://github.com/groue/GRDB.swift.git", from: "7.0.0"),
        // The Noise/CBOR crypto on Linux (docs/COVALENCE_RUNNER.md §9): swift-crypto is the
        // cross-platform CryptoKit — NucleicProtocol imports it only where CryptoKit is absent
        // (`#if canImport(CryptoKit)`), and the NucleicProtocolTests Noise/enrollment vectors
        // pin the two backends byte-for-byte. Already in the graph transitively (containerization
        // → grpc-swift), so this adds no new resolution.
        .package(url: "https://github.com/apple/swift-crypto.git", from: "3.0.0"),
        // The relay WebSocket on Linux (docs/COVALENCE_RUNNER.md §9): corelibs-foundation's
        // URLSessionWebSocketTask rides libcurl's experimental WS support, absent from distro
        // builds — so the runner's socket is SwiftNIO end to end (TLS via NIOSSL, HTTP/1.1
        // upgrade, RFC 6455 client framing). Darwin/iOS keep URLSessionWebSocketTask; these
        // products are linked into NucleicProtocol on Linux only. Both packages are already in
        // the graph transitively (containerization → grpc-swift), so this adds no resolution.
        // Floor is 2.101.3 for WINDOWS, not for any API we call: NIOPosix didn't compile on
        // Windows before it (apple/swift-nio#3433). 2.101.2 fails three ways — `HANDLE` is an
        // opaque pointer whose `Sendable` conformance is unavailable, so `ThreadOpsWindows`
        // and `NIOThread.handle` are both rejected, and `CInt(IPPROTO_UDP)` doesn't compile
        // now that WinSDK imports IPPROTO as an enum (2.101.3 uses `.rawValue`).
        .package(url: "https://github.com/apple/swift-nio.git", from: "2.101.3"),
        // TEMPORARY FORK — revert to `.package(url: "https://github.com/apple/swift-nio-ssl.git",
        // from: "2.27.0")` the moment apple/swift-nio-ssl#567 lands.
        //
        // NIOSSL's Swift layer has no Windows support upstream, not even on main as of 2.37.2:
        // every file's platform #if handles only Darwin/Musl/Glibc/Android and then hits
        // `#error("unsupported os")`. Only the C layer was fixed (#585), which is why
        // CNIOBoringSSL compiles and then hundreds of Swift errors follow. #567 ("Add support
        // for Windows", by the same contributor whose Winsock fixes shipped in swift-nio
        // 2.101.3) ports the Swift layer; it is mergeable and actively moving.
        //
        // Pinned by REVISION, not branch, so the build stays reproducible and a force-push to
        // the fork can't silently change what CI compiles. To move to a newer PR commit, bump
        // the revision here AND in Package.resolved. (The commit lives only on the fork —
        // apple/swift-nio-ssl doesn't expose PR refs to a default clone, so the upstream URL
        // can't be kept while pointing at this revision.)
        //
        // EXPECTED NOISE while this pin is in place: four "Conflicting identity for
        // swift-nio-ssl" warnings, because third_party/containerization, async-http-client,
        // swift-nio-extras and grpc-swift-nio-transport all reach the upstream URL. The root
        // package wins, so the build is correct — but SwiftPM says it "will be escalated to an
        // error in future versions", which is a second reason not to sit on this fork.
        .package(
            url: "https://github.com/Joannis/swift-nio-ssl.git",
            revision: "bc3bd90098701c6c1d7aa6275b6911860fe9d439"),
        // The host-side MDM plane (docs/MACOS_VM_MDM.md): a local CA that issues the guest's
        // enrollment identity + the server's TLS cert, and the SCEP/CMS ASN.1 the enrollment
        // handshake speaks. swift-certificates (X.509 issuance, CSR parse, CMS SignedData) +
        // swift-asn1 (the DER layer we hand-roll CMS EnvelopedData on, which swift-certificates
        // does not provide). Both are already in the graph transitively (swift-certificates →
        // swift-asn1; both reachable via grpc-swift/containerization), so this adds no resolution.
        .package(url: "https://github.com/apple/swift-certificates.git", from: "1.19.0"),
        .package(url: "https://github.com/apple/swift-asn1.git", from: "1.7.0"),
        .package(url: "https://github.com/migueldeicaza/SwiftTerm.git", from: "1.2.0"),
        // Apple's containerization framework — the in-process runtime the `container` CLI is built
        // on. **Vendored** (third_party/containerization) at upstream commit 6b7b42ca rather than
        // pulled from github, because we carry a small patch: `LinuxContainer.Configuration` forwards
        // `vmExtensions` into `VMConfiguration.extensions` so we can attach a memory-balloon device
        // (see third_party/containerization/PATCHES.md). The package is pre-1.0 (source stability
        // holds only within a minor version); to bump, re-vendor the new commit and re-apply the
        // patch. All framework calls are centralized in `ContainerEngine`/`MemoryBalloon`.
        .package(path: "third_party/containerization"),
    ] + (nonDarwin ? [] : [
        // Sparkle — in-app auto-update for the directly-distributed (non-App-Store) macOS
        // builds. macOS-only and used ONLY by the NucleicApp executable target, so the iOS
        // resolution of the package graph never compiles it. Updates are delivered as signed,
        // notarized DMGs described by a per-channel appcast (see BUILD.md "Auto-update").
        // Excluded from the Linux graph (see the manifest note above): NucleicApp isn't built for
        // the runner container, and leaving it declared makes `swift build --product nucleicd`
        // clone Sparkle's large repo during resolution for a target that can never use it.
        .package(url: "https://github.com/sparkle-project/Sparkle", from: "2.6.0"),
    ])

// Pure, platform-agnostic: identifiers, the AgentEvent model, the sync wire
// protocol (ClientMsg/HostMsg), CBOR framing, and the Noise SecureChannel.
// No GRDB / Process / Network — compiles for macOS, iOS, Linux, and Windows alike
// (CryptoKit on Darwin, swift-crypto elsewhere; see the swift-crypto dependency note).
// Out-of-line because the `windowsProtocolOnly` M0 CI leg declares JUST this pair.
let protocolTarget: Target = .target(
            name: "NucleicProtocol",
            dependencies: [
                .product(
                    name: "Crypto", package: "swift-crypto",
                    condition: .when(platforms: [.linux, .windows])),
                // The non-Apple relay WebSocket (RelayTransport.swift's `#else` leg) — see the
                // swift-nio dependency note above. Windows rides the same NIO leg as Linux
                // (docs/WINDOWS_PORT.md §4.1).
                .product(
                    name: "NIOCore", package: "swift-nio",
                    condition: .when(platforms: [.linux, .windows])),
                .product(
                    name: "NIOPosix", package: "swift-nio",
                    condition: .when(platforms: [.linux, .windows])),
                .product(
                    name: "NIOHTTP1", package: "swift-nio",
                    condition: .when(platforms: [.linux, .windows])),
                .product(
                    name: "NIOWebSocket", package: "swift-nio",
                    condition: .when(platforms: [.linux, .windows])),
                .product(
                    name: "NIOFoundationCompat", package: "swift-nio",
                    condition: .when(platforms: [.linux, .windows])),
                .product(
                    name: "NIOSSL", package: "swift-nio-ssl",
                    condition: .when(platforms: [.linux, .windows])),
            ],
            linkerSettings: [
                // Sync/Direct is hand-rolled BSD sockets (DirectSocketShim.swift). On Windows
                // that means Winsock, and interface enumeration has no getifaddrs equivalent —
                // it goes through GetAdaptersAddresses, which lives in the IP Helper API.
                .linkedLibrary("ws2_32", .when(platforms: [.windows])),
                .linkedLibrary("iphlpapi", .when(platforms: [.windows])),
            ])

let protocolTestsTarget: Target = .testTarget(
    name: "NucleicProtocolTests",
    dependencies: ["NucleicProtocol"])

let crossPlatformTargets: [Target] = [
        protocolTarget,
        // The Tailnet sync transport (SYNC_PROTOCOL §3): an embedded tsnet node
        // (TailscaleKit) plus the fd-backed FrameChannel both sides speak over it. Compiles
        // for macOS (host listener) and iOS (phone dialer) alike; when the TailscaleKit
        // artifact is absent the module still builds and `TailnetSupport.isBuiltIn` is false.
        .target(
            name: "NucleicTailnet",
            dependencies: ["NucleicProtocol"] + (tailscaleKitAvailable ? ["TailscaleKit"] : [])),
        // The host-side MDM plane (docs/MACOS_VM_MDM.md), factored out of NucleicCore so it
        // builds and tests in **isolation** — pure Swift + crypto/ASN.1, no Virtualization or
        // containerization — which is what makes `swift test --filter NucleicMDMTests` fast. The
        // one Darwin-only surface (the `NWListener`+TLS HTTPS server, `NucleicMDMServer`) is
        // guarded with `#if canImport(Network)`, so this target still compiles on Linux (server
        // omitted) alongside the rest of NucleicCore. NucleicCore depends on it for the
        // provisioning-time (Mode A) and live-clone (Mode B) glue.
        .target(
            name: "NucleicMDM",
            dependencies: [
                .product(name: "X509", package: "swift-certificates"),
                .product(name: "SwiftASN1", package: "swift-asn1"),
                .product(name: "Crypto", package: "swift-crypto"),
                .product(name: "_CryptoExtras", package: "swift-crypto"),
            ]),
        .testTarget(
            name: "NucleicMDMTests",
            dependencies: ["NucleicMDM"]),
        .target(
            name: "NucleicCore",
            dependencies: [
                "NucleicProtocol",
                "NucleicTailnet",
                "NucleicMDM",
                .product(name: "GRDB", package: "GRDB.swift"),
                // In-process container runtime (host-only, macOS-conditioned: on Linux the cloud
                // container IS the sandbox — `RunSpec.container` stays nil and the Container/*
                // sources compile to empty via `#if canImport(Containerization)`). EXT4 unpacking
                // + vmnet networking are reached transitively through `Containerization`;
                // `ContainerizationOCI` provides the `User`/`Platform` types used directly by
                // `ContainerEngine`.
                .product(
                    name: "Containerization", package: "containerization",
                    condition: .when(platforms: [.macOS])),
                .product(
                    name: "ContainerizationOCI", package: "containerization",
                    condition: .when(platforms: [.macOS])),
                // For `AddressAllocator` — named in the `VZInstanceExtension.configureVZ` signature
                // our `MemoryBalloon` conforms to (it lives here, not in `Containerization`).
                .product(
                    name: "ContainerizationExtras", package: "containerization",
                    condition: .when(platforms: [.macOS])),
                // The Windows control-plane TCP listener (MCPApprovalServer's NIO leg,
                // docs/WINDOWS_PORT.md §4.3) — Winsock SOCKETs aren't POSIX fds, so the
                // Linux BSD-socket mirror can't serve there. Already in the graph via
                // NucleicProtocol's non-Apple legs; this just links it into NucleicCore.
                .product(
                    name: "NIOCore", package: "swift-nio",
                    condition: .when(platforms: [.windows])),
                .product(
                    name: "NIOPosix", package: "swift-nio",
                    condition: .when(platforms: [.windows])),
            ],
            // Non-Darwin platforms compile NucleicCore without the Apple-framework surfaces: the
            // containerization sandbox engine + macOS-VM engine (stub actors in
            // LinuxSupport.swift keep the shared signatures compiling; the pure value/spec
            // files — MacVMSpec/MacVMSurface/MacVMPackage/CommandInterceptor — stay) and the
            // Network.framework LAN transport (a runner listens relay/tailnet-first;
            // docs/COVALENCE_RUNNER.md §9). Linux additionally drops the container-policy layer
            // (`Container/ContainerManager.swift` — the LinuxSupport.swift stub is the Linux
            // story), which Windows KEEPS: there the manager compiles against the platform-
            // neutral `SandboxEngine` protocol and the wslc broker supplies the mechanism
            // (docs/WINDOWS_PORT.md §3.1). The `Windows/` shim directories are compiled only on
            // Windows and excluded everywhere else.
            exclude: computeNucleicCoreExcludes(),
            // The build channel reaches the container-naming code here: `ContainerManager` appends a
            // per-channel suffix (e.g. `-beta`, `-local`) to non-release container names so a beta /
            // local-dev build's containers don't collide with a release's on the shared on-disk
            // container store (ContainerEngine.defaultStorageRoot). Same define NucleicApp's banner uses.
            swiftSettings: [.define(channelDefine)]),
        .executableTarget(name: "nucleic-smoke", dependencies: ["NucleicProtocol"]),
        .executableTarget(name: "nucleic-punch-harness", dependencies: ["NucleicProtocol"]),
        protocolTestsTarget,
        // The Carbon data layer's suites (docs/CARBON_SHARDING.md §16), split out of the
        // Darwin-only NucleicCoreTests because phase acceptance requires them green on macOS
        // AND Linux (the frozen vectors pin CryptoKit and swift-crypto byte-for-byte).
        .testTarget(
            name: "NucleicCarbonTests",
            dependencies: ["NucleicCore"]
        ),
]

let package = Package(
    name: "Nucleic",
    // NucleicProtocol (the shared sync wire layer) must build for iOS too — that's what the
    // NucleicRemote iPhone client links. The host-only targets (NucleicCore/App/spike) use
    // macOS-only APIs and are simply never built for iOS.
    //
    // macOS floor is 27: the app depends on macOS 27-only features. (The container sandbox is also
    // built directly on Apple's `containerization` framework, whose in-process vmnet networking
    // requires macOS 26 + Apple silicon — comfortably below our 27 floor.) `containerization` is a
    // dependency of NucleicCore ONLY (never of the iOS-linked NucleicProtocol), so the iOS build
    // resolves the package graph but never compiles it.
    //
    // iOS floor is 18.1: TailscaleKit ships with an 18.1 minimum (its `state()` methods use
    // the Failure-typed `any AsyncSequence` existential, an iOS 18 Swift-runtime feature), and
    // SwiftPM has no per-target floors, so the iOS-linked targets rise with it. Every device
    // that runs iOS 17 also runs 18.1, so this drops no hardware — only stale installs.
    platforms: [.macOS("27.0"), .iOS("18.1")],
    products: windowsProtocolOnly
        ? [.library(name: "NucleicProtocol", targets: ["NucleicProtocol"])]
        : coreProducts
            + (onWindows ? windowsOnlyProducts : nonWindowsProducts)
            + (nonDarwin ? [] : darwinOnlyProducts),
    dependencies: packageDependencies,
    targets: windowsProtocolOnly
        ? [protocolTarget, protocolTestsTarget]
        : crossPlatformTargets
            + (nonDarwin ? [] : darwinOnlyTargets)
            + linuxProtocolCTargets
            + (onWindows ? windowsOnlyTargets : nonWindowsTargets)
            + (tailscaleKitAvailable
                ? [.binaryTarget(
                    name: "TailscaleKit",
                    path: "third_party/TailscaleKit/TailscaleKit.xcframework")]
                : [])
)

// NucleicCore's per-platform `exclude` list (hoisted out of the target declaration for the
// same type-checker reason as the products/targets lists above; a function declaration is
// visible to the earlier `crossPlatformTargets` literal, and by the time that literal is
// evaluated the `onLinux`/`onWindows` lets it reads are initialized).
func computeNucleicCoreExcludes() -> [String] {
    let appleOnlyEngineFiles = [
        "Container/ContainerEngine.swift",
        "Container/ContainerEngine+Rootfs.swift",
        "Container/OCIArtifact.swift",
        "Container/ContainerizedProcessHandle.swift",
        "Container/MemoryBalloon.swift",
        "MacVM/MacVMAgentClient.swift",
        "MacVM/MacVMEngine.swift",
        "MacVM/MacVMEngine+Base.swift",
        "MacVM/MacVMEngine+Computer.swift",
        "MacVM/MacVMEngine+ComputerAgent.swift",
        "MacVM/MacVMEngine+ConsentGrant.swift",
        "MacVM/MacVMEngine+HostPaths.swift",
        "MacVM/MacVMEngine+InstallApps.swift",
        "MacVM/MacVMEngine+InstallPackages.swift",
        "MacVM/MacVMEngine+MDM.swift",
        "MacVM/MacVMEngine+MDMDiagnostics.swift",
        "MacVM/MacVMEngine+MDMPolicyPass.swift",
        "MacVM/MacVMEngine+PolicyGrants.swift",
        "MacVM/MacVMEngine+Readiness.swift",
        "MacVM/MacVMEngine+LinuxArtifactCache.swift",
        "MacVM/MacVMEngine+LinuxBase.swift",
        "MacVM/MacVMEngine+LinuxConfig.swift",
        "MacVM/MacVMEngine+LinuxProvision.swift",
        "MacVM/MacVMEngine+Provision.swift",
        "MacVM/MacVMEngine+Provision27.swift",
        "MacVM/MacVMEngine+Reprovision.swift",
        "MacVM/MacVMEngine+ShellDrain.swift",
        "MacVM/MacVMExecChannel.swift",
        "MacVM/MacVMManager.swift",
        "Sync/LANTransport.swift", "Sync/LANBrowser.swift",
        "Sync/LANAddress.swift", "Sync/MeshPathMonitor.swift",
    ]
    let windowsOnlyDirs = ["Windows", "Container/Windows"]
    if onWindows { return appleOnlyEngineFiles }
    if onLinux {
        return appleOnlyEngineFiles + ["Container/ContainerManager.swift"] + windowsOnlyDirs
    }
    return windowsOnlyDirs
}
