02b8e37526fbfbe8bbdb3177e69409ba487f6172
PR Gates / CI image pin matches docker/ci (pull_request) Successful in 13s
Build ErsatzTV Image / Delimiter ban (release path) (pull_request) Successful in 20s
PR Gates / Fix proofs (Proves trailers) (pull_request) Successful in 10s
PR Gates / decisions lifecycle (pull_request) Successful in 15s
PR Gates / Docs update reminder (pull_request) Successful in 10s
review-verdict/h10 Awaiting review verdict for 02b8e37
Review verdict / Set review-verdict status (pull_request_target) Successful in 20s
PR Gates / Script tests (pytest) (pull_request) Successful in 4m45s
Build ErsatzTV Image / Build & test (.NET) (pull_request) Successful in 8m51s
Build ErsatzTV Image / EF migration integrity (SQLite + MySql) (pull_request) Successful in 6m25s
Build ErsatzTV Image / Build & push image (amd64) (pull_request) Skipped
Build ErsatzTV Image / Functional E2E (curl + UI contracts) (pull_request) Canceled after 2m24s
Build ErsatzTV Image / API docs in sync (OpenAPI + endpoint index) (pull_request) Canceled after 0s
Build ErsatzTV Image / Formatting (changed .cs conform to .editorconfig) (pull_request) Canceled after 0s
Cold adversarial review found two false factual claims written into the new decision records by the change whose whole purpose is to make such records authoritative. Both are corrected; the code was sound. 1. The Id exemption. Its written reason was "the PUT replaces the item set, so B's rows are new rows with new ids". The request forwards each Id, so the handler takes its id-based reconcile path and updates rows in place. Removing the exemption leaves the test green, so Id is now compared and the exemption set is empty. The inherited "ignoring regenerated row ids" comment above the round trip said the same false thing and is corrected too. The reviewer's proposed replacement rationale — that comparing Id now pins #252's id-preservation invariant — is ALSO an overclaim, and the check is cheap: replacing the forwarded r.Id with null, which sends the handler down the create-new-rows path, leaves this test GREEN, because a fresh database reassigns the same ids once the old rows are deleted. Measured, not assumed. The comment now says what the assertion does not establish, and points at ReplaceProgramScheduleItemsReconcileTests, which does pin it. 2. The second clause's negative — "no persisted child state is keyed to those item ids" — is false. PlayoutItem.SchedulingContext serializes a BlockSchedulingContext carrying BlockItemId, and its consumer falls back to (id, null, null) once ReplaceBlockItemsHandler renumbers those ids, so the troubleshooting view silently loses collection type and name. GuideGroup is set to TemplateItemId and persisted while ReplaceTemplateItemsHandler renumbers it. Restated narrowly and dated: no PROGRESSION state is keyed to those ids; two diagnostic/grouping fields are, and are orphaned. Degradation, not data loss. Also from the review: - the anti-vacuity check is a PIN (ShouldBe(55)) rather than a >= floor, which was the one-sided version of the both-directions rule the record invokes; its message interpolates the expectation instead of a stale literal - the record now states that a complete COMPARER is not a complete FIXTURE: the seed builder is still hand-written, so a new nullable field is compared null-against-null until someone seeds it - ProviderWith appends the WriteKey entry LAST so a caller cannot override it to empty and send a unit test into the live config volume - the elementwise-comparison comment no longer implies value equality is intrinsic; it holds because those elements are records Mutation proofs re-witnessed after the clauses changed: pin 55->60 reddens, a bogus exemption name reddens, and the r.Id->null mutation is recorded as NOT reddening, which is why the Id claim is stated as it is. Refs #779 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
fix(754,757): declare graphicsElementIds + padToNearestMinute, and pin every MCP tool to its OpenAPI contract (#760)
fix(754,757): declare graphicsElementIds + padToNearestMinute, and pin every MCP tool to its OpenAPI contract (#760)
ErsatzTV
ErsatzTV lets you transform your media library into a personalized, live TV experience - complete with EPG, channel scheduling, and seamless streaming to all your devices. Rediscover your content, your way.
How It Works
- Install ErsatzTV: Download and set up the server on your system.
- Add Your Media: Connect your media libraries and collections.
- Create Channels: Design and schedule your own live channels.
- Stream Anywhere: Watch on any device with IPTV and EPG support.
Key Features
- Custom channels: Create and schedule your own live TV channels.
- IPTV & EPG: Stream with IPTV and Electronic Program Guide support.
- Hardware Transcoding: High-performance streaming with hardware acceleration (NVENC, QSV, VAAPI, AMF, VideoToolbox)
- Media Server Integration: Connect Plex, Jellyfin, Emby and more.
- Music & Subtitles: Mix music videos and enjoy subtitle support.
- Open Source: Free, open, and community-driven project.
Documentation
Documentation is available at ersatztv.org.
License
This project is inspired by pseudotv-plex and the dizquetv fork and is released under the zlib license.
