139bdaff5a0ee5cbc0829cc706eb72b34216e24e
The #311 fix-as-you-touch rule is already enforced by a pre-commit hook and a blocking CI job, and both missed the same mistake twice on 2026-07-17: PR #405 shipped 6 BOM'd files, PR #402 shipped 19. The gap is that the pre-commit hook is routinely skipped — worktree hook friction makes `git commit --no-verify` the normal path here — which leaves CI, a ~10-minute round trip, as the first thing that tells you. This guard fires on Bash before git runs, so --no-verify can't skip it. It denies `git commit`/`git push` when a .cs the branch touches still carries a BOM, names the files, and gives the strip command. Why a hook rather than a note: a memory describing this exact trap did not prevent either failure — the PR #402 session re-added a BOM an hour after writing that memory down, because the usual ways of touching a legacy file re-add it silently (Python io.open(..., encoding='utf-8-sig') WRITES a BOM back; sed/perl round-trips keep it). A check that runs beats one you have to remember. Follows the existing gate pattern: a fail-open script in .claude/hooks/ registered on the PreToolUse/Bash matcher alongside bash-guard and worktree-guard. Any parse or lookup trouble → allow; this must never be the reason a commit can't happen, and CI remains the backstop. Generated *.Designer.cs / TvContextModelSnapshot.cs are exempt, matching what dotnet format itself skips. Scoped to this repo, and it resolves the target tree from an in-command `cd` because commits here run as `cd <worktree> && git ...` and the harness resets the shell cwd between calls. Verified against a throwaway fixture: denies commit and push for a BOM'd touched file (naming it); allows a clean tree, a BOM in generated files only, `git status` with a BOM present, and `echo 'run git push later'` (no false-trip on the words). Proved live via a sentinel — it fires on the Bash matcher — sentinel removed. docs/contributing.md updated: three enforcement layers, why the third exists, the utf-8-sig re-add trap, a by-hand check, and the bash-not-zsh caveat for `dotnet format --include` (mapfile is bash-only; under zsh the file list is empty and the tool looks like it silently did nothing). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
ci(406): disable swap on the capped mysql; fix peak-step placement and failure mode [decisions-edit]
feat(api): optimistic-concurrency contract for replace-all PUTs — PR1 infra + Block reference (#253)
feat(api): optimistic-concurrency contract for replace-all PUTs — PR1 infra + Block reference (#253)
Merge pull request 'feat(69): Auto-Tune SPA wizard (Configure → Preview → Create) — PR2' (#389) from feat/69-auto-tune-spa into main
Merge pull request 'chore(deps): update dependency humanizer.core to 3.0.10' (#328) from renovate/humanizer-monorepo into main
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.
