Records: main=1b6de047, PR #122 hotfix pending consent, PR #121 (#99) FIX-FIRST review state, new baselines (364/488), worktree cleanup status. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
9.6 KiB
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 7–9-way parallel workflow build once exhausted RAM, so builds are limited to 2–3 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-merge-pass): main = 1b6de047 (PR #120 "ChicoryTV SPA
foundation (#59 stack)"): design-system/ + web/ SPA foundation (#78–#83) + backend #96/#98/#97
all on main; #97 closed (its universal-onAir remainder = #99). Reconciliation was ONE merge
commit (06355b75) + regen (7bd69439) + nullable-DTO fix (8d89ab16); Fable review: no dropped
work, no duplicated routes/tests. Test baselines on main: ErsatzTV.Tests 364, Core.Tests
488 (+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).
- Main image build: post-#120 run 482 FAILED in "Build & push image" only —
node:22-nobledoesn't exist (tests+migrations green). Hotfix PR #122 (node:22-bookworm-slim, one line) is CI-green and awaiting "merge" consent. After merging, confirm the next main run is fully green INCLUDING the image job before deploying anything. - PR #121 (#99, Codex, direct-stream session tracking): OPEN, CI green, base main (pre-#120 — needs a main merge/rebase, confirmed conflict-free with #120). Fable review = FIX-FIRST: (1) Register/Remove race in DirectStreamSessionTracker can orphan a live session's registration; (2) HEAD probes register phantom viewers on ts-legacy. Round-2 Codex prompt (findings verbatim) was handed to the user; full text also in the PR #121 review comment. #99 stays open (final /api/channels/state wiring comes after #121 merges).
- Worktrees: .worktrees/issue-99-session-tracking (Codex, keep); .worktrees/merge-pass (stack landed — REMOVE after this doc lands); 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-noblecost a red main run; node official images have no-noblevariant.) - 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.Xis a fresh instance per access — stub withArg.Any<ConfigElementKey>()disambiguated by the generic<T>. Option<T>.ToNullable()doesn't exist here; useMatchUnsafe(v => (T?)v, () => null).- ./scripts/update-openapi.sh only runs
-t:GenerateOpenApiDocuments— do a normaldotnet build ErsatzTV/ErsatzTV.csprojFIRST 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;
fillerKindis almost alwaysNone(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 2–3 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: full backend read API + SPA foundation. Baselines: ErsatzTV.Tests
364, Core.Tests 488(+1 skip); web
npm run typecheck+npm run buildclean. 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
onAiris segmenter-session-based until #99/PR #121 lands — render it as-is; no workarounds.
Process
- Comment on issue #109 with findings + approach before coding.
- Implement: swap stubs for typed client calls (follow #81's client/query patterns);
loading/error/empty states per the design-system components. No
anycasts around generated types. - Verify:
cd web && npm ci && npm run typecheck && npm run build; plusdotnet build ErsatzTV.slnand 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. - 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. - 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)
- HOUSEKEEPING (carry into next session): merge PR #122 if still open (one-word consent), verify main run fully green incl. image job; remove .worktrees/merge-pass; Codex round-2 on PR #121 (#99) — prompt already with the user — then Fable re-review of the delta.
- #109 Dashboard follow-up ← CODEX PROMPT above (review/merge/doc-update falls to the Fable session that Codex's end-of-run handoff spawns).
- #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).
- 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/PR #121 in flight (see state). Done this pass: MERGE PASS → PR #120 (closed #97; #78–#83/#96/#98 validated on main), hotfix PR #122 (Dockerfile node tag). 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.