Files
ersatztv/docs/handoffs/chicorytv-issue-queue.md
T
timothyandClaude Fable 5 688cf10fee
Build ErsatzTV Image / Build & test (.NET) (push) Successful in 7m35s
Build ErsatzTV Image / Docs update reminder (push) Has been skipped
Build ErsatzTV Image / EF migration integrity (SQLite + MySql) (push) Successful in 9m58s
Build ErsatzTV Image / Build & push image (amd64) (push) Successful in 11m20s
docs: advance ChicoryTV queue past #198/#193 (PRs #200/#201); next = #91 phase b cold-review gate
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-09 20:22:28 +02:00

9.7 KiB
Raw Blame History

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), #145 playback troubleshooting (PR #199), and now #198 OpenAPI casing drift (PR #201) + #193 rerun existence-check (PR #200) are MERGED. v26.5.0 in prod; v26.6.0 tagged (awaiting Komodo pin bump in server-management).

#145, #198, and #193 are CLOSED — every SPA-parity screen exists and the casing-drift bug class is structurally closed. Blazor removal (#91 phase b) is now THE next item, 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 evening, #198+#193 session): main = 2b918371 (merge of PR #201 (#198 casing) on top of PR #200 (#193 rerun existence); post-merge main run was pending at handoff-write time — confirm Build & test + image jobs green before relying on :latest). Tests on main: ErsatzTV.Tests 939 (927 + 7 rerun-handler + 5 OpenAPI-contract), web 385, Core.Tests 493(+1 skip), Architecture 5. Both worktrees removed after merge.

What PR #201 (#198) added:

  • NewtonsoftSchemaNamingTransformer (ErsatzTV/Serialization/, registered on all 3 OpenAPI docs in Startup.cs): renames every schema property/Required key through the RUNTIME CustomContractResolver — spec mirrors runtime by construction (naming-strategy special cases AND [JsonProperty] overrides); collisions throw. Regenerated v1.json/v1.d.ts.
  • Real drift was only ffmpegProfileId (CustomNamingStrategy special case) + ffmpegProfile ([JsonProperty] on ChannelResponseModel — found BY the transformer; fixed a second latent live bug: channel-list FFmpeg column always "Unassigned"). fFmpegPath/fFprobePath/ zIndex/rFrameRate verified live as never drifted.
  • SPA readers/mocks fixed (ChannelEditScreen, ChannelBuilder, App.tsx); #145 escape hatch in PlaybackTroubleshootingScreen removed. CI guard: OpenApiSerializerContractTests (5 fully-populated DTOs through the exact runtime Newtonsoft settings vs committed v1.json).
  • Live E2E verified: editor shows profile 2 with 2 profiles, save keeps ffmpegProfileId=2, troubleshooting preselect, channel-list profile names. Docs: api-conventions §5a, decisions.md, spa-conventions §4.

What PR #200 (#193) added: RerunCollectionSelectionValidation — per-CollectionType, subtype-accurate, non-tracking AnyAsync(EF.Property<int>(e,"Id")==id) existence check in create/update Validate → 422 (deliberately not 404). First RerunCollectionHandlerTests (7). Bonus: removed the broken Playlist option from Blazor RerunCollectionEditor (always persisted an all-null-FK row).

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 RESOLVED — casing drift class is closed: the OpenAPI spec now mirrors the runtime Newtonsoft serializer by construction (NewtonsoftSchemaNamingTransformer + OpenApiSerializerContractTests guard). Generated TS types are trustworthy for response keys again; no more escape hatches needed. The surviving lesson: spec-typed unit-test mocks can't catch runtime-shape drift — the per-PR live-E2E gate stays non-negotiable.
  • Keep the MAIN checkout's web/node_modules fresh (npm install after pulling a PR that adds a dep) — worktrees copy it, and a stale copy broke typecheck in a #198 worktree (hls.js from #145 was missing; fixed 2026-07-09).
  • 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 — #91 phase (b): Blazor removal, part 1 = COLD ADVERSARIAL PLAN REVIEW

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 stays 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 — run the #91 phase-(b) hard gate, then (if cleared) start the removal

  1. Read issue #91 and its phase-(b) gate comment (it embeds the review brief), plus docs/blazor-route-parity.md (Section 2 = SPA-ready-but-not-redirected routes) and docs/decisions.md.
  2. COLD REVIEW (the gate): the review must run from the external ~/adversarial-reviewer project with ZERO shared context — a fresh session there, /review with the brief from the #91 comment. Findings come back via /dispatch. Do NOT run this review from the ersatztv session or with an ersatztv-context fork — cold means cold. If you cannot start the external session yourself, prepare the exact brief + ask the user to run it.
  3. Resolve ALL MUST-FIX findings (file follow-up issues for SHOULDs as appropriate) before any implementation.
  4. If (and only if) the gate clears in-session: implement removal in a worktree — redirect the Section-2 routes (LegacyUiRedirects.cs), delete Blazor pages + MudBlazor deps (kills the 3 deprecated Renovate-dashboard packages incl. MediatR.Courier — but check #145's ITroubleshootingPlaybackStatusStore notification wiring which REPLACED the Courier dependency for the SPA path; verify nothing else still consumes Courier), update docs/blazor-route-parity.md + domain-model.md + README index in the SAME PR. Full PR routine (review fork + live-E2E + CI). Expect this to be its own session; splitting review-session / removal-session is fine and pre-approved by the queue plan.

On completion — REQUIRED last step

Update THIS handoff (pop #91 phase (b) or record partial progress, promote #197/backlog), commit to main, print the next prompt in a fenced code block.


Issue queue (work top-down)

  1. #91 phase (b) Blazor removal ← PROMPT above (cold review gate first).
  2. #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.
  3. 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.