Client progress reports could keep a zero-byte phantom session alive and,
worse, make it outrank a genuinely-serving stream when the enforcer picked
over-cap victims.
`streammonitor.LiveLocalSessions` substituted `Session.LastActivityAt` for a
zero `LastServedAt`, and `LastActivityAt` is advanced by `UpdateProgress` and
by the realtime WebSocket hello/ack/result handlers. Since
`streamenforcer.selectVictims` keeps the `limit` most-recently-served streams,
a progress-only phantom sorted ahead of a real stream and the real one was
trimmed instead. Reaping had the same root cause: `sessionIsInactiveLocked`
keyed idleness on `LastActivityAt`, so a client that kept pinging held a
session open forever.
Per decision A5 (Option C), client progress is now UI metadata only and never
feeds enforcement or reaping:
- `LiveLocalSessions` projects `LastServedAt` verbatim, emitting an empty
timestamp when the session has never served, so it sorts as the stalest
over-cap victim.
- `sessionIsInactiveLocked` measures idleness from `LastServedAt`, falling back
only to `StartedAt`.
- A configurable never-served window (`DefaultUnservedSessionGrace`, 2m, via
`SetUnservedSessionGrace`) keeps a legitimately slow start from being reaped
before its first byte, without granting a phantom unbounded life. It is a
separate knob rather than a hardcoded floor so it cannot silently override
`SetLivenessGracePeriods`.
In-flight transports remain exempt, so direct-play and remux long pours and
per-segment HLS serves are unaffected.
Paused sessions with an open realtime/WebSocket connection are exempt from
reaping. That preserves the issue #243 fix (reaping a paused transcode froze
clients) while staying within Option C: an open, ping-checked connection is
server-observed, unlike a client's reported progress, and the session still
consumes one of the user's cap slots.
Part 1 of 3 for the Batch 4 liveness/replica work.
Part of #305