Files
ersatztv/docs/handoffs/chicorytv-issue-queue.md
T
timothyandClaude Fable 5 415da214ec
Build ErsatzTV Image / Build & test (.NET) (push) Successful in 7m36s
Build ErsatzTV Image / Docs update reminder (push) Has been skipped
Build ErsatzTV Image / EF migration integrity (SQLite + MySql) (push) Successful in 10m7s
Build ErsatzTV Image / Build & push image (amd64) (push) Successful in 11m28s
docs: advance ChicoryTV queue past #145 (PR #199); next = #198 casing drift + #193; #91 phase b gated on cold review
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-09 08:24:05 +02:00

141 lines
9.8 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# ChicoryTV issue-queue handoff (living document)
Paste the prompt below into a fresh session to work the next item. Each session ends by
UPDATING THIS FILE in place (rewrite the state section and the queue for the next item) so it
always holds the current handoff. History: created 2026-07-02 after the plan audit (#59 epic);
all backend gap issues (#100#111), all SPA screens (#84#89, #93, #109), the rebrand (#90),
the cutover root-flip (#91 phase a), full scheduling parity (#144/#162), media/troubleshooting
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), and now **#145 playback troubleshooting (PR #199)** are
MERGED. **v26.5.0 in prod; v26.6.0 tagged** (awaiting Komodo pin bump in server-management).
**#145 is CLOSED — every SPA-parity screen exists. Blazor removal (#91 phase b) is now gated
ONLY on the cold adversarial review** (hard-gate comment on #91; runs from the external
`~/adversarial-reviewer` project, zero shared context, brief embedded in the #91 comment).
**Session state (2026-07-09, #145 playback troubleshooting session)**: main = af17dafc
(post PR #199, merge pre-approved by user after CI green; post-merge main run was being
verified at handoff-write time — confirm `Build & test` + image jobs green before relying on
`:latest`). Tests on main: `ErsatzTV.Tests` **927**, web **385**, `Core.Tests` 493(+1 skip),
Architecture 5 — all green. Worktree `.worktrees/feat-145` removed after merge.
What PR #199 added:
- Backend: `GET /api/troubleshoot/playback/{stream-selectors,subtitles/{id},status}`;
`ITroubleshootingPlaybackStatusStore` (Core singleton, `TroubleshootingNotifier` precedent)
filled by a MediatR `INotificationHandler<PlaybackTroubleshootingCompletedNotification>`,
reset at session lock in `PrepareTroubleshootingPlaybackHandler` — the pollable replacement
for Blazor's in-process Courier completion event. `GET /api/graphics-elements?refresh=true`.
- SPA: `/app/troubleshooting/playback` (`?channel=`/`?mediaItem=` modes) mirroring
`PlaybackTroubleshooting.razor`; reusable `web/src/media/HlsPlayer.tsx` (hls.js; `playToken`
prop forces re-attach — REQUIRED because the m3u8 GET is what starts a session and identical
URLs are React no-ops); 2s status polling with a starting→running→settled phase machine
(30s start timeout); entry points: channels-table Troubleshoot action + movie detail page.
- New issues filed: **#198** (OpenAPI spec/runtime property-casing drift, see lessons — M,
includes a LIVE ChannelEditScreen bug on main), **#197** (cold API contract+security review,
runs when MCP PR #76 stabilizes; HARD GATE on #58 closing/go-live). #91 got the phase-(b)
review-gate comment.
**Lessons for all remaining prompts** (conventions live in docs/; this is workflow lore):
- READ docs/README.md → the convention docs FIRST; point recon/implementer agents at specific
doc sections. Only recon the task-specific delta.
- **#198 / spec-typed mocks blind spot**: runtime JSON is Newtonsoft-cased
(`FFmpegProfileId``ffmpegProfileId`) but the OpenAPI generator uses STJ naming
(`fFmpegProfileId`) — generated TS types are WRONG for leading-acronym response fields, and
unit-test mocks typed from them stay green while the real screen breaks. Until #198 lands:
read such fields via a typed escape hatch pinned to the runtime key (precedent:
`PlaybackTroubleshootingScreen` default-profile derivation), and make test mocks use the
runtime casing. Only live E2E catches this class — one more reason the per-PR live-E2E gate
is non-negotiable.
- **Playwright-MCP E2E: never open tabs/window.open for file-download endpoints** — a download
never renders a page and wedges the browser session hard enough to kill the agent (stalled a
sonnet E2E agent 3× this session; had to pkill the mcp-chrome profile). Verify downloads with
plain HTTP (curl) instead. If browser tools stall repeatedly, `pkill -f ms-playwright-mcp`
and drive a fresh session.
- The user's main checkout (/Users/timothy/ersatztv) is now ON main and pulled each session —
keep it that way (stale-checkout confusion resolved 2026-07-09; stale branches pruned).
Still do feature work in worktrees off origin/main; copy `web/node_modules` from the main
checkout into new web worktrees instead of `npm ci`.
- Subagent connection drops = laptop sleep; re-resume via SendMessage (work survives).
- Build/verify locally, then trust it; CI (VM 127) is the slow confirming step (~9 min warm).
Watch by commit status: `/api/v1/repos/timothy/ersatztv/commits/{sha}/status`. A red MAIN
push is most likely a CANCELLED job from VM churn — check conclusion before assuming
regression.
- PR routine (works): worktree off origin/main → implement (opus judgment-heavy / sonnet
mechanical; doc pointers + exact facts; csharp-lsp for C#) → API changes: build app project
FIRST then `./scripts/update-openapi.sh` then `npm run generate:api` → full local test pass
→ push, PR → fork adversarial review (SCOPED "review only") + live-E2E for UI changes
(`scripts/e2e-local.sh`) → review fixes as follow-up COMMITS (never amend/force-push) → CI
green → merge (consent per session; 2026-07-09 session had pre-approval for #199 only) →
structured close comments per CLAUDE.md protocol.
- Never run TWO committing agents on one worktree concurrently. Read-only review forks are fine.
- Reusable API facts: DTO records in ErsatzTV.Core/Api need file-scoped `#nullable enable`;
ErsatzTV.Application has NO nullable context (CS8632); `Option<T>`→nullable =
`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].
---
# PROMPT — #198 OpenAPI casing drift (live ChannelEditScreen bug) + #193 warm-up
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).
FIRST read CLAUDE.md, docs/README.md + the convention docs it indexes, and the Lessons above.
HARD CONSTRAINTS:
- Work in worktrees off origin/main. The main checkout should stay on main/pulled — safe for
reading, but do feature work in a worktree. Copy web/node_modules from the main checkout.
- Up to 3 concurrent builds; never 5+. NEVER set ETV_UPDATE_GOLDENS.
- Merge consent in-conversation per session (prior pre-approvals do NOT carry over).
- 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 1 — #198 OpenAPI spec/runtime casing drift [M]
Read issue #198 first — it has the full analysis. Summary: MVC responses serialize via
Newtonsoft (leading acronyms fully lowercased: `ffmpegProfileId`) but the OpenAPI generator
uses STJ naming (`fFmpegProfileId`), so generated TS types lie about response keys.
**ChannelEditScreen.tsx:122 reads the phantom key → with >1 ffmpeg profile, editing a channel
shows/saves the WRONG profile (repro'd live 2026-07-09).**
1. Root fix: make spec generation match runtime naming (schema transformer or serializer
alignment — investigate what ErsatzTV/Startup.cs + the OpenAPI pipeline allow). Regen
v1.json/endpoint-index/web types (expect broad-but-mechanical v1.json diff).
2. Fix all SPA readers of drifted fields (grep web/src for `fFmpeg`/`fFprobe`/`rFrameRate`/
`zIndex`; at minimum ChannelEditScreen + remove the #145 escape hatch in
PlaybackTroubleshootingScreen) and their test mocks; verify each candidate field against a
live scratch instance (curl) — the single-leading-cap ones (`zIndex`, `rFrameRate`) may not
actually drift.
3. Add the CI guard from the issue: a test that round-trips a known DTO through the real MVC
serializer and asserts the OpenAPI schema uses those exact keys.
4. Live-E2E the channel editor with 2 profiles (the repro case) + playback screen preselect.
5. Full PR routine; close #198.
## Task 2 (warm-up/interleave) — #193 rerun-collection existence check [S, backend]
Per issue: existence pre-check for `selectedId` per selection type in RerunCollection
create/update → 404/422 ProblemDetails instead of dangling FK/500. api-conventions §3
precedent. Can ride as its own small PR.
## On completion — REQUIRED last step
Update THIS handoff (pop #198/#193, promote the #91 phase-(b) queue item), commit to main,
print the next prompt in a fenced code block.
---
## Issue queue (work top-down)
1. **#198** casing drift + **#193** rerun existence-check ← PROMPT above.
2. **#91 phase (b)** Blazor removal — HARD GATE: cold adversarial plan review first (brief in
the #91 gate comment; run `/review` from `~/adversarial-reviewer`, zero shared context,
findings via `/dispatch`, MUST-FIXes resolved before implementation). Then: redirect the
Section-2 routes in docs/blazor-route-parity.md, delete Blazor pages/MudBlazor deps
(also kills the 3 deprecated Renovate-dashboard packages incl. MediatR.Courier), update
docs. Likely its own session (or two: review, then removal).
3. **#197** cold API contract+security review — runs when MCP PR #76 stabilizes; HARD GATE on
#58 close/go-live and MANDATORY before any remote exposure.
4. Backlog: #99 (TS/HLS-Direct session tracking — makes channel-state `onAir` universal),
#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 issue comments).
Cross-refs: v26.6.0 deploy = Komodo pin bump (server-management). Real-transcode E2E of the
playback screen: do once on the test container (`ersatztv-test` runs `:latest`) — local ffmpeg
8.1.2 lacks subtitles/zscale filters.