7.7.1 - The Now-Playing card survives the screensaver
Fixed
The system Now-Playing card survives a screensaver. A tvOS backgrounding tears the video pipeline down and deliberately keeps the NativeAVPlayerHost alive across it, because AVKit registers its MediaRemote Now-Playing client exactly once per AVPlayer instance and never registers again against a replacement one (Code=14 "requires a client callback", issue #15). The instance has to outlive the suspension or the card dies with it.
The reload on the way back threw that host away. It decided whether to preserve it by reading playbackBackend, and the very teardown that had preserved the host had already reset that value to .none. So the load answered "nothing native here", built a fresh AVPlayer, and left AVKit registered against a player nobody was using any more.
What a viewer saw: pause a film, let the Apple TV fall into its screensaver, resume with the remote. The video plays correctly and the system card is gone, with no transport on the iPhone lock screen, until the player is left entirely and a new AVPlayerViewController registers from scratch.
shouldPreserveNativeHostAcrossLoad(backend:nativeHostSurvives:) asks whether a host is still there rather than which backend is running. Keeping one a load has no use for was already the contract: the software and audio-only dispatch branches each release a preserved-but-unused host before they build. The in-place item handover (#158) keeps reading the running backend, because a background teardown has already unloaded the item and there is nothing left to hand over. (Sodalite#149)
The loopback server keeps answering in a process whose dispatch pool is busy. HLSLocalServer ran its accept loop on a serial queue and every connection on a concurrent one, so both sat on the global pool. Both block by design: accept spends the server's whole life in a syscall, and a handler blocks in UpstreamPump while the origin feeds it. The pool hands out a worker only once one is free, so a process whose workers are all in a blocking wait stopped answering while the server was perfectly healthy, and AVPlayer read a dead server. Accept and each connection get a real thread.
A size probe that blocks on a round trip no longer costs a source its seekability. The staggered open-time ladder ran its three probes with asyncAfter on a concurrent queue, so each held a global-pool worker for a whole semaphore-driven URLSession round trip. Where the pool was saturated the fallbacks never started, open() spent its budget (stagger + min(25, chunkRequestTimeout) + 2), and the source fell back to streaming mode although the origin would have answered the range question. Each probe gets a thread of its own.
Full notes: CHANGELOG.md · 7.7.0...7.7.1