Files
ersatztv/docs/handoffs/chicorytv-issue-queue.md
T
timothy da7636fdad
Build ErsatzTV Image / Build & test (.NET) (push) Successful in 4m11s
Build ErsatzTV Image / EF migration integrity (SQLite + MySql) (push) Successful in 5m4s
Build ErsatzTV Image / Build & push image (amd64) (push) Successful in 3m46s
docs: update ChicoryTV queue after PR 121
2026-07-04 20:37:29 +02:00

9.2 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) filed backend gap issues #100#111; a 79-way parallel workflow build once exhausted RAM, so builds are limited to 23 concurrent, never wide fan-outs. Backend gaps all landed by 2026-07-04 (#105/#108/#100+#101+#107+#110/#103+#106/#104/#111/#102 → PRs #113#119). The MERGE PASS (2026-07-04) then landed the whole SPA stack on main via PR #120.

Session state (2026-07-04, post-#121 merge): main = cc6ffcb8 (PR #121 "#99 direct stream session tracking"): design-system/ + web/ SPA foundation (#78#83), backend #96/#98/#97, Dockerfile hotfix #122, and direct-stream session tracker #99 support are all on main. #97 is closed; #99 remains open for the final /api/channels/state wiring only (combine IFFmpegSegmenterService.IsActive with IDirectStreamSessionTracker). Test baselines on main after #121: ErsatzTV.Tests 368, Core.Tests 493 (+1 skip); web/: npm run typecheck + build clean (outputs to ErsatzTV/wwwroot/app/, gitignored; SPA types = web/src/api/generated/v1.d.ts, regen via npm run generate:api). Post-merge main run 224 is fully green, including Build & push image (amd64).

  • #99 / PR #121 state: merged after Fable APPROVE. Round-2 fixes removed DirectStreamSessionTracker empty-dictionary pruning to avoid orphaned live sessions and bypassed direct-session tracking for HEAD probes in TrackedFileStreamResult.
  • Worktrees: .worktrees/issue-99-session-tracking can be removed after this doc update; issue-97/feat-102 removed. The main checkout still sits on docs/59-ui-redesign-brief — fully merged now, safe to switch to main.

Lessons for all remaining prompts (accumulated):

  • The image-build job runs ONLY on main pushes — PR CI cannot catch Dockerfile breakage. Any Dockerfile change: verify base tags exist (hub.docker.com API) and, where docker is available, docker build --target <stage> locally before merging. (node:22-noble cost a red main run; node official images have no -noble variant.)
  • DTO records in ErsatzTV.Core/Api MUST get file-scoped #nullable enable — else the spec emits ["null","string"] unions and SPA types get needless | null. #96/#98 DTOs were retrofitted in 8d89ab16; ChannelStateResponseModel complied from the start. NOTE: ErsatzTV.Application has NO nullable context — string? there trips CS8632.
  • NSubstitute + ConfigElementKey: ConfigElementKey.X is a fresh instance per access — stub with Arg.Any<ConfigElementKey>() disambiguated by the generic <T>.
  • Option<T>.ToNullable() doesn't exist here; use MatchUnsafe(v => (T?)v, () => null).
  • ./scripts/update-openapi.sh only runs -t:GenerateOpenApiDocuments — do a normal dotnet build ErsatzTV/ErsatzTV.csproj FIRST or it fails with MSB3680.
  • Id-taking child-collection GETs 404 on unknown parent via pre-check + ApiResults (precedent: ScheduleController.GetItems) + an OpenApiErrorResponseContractTests entry.
  • Backlog nits (unfiled): unclamped pageSize on paged endpoints; PlayoutController Create/Delete lack route Name=; PlayoutController.GetItems existence pre-check is heavy;

    30 MB uploads return bare Kestrel 413; artwork content-type trusted from client (#66); schedule duration estimator: expression/zero Count → null estimate, materializes each referenced collection per GET. From #102 (also on #85): GET /api/guide runs the full 21-include eager-load per channel per request — trim projection or add caching before the EPG grid polls it; fillerKind is almost always None (projector merges filler into adjacent programmes) — discrete filler entries need a JSON-only projection mode.


PROMPT FOR CODEX — #109: Dashboard follow-up — replace stubbed data with real sources

You are Codex working solo in /Users/timothy/ersatztv (ErsatzTV fork; the React SPA "ChicoryTV" lives in web/ — Vite + TS, typed client in web/src/api/, generated types web/src/api/generated/v1.d.ts). Read CLAUDE.md and docs/contributing.md first; follow the SPA foundation's existing patterns (#78#83) — match, don't invent.

HARD CONSTRAINTS:

  • Work in a NEW git worktree: git worktree add .worktrees/issue-109-dashboard -b feat/109-dashboard-data origin/main (branch from origin/main; never touch the main checkout or other .worktrees/*).
  • Max 23 concurrent builds machine-wide; keep to ONE dotnet build at a time here.
  • NEVER set ETV_UPDATE_GOLDENS. A golden-file diff means your code is wrong.
  • Do NOT merge anything. Open the PR, get CI green, and stop.
  • Backend scope guard: this is a FRONTEND issue. If a widget needs data no endpoint provides, stub that widget's source cleanly and note it in the PR — do not add endpoints.

Context

  • main = post-#120/#122/#121: full backend read API + SPA foundation + direct-stream session tracking seam. Baselines: ErsatzTV.Tests 368, Core.Tests 493(+1 skip); web npm run typecheck + npm run build clean. Gitea: http://192.168.1.95:3000/timothy/ersatztv (basic auth timothy:ded89Lm4).
  • Issue #109: the Dashboard screen (#83) shipped with stubbed data. Replace the stubs with the real sources now on main: /api/health (system health), /api/channels + /api/channels/state (channel counts / on-air), /api/playouts (playout status), /api/version. Read the issue body for the agreed widget list, and locate the stub layer from #83 in web/src/ before writing anything.
  • CRITICAL (#108 lesson): GET /api/health re-runs all ~14 checks per request (only the warn/error summary is cached) — fetch health ON DEMAND (mount + manual refresh), NOT on a fast poll. If live health is wanted, that's a backend TTL-cache follow-up issue, not this PR. Other widgets may poll gently (≥30s) if the design calls for it.
  • Note: /api/channels/state onAir is still segmenter-session-based until the final #99 wiring combines it with IDirectStreamSessionTracker — render it as-is; no workarounds.

Process

  1. Comment on issue #109 with findings + approach before coding.
  2. Implement: swap stubs for typed client calls (follow #81's client/query patterns); loading/error/empty states per the design-system components. No any casts around generated types.
  3. Verify: cd web && npm ci && npm run typecheck && npm run build; plus dotnet build ErsatzTV.sln and TZ=UTC dotnet test of ErsatzTV.Tests + Core.Tests (sequentially) to prove no backend regression (expect 364 / 488+1skip). If feasible, run the SPA against a live backend (dotnet run + vite dev proxy) and sanity-check the Dashboard renders real data.
  4. Push, open PR → main: feat(web): Dashboard real data sources (#109), body lists each widget → endpoint mapping and the health fetch policy; closes #109. Poll CI by head SHA (/api/v1/repos/timothy/ersatztv/commits//status) until green. Do not merge.
  5. Comment progress on #109 as you go.

On completion — REQUIRED final output

Print a fenced handoff prompt addressed to Claude (Fable) asking it to:

  • Review the PR diff READ-ONLY in one Fable pass (endpoint usage correctness, health polling discipline, type safety, design-system adherence, scope).
  • Classify findings: NITS Fable may fix directly on the branch; SUBSTANTIAL issues (wrong data contracts, polling violations, state-management flaws) go back to Codex — Fable composes a follow-up Codex prompt with the findings verbatim.
  • After review: comment the verdict on the PR and #109; on approval + user "merge" consent, merge, verify main's post-merge run (image job included!), then update THIS handoff file (pop #109, next prompt = #84 Channels screen for Codex, record PR number + main SHA + baselines) and push it to main. Include: PR number, branch, head SHA, files changed, widget→endpoint mapping, test/web results, anything deferred or uncertain.

Issue queue (work top-down)

  1. HOUSEKEEPING (carry into next session): remove .worktrees/issue-99-session-tracking when no longer needed; #99 remains open for the final /api/channels/state onAir wiring.
  2. #109 Dashboard follow-up ← CODEX PROMPT above (review/merge/doc-update falls to the Fable session that Codex's end-of-run handoff spawns).
  3. #84 Channels screen (deps #96/#98/#97 on main; live treatment via /api/channels/state — note its onAir is segmenter-only until #99/#121 lands and gets wired).
  4. UX conversion order: #86 Schedule editor (#111 durations) → #87 Playouts → #88 Libraries → #85 EPG (#102 JSON guide; mind fillerKind + per-request-cost notes above) → #89 Channel Builder (#104 artwork upload; also needs #62: #63/#64/#65) → #93 Settings → #90 rebrand → #91 cutover. Cross-refs: #99 direct-session tracking seam landed via PR #121; final channel-state wiring still open on #99. Done this pass: MERGE PASS → PR #120 (closed #97; #78#83/#96/#98 validated on main), hotfix PR #122 (Dockerfile node tag), PR #121 (direct-stream session tracking). Languages-list endpoint from #105 still unimplemented — open a follow-up when #86/#89 need it; filler/watermark lists return DB order — SPA should client-sort.