Build ErsatzTV Image / Docs update reminder (pull_request) Successful in 5s
Build ErsatzTV Image / EF migration integrity (SQLite + MySql) (pull_request) Successful in 7m57s
Build ErsatzTV Image / Build & test (.NET) (pull_request) Successful in 9m3s
Build ErsatzTV Image / Build & push image (amd64) (pull_request) Has been skipped
Move the Channels domain verbatim out of web/src/App.tsx into web/src/screens/ChannelsScreen.tsx (zero-prop, self-sufficient, mirroring the SchedulesScreen extraction). Pure structural move: no API, route, CSS, or visual change. App.tsx retains only the import + the <ChannelsScreen /> dispatch. - 14 symbols moved (ChannelViewFilter → ChannelTableRow); the Dashboard-owned progressFromNowPlaying is inlined into the moved progressFromChannelState so the screen has no import back into App.tsx (behavior-identical). - 12 Channels behavior tests moved to a colocated ChannelsScreen.test.tsx with its own scoped fetch mock (renders <ChannelsScreen /> directly, no mockDashboardApi); App.test.tsx keeps one nav-smoke test for the route. - Pruned 12 now-dead App.tsx imports; shared symbols (ChannelState, messageFromError, ApiError, useChannelsQuery) verified still used and kept. - Docs: spa-conventions §6 (extracted-screen own-fetch-mock convention), decisions.md (single-file rationale; no web/src/channels/ sibling dir, unlike Schedules; inlined helper; #238 deferral). Verified: web vitest 587 passed, eslint clean, tsc/vite build clean, check:api no drift. #212 empty-lineup bare-create success+failure coverage preserved. #238 TopBar dead-button left as-is (its owned bug; shell redesign is epic phase 4 / #247). refs #244 #243 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>