diff --git a/docs/handoffs/chicorytv-issue-queue.md b/docs/handoffs/chicorytv-issue-queue.md index d3be7dc36..9d8e61330 100644 --- a/docs/handoffs/chicorytv-issue-queue.md +++ b/docs/handoffs/chicorytv-issue-queue.md @@ -8,37 +8,39 @@ the cutover root-flip (#91 phase a), full scheduling parity (#144/#162), media/t parity (#141/#161/#158/#180), onboarding docs (#185), the collections/API-gap batch (#155/#151/#152-backend/#184), the multi/rerun/playlist SPA editors (#151/#152 PR #194, #153 PR #195), CI speedups (#190 PR #192), #145 playback troubleshooting (PR #199), #198 OpenAPI -casing (PR #201) + #193 rerun existence-check (PR #200), and the first #91-gate batch -#210/#211/#213-partial (PR #214) are MERGED. **v26.5.0 in prod; v26.6.0 tagged** (awaiting -Komodo pin bump in server-management). +casing (PR #201) + #193 rerun existence-check (PR #200), the first #91-gate batch +#210/#211/#213-partial (PR #214), and the search/media mutation batch #208/#209 (PR #216, shared +Add-to layer) are MERGED. **v26.5.0 in prod; v26.6.0 tagged** (awaiting Komodo pin bump in +server-management). -**Session state (2026-07-09 late night, #210/#211/#213 session)**: **#210 + #211 CLOSED via PR -#214** (merged; main green). New API surface: `POST /api/playouts/{id}/erase-items` + -`.../erase-items-and-history` (reuse Blazor's ErasePlayoutItems/ErasePlayoutHistory commands; -controller 404 pre-check + per-kind 422 guards), `GET /api/playouts/items/{id}/scheduling-context` -(decode-by-row-id; PlayoutItemResponseModel gained `id` null-for-gap-rows + `hasSchedulingContext`; -PlayoutListItemResponseModel gained `playoutMode`), `PUT /api/collections/{id}/custom-order` -(Index from array order; GetCollectionItems now orders by CustomIndex when the flag is on — array -order carries it, DTO unchanged). SPA: playout Reset/Delete/erase-by-kind wired, scheduling-context -dialog, templates preview month-grid (TS port of `AlternateScheduleSelector.GetScheduleForDate` in -`web/src/screens/playoutTemplateCalendar.ts`), OnDemand alternate-schedules gating, collections -reorder mode (loads ALL pages then move up/down; any-kind, not movies-only — decisions.md), -10-kind add picker, block-copy dialog, watermark `/add?from=` prefill, trash select-all + -pageSize 100. **#213 stays OPEN** (reduced scope: logs sort/page-size persistence, block-history -page-size/gating, blocks/templates list filter — read-only conveniences or API-gap-blocked). -Adversarial review fork returned APPROVE-WITH-FIXES: watermark mode-from-state fixed in-PR; -erase/reset **EntityLocker lock-gating race filed as #215** (systemic API posture, all playout -mutations lock-free — not a #214 regression). Flake note: `App.test.tsx` builder collections -fan-out test deflaked (passive-effect flush let the mount fan-out leak past mockClear on slow CI). +**Session state (2026-07-10, #208/#209 session)**: **#208 + #209 CLOSED via PR #216** (merged +c63cadb8). New API surface: `POST /api/playlists/{id}/items` (wraps existing `AddItemsToPlaylist`; +handler hardened to reject system playlists — latent Blazor-path gap) and `GET +/api/search/all-items` (wraps `QuerySearchIndexAllItems`; Add-All = materialize-then-add, Blazor +two-step parity — no query-based add command exists). SPA: shared Add-to layer +`web/src/media/addTo/` (Collection dialog w/ inline create, Playlist dialog filtering `isSystem`, +Schedule dialog replicating `AddProgramScheduleItem.ForMediaItem` defaults — +`addTo/scheduleItem.ts`, SaveAsSmartCollectionDialog, AddToMenu popover) + `MediaPosterCard` +`actions` slot; wired into search (card drill-in, per-card add, Select toggle + selection bar, +Add All, Save As Smart Collection) and browse/detail (multi-select + select-all-on-page, all four +detail screens, per-show Quick/Deep scan gated Plex/Jellyfin/Emby, per-episode Media Info + +Troubleshoot, seasons browse kind). **Add-to-schedule is gated to shows/seasons/artists** — the +server validator (`ProgramScheduleItemCommandBase.CollectionTypeMustBeValid`) only accepts those +per-media-item kinds, matching Blazor's ForMediaItem call sites (found by live E2E: movie adds +422'd). Also fixed: portal font bug (nothing set a `body` font; portaled dialogs fell back to +Times — `body` rule added), a mount-debounce race (the 300ms query-debounce commit wiped +in-progress selections; `lastQueryRef` no-change guard in Search+Browse screens), and the +e2e-local.sh stale-asset gotcha (`cp -R` into an existing dir nests + serves the previous run's +assets — script now rm-first). New issues: **#217** (add-items handlers validate only 4/10 kinds +— pre-existing), **#218** (fresh-DB "Playouts 3" badge + "1 failing" chip). Retro/pre-review +issues filed in adversarial-reviewer: #18 (this milestone + #214), #19 (upcoming #207/#212, #202, +#91b, #197). #213 remains OPEN (reduced read-only-convenience scope). **Blazor removal (#91 phase b) remaining MUST-FIX gates, in order:** -1. **#208 + #209** — shared "Add to collection/playlist/schedule" component: search-screen - mutations + media browse/detail mutations, per-show scan wiring, episode info/troubleshoot - entries ← NEXT (prompt below) -2. **#207 (+#212)** — schedules editor full mutation depth (endpoints exist; needs languages - enumeration endpoint shared with #212 channel-editor pickers) -3. **#202** — media-source management (write REST API + SPA screens; largest, riskiest) -4. **#91 phase (b) removal PR** — pattern redirects + catch-all (#204 plan), blazor-final tag +1. **#207 (+#212)** — schedules editor full mutation depth (endpoints exist; needs languages + enumeration endpoint shared with #212 channel-editor pickers) ← NEXT (prompt below) +2. **#202** — media-source management (write REST API + SPA screens; largest, riskiest) +3. **#91 phase (b) removal PR** — pattern redirects + catch-all (#204 plan), blazor-final tag (#205), auth posture note (#206), delete Pages/Shared/ViewModels/Validators + 6 packages (MudBlazor, MediatR.Courier.DependencyInjection, Blazored.FluentValidation, BlazorSortable, Heron.MudCalendar, Chronic.Core). Removal recon is DONE (2026-07-09 gate session): Courier @@ -80,10 +82,23 @@ fan-out test deflaked (passive-effect flush let the mount fan-out leak past mock `MatchUnsafe(v => (T?)v, () => null)`; `LanguageExtensions.Apply` collapses NotFoundError→422, so real 404s need a controller pre-check; id-taking child GETs get an OpenApiErrorResponseContractTests [TestCase]. +- **Arm a CI monitor the moment the PR opens** — in the #216 session three PR runs sat red for + ~an hour (a CI-only debounce-timing flake) while review/E2E ran, because CI was only checked + "at the end". Also: concurrent runs can collide on the MySQL service host port 3306 + ("port is already allocated") — that's infra flake, rerun; consider unpinning the host port. +- **"Make X consistent with Y" review findings deserve new-code scrutiny** — a #216 review fix + propagated MediaBrowseScreen's mount-debounce selection-wipe race to SearchScreen instead of + catching it. For any timer/effect, ask "when does this fire?" (incl. mount). +- SPA mutation affordances on media items: USE the shared `web/src/media/addTo/` layer + (spa-conventions §5c) — don't build screen-local pickers. Schedule adds are valid only for + shows/seasons/artists (server validator). +- Live E2E seeding: the local library isn't API-seedable; the #216 E2E agent generated tiny + ffmpeg testsrc MKVs + inserted LibraryPath rows via SQL then scanned. Recipe not yet in + docs/e2e-local.md — worth adding next time it's needed. --- -# PROMPT — #208 + #209: search + media mutation surface (shared "Add to…" component) +# PROMPT — #207 + #212: schedules editor mutation depth + channel-editor gaps You are Fable, the ORCHESTRATOR in the main Claude Code session. Fable is EXPENSIVE: delegate (recon → Explore/haiku; mechanical → sonnet; judgment-heavy → opus; fable forks for review). @@ -97,48 +112,48 @@ HARD CONSTRAINTS: fan-out — if 1-min load > ~6, drop to 2–3. Never 5+ builds (the historic crash was an 8–9-way fan-out). NEVER set ETV_UPDATE_GOLDENS. - Never two committing agents on ONE worktree — give each parallel slice its own worktree - branched off the feature branch and merge back (worked well for #213 alongside #211). - Sequence only where a slice genuinely depends on another's output (e.g. SPA needs the - backend's regenerated OpenAPI types — consider stubbing types or splitting backend-first - narrow, SPA-wide after). + branched off the feature branch and merge back. Sequence only where a slice genuinely depends + on another's output (backend-first narrow, SPA-wide after worked well for #216). - Merge consent in-conversation per session (prior pre-approvals do NOT carry over). +- Arm a CI monitor on the PR head sha AT PR-OPEN (commit-status endpoint), not at the end. - Live-E2E via scripts/e2e-local.sh; NEVER exercise download endpoints via browser tabs (curl them). Adversarial review fork per PR diff, SCOPED "review only". -## Task — close the #208 + #209 gates -Read issues #208 and #209 (bodies carry exact file:line evidence from the #203 sweep). -1. **#208 search screen**: the SPA search screen is display-only; Blazor's search page mutates - (add result → collection/playlist/schedule). Build the shared "Add to - collection/playlist/schedule" component ONCE (design for reuse by #209) and wire it into - search results. Check the endpoint index first — several add-item endpoints already exist - (collections add-items accepts all 10 kinds since PR #214). -2. **#209 media browse/detail**: the same component on browse tiles + detail pages; per-show - scan wiring (endpoint exists — check index); episode info + troubleshoot entry points - (playback troubleshooting takes `?mediaItem={id}`; MediaDetailScreen.tsx:524 carries a stale - "#153/#155" pointer to fix). -Decide component placement per spa-conventions (shared component location) and record any new -convention in decisions.md. Full PR routine; update blazor-route-parity.md verdict rows -(#208/#209 clusters), OpenAPI regen if any new endpoint. Comment + close #208/#209 per protocol. +## Task — close the #207 + #212 gates +Read issues #207 and #212 (bodies carry the #203-sweep evidence) and adversarial-reviewer #19 +(pre-review asks). #207 is the screen the false-parity review made an example of: the SPA +schedules editor is a near read-only viewer — schedule create/edit/delete missing, every +per-item inspector control hard-coded disabled (~35 Blazor fields), add-item is defaults-only — +while the REST endpoints already exist and are unused (`PUT /api/schedules/{id}/items` replace, +`POST .../items` single-add with the FULL ScheduleItemRequest — see `addTo/scheduleItem.ts` for +a working payload builder). Enumerate EVERY Blazor `ScheduleItemsEditor.razor` field and wire it +(per-capability diff is the parity standard). #212 channel editor gaps: external logo URL, bare +create, and pickers — needs a languages enumeration endpoint shared by both editors (preferred +audio/subtitle language pickers); design that endpoint once. Record conventions in +decisions.md; full PR routine (OpenAPI regen for any new endpoint; blazor-route-parity verdict +rows for the schedules + channel-editor clusters). Comment + close #207/#212 per protocol. ## On completion — REQUIRED last step -Update THIS handoff (pop the done items, promote #207+#212 to next with a fresh prompt), -commit to main, print the next prompt in a fenced code block. +Update THIS handoff (pop the done items, promote #202 to next with a fresh prompt), commit to +main, print the next prompt in a fenced code block. --- ## Issue queue (work top-down) -1. **#208 + #209** search + media mutation surface (shared "Add to…" component) ← PROMPT above. -2. **#207 (+#212)** schedules editor depth + channel-editor gaps (shared languages endpoint). -3. **#202** media-source management (write API + SPA) — largest. -4. **#91 phase (b)** Blazor removal PR (gate cleared once 1–3 close; #204/#205/#206 fold in; - removal recon in session state above). -5. **#197** cold API contract+security review — HARD GATE on #58 close/go-live and MANDATORY - before any remote exposure. -6. Backlog: #215 (API playout mutations skip EntityLocker gating — from #214 review), #213 - remainder (logs sort/page-size persistence, block-history page-size/gating, list filters), - #99 (TS/HLS-Direct session tracking), #66 (artwork magic-byte sniffing), backlog nits - (unclamped pageSize, 30 MB bare 413, PlayoutController route Name=/lightweight exists-check, - guide 21-include eager-load + fillerKind notes — see #85/#102 comments). +1. **#207 (+#212)** schedules editor depth + channel-editor gaps ← PROMPT above. +2. **#202** media-source management (write API + SPA) — largest; pre-review the API design + (adversarial-reviewer #19) before building. +3. **#91 phase (b)** Blazor removal PR (gate cleared once 1–2 close; #204/#205/#206 fold in; + removal recon in session state of the 2026-07-09 entry; adversarial pass mandatory). +4. **#197** cold API contract+security review — HARD GATE on #58 close/go-live and MANDATORY + before any remote exposure. Inputs: #215, #217, backlog nits. +5. Backlog: #215 (API playout mutations skip EntityLocker gating), #217 (add-items validates + only 4/10 kinds), #218 (fresh-DB Playouts badge / "1 failing" chip), #213 remainder (logs + sort/page-size persistence, block-history page-size/gating, list filters), #99 (TS/HLS-Direct + session tracking), #66 (artwork magic-byte sniffing), CI: unpin MySQL service host port 3306 + (concurrent-run collision), nits (unclamped pageSize, 30 MB bare 413, PlayoutController route + Name=/lightweight exists-check, guide 21-include eager-load + fillerKind notes — #85/#102 + comments), review nits from #216 (last-used-collection memory, detailBrowseItem cast). Cross-refs: v26.6.0 deploy = Komodo pin bump (server-management). Real-transcode E2E of the playback screen: once on the test container (`ersatztv-test` runs `:latest`) — local ffmpeg 8.1.2 lacks subtitles/zscale filters.