s3: roll over the live upload object when the livestream changes master
A single continuous ingest moves through several place.stream.livestream records — each "update livestream" mints a NEW record (chapter markers), baked into subsequent segments' manifests. The S3 uploader stamped each object with one livestream URI at object-start, so an object could straddle a chapter change; finalize(newChapter) then matched no object and returned "no segments found". Cut a fresh object the moment the livestream URI changes (alongside the existing time-based cutover), so every object belongs to exactly one livestream. finalize(URI) is unchanged and now selects exactly that chapter's objects. This also composes with multi-node: a livestream that spans two nodes' StreamSessions tags both nodes' objects with the same shared URI into the shared bucket/statedb, so finalize coalesces them into one VOD (the reconnect's tfdt reset in the middle is the clean EXT-X-DISCONTINUITY case). Per-record VODs for now; a cross-record playlist can stitch a whole multi-chapter stream later. Also: object keys gain a per-uploader sequence number so a same-second rollover (now possible with per-chapter cutover) can't collide/overwrite; and the upload loop's S3 calls go through an injectable interface so the rollover is unit-tested with a fake client. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>