AetherEngine 5.23.4
Patch release. One fix to a reported figure, plus the contract it belongs to, stated consistently.
Fixed
The reported buffer frontier no longer collapses to the playhead under an opt-in whole-source prefetch
clock.bufferedPosition fell back to exactly the playhead a few minutes into a session and stayed there for the rest of it. A host drawing a buffer bar from it watched the bar shrink to nothing and vanish, while a quarter-hour of content sat resident on disk.
The cause was an anchor mismatch rather than byte accounting. The frontier walk started at the playhead's segment, while the cache's eviction window is anchored on the consumer's fetch target (lo = target - backwardWindow). AVPlayer's fetch target runs around 120 s ahead of the playhead, so the playhead's own segment falls below the retained low end and is an evictable extra. Only an opt-in prefetch ever reaches the retention budget, so only there does the eviction of those extras actually run, which is why a whole-source window surfaced a bug the historical 10-segment window never could: the walk began on a hole and reported nothing cached ahead on every tick.
The walk now anchors at max(playhead, fetch target). That is sound rather than merely optimistic, because a segment is only ever declared as the target from the segment-serve path, so everything below it has been handed to the consumer and sits in its own buffer.
Simply skipping the leading hole would have failed in the opposite and more dangerous direction. After a backward seek the band above the new position survives, since nothing forces its eviction while the budget has room, and a walk that skipped the hole would report that stale band as buffered when almost nothing is available where playback actually is. Anchoring at the target stops the walk at the hole above the freshly produced segments instead.
Playback was never affected and nothing was ever missing: AVPlayer held the gap in its own buffer and the union of that buffer and the cache stayed contiguous throughout. Only the published figure was wrong.
Changed
clock.bufferedPosition's contract is now stated the same way everywhere
It is the end of the contiguous safe range ahead of the playhead: on the native path what AVPlayer already holds, plus the contiguous disk cache band above it, which is the part that grows with the network buffer setting. On the software path it remains the newest demuxed source PTS, and the audio path still mirrors currentTime.
The documentation had drifted, describing the frontier as AVPlayer's loadedTimeRanges span in one place, superseded in 5.0.0 when it moved to the disk cache read-ahead, and as the disk read-ahead alone in another. No behaviour change beyond the fix above, which also removes an under-report during the first seconds of a session before the cache has built.
Verification
7 new tests in Issue207FrontierAnchorTests covering the eviction precondition, the collapse, the target-anchored band, the backward-seek trap, a backward refetch behind the playhead, the unchanged default window, and a cache with no declared target. Full suite 1081 tests in 174 suites green, swift build -Xswiftc -strict-concurrency=complete clean, CI green on macOS, tvOS Simulator and iOS Simulator.
Acknowledgements
Thanks to @fxndxs, who reported this and root-caused it from the field logs of the #207 confirmation session, including the backward-seek trap that rules out the simpler fix, and who separately confirmed the byte-bounded prefetch's tail behaviour on device to the segment.