--- key: spa.playouts-screen-extraction title: '2026-07-14 — Playouts screen extraction (#245): screen-owned route wrapper (epic #243 phase 2)' status: active since: '2026-07-14' supersedes: none superseded-by: none rule: The Playouts domain (including its unguarded `PlayoutsRouteScreen` route wrapper with local pathname/popstate state) moved as one unit into `web/src/screens/PlayoutsScreen.tsx`, keeping its screen-specific sub-path route ownership colocated with the base screen; a pure structural move with no API/route/CSS/behavior change. signals: 'screen extraction, route wrapper ownership, sub-path routing · paths: `web/src/screens/PlayoutsScreen.tsx` · issues: #245, #243' mechanics: '`docs/spa-conventions.md` §2' --- Second bounded extraction under the App.tsx modularization epic (#243): the Playouts domain moved from `web/src/App.tsx` into `web/src/screens/PlayoutsScreen.tsx`, including its loading/error/empty states, dialogs, mutations, timeline/filter helpers, and the existing `PlayoutsRouteScreen`. The dedicated `PlayoutScheduleEditors.tsx` modules remain separate. This is a pure structural move: no API, route, CSS, or runtime behavior changed; `App.tsx` retains only the import and `` dispatch. **The unguarded route wrapper moves with the screen.** Playouts owns two sibling sub-path editors and, per `spa-conventions.md` §2, must keep its local pathname state plus `popstate` listener because App's allow-sub-path route object is reference-stable. Colocating the wrapper keeps that screen-specific route ownership beside the base screen while App-level tests retain the cross-route navigation assertions. **Temporal mutation behavior stays verbatim.** `mutatingRef`, `runMutation`, and the explicit `query.refresh()` after a 409 moved as one unit. The extraction deliberately does not add mount/current guards to these pre-existing promise completions; changing those semantics belongs to a separate issue. Detailed behavior tests now render `PlayoutsScreen` directly with a scoped fetch mock, including the zero-playout Add Playout affordance, lock/409 handling, refresh/poll ownership, action and kind gates, and dialog flows. Refs #245 #243.