Also: record prod cutover as done in CLAUDE.md (fork :prod live since
2026-06-27) and add standing v26.4.0 release-checkpoint note to the handoff.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Records the #128 review outcome (percent-fraction + poll-start-race fixes,
bee686c8), main d63719ec, baselines (368/493+1skip/75 web), and new lessons
(numeric wire-field units, trigger≠started grace windows, no ref mutation in
updaters).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Records the #125 review outcome (refuted reorder blocker → #126), pre-merge
fixes (f18f2dc0), main 229f7cc1, baselines (368/493+1skip/52 web), and new
lessons (mount call-count assertions, no world-refetch, no list[0]-only UIs).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Records the new Fable-prompts/Fable-reviews process, PR #124 review outcome
and pre-merge fixes (ecf6cf75), main 7ee6e987, baselines (368/493+1skip/39 web).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Records: main=4ffd777b, review-fix cycle lesson (fixture values must come from
backend serializers), health no-poll policy, baselines 368/493 + 31 web tests.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Adversarial review fixes for the #97 endpoint:
- nowPlaying now resolves like the XMLTV guide: when the wall-clock item
is pre/mid-roll filler, surface the guide group's program item (title +
full program bounds) instead of the filler; a guide entry with no
program item (e.g. fallback loop) reports null. Previously an ad break
would retitle the channel and reset progress every 30 seconds.
- covering-item lookup now runs one cheap projected query per distinct
playout offset instead of hydrating the full metadata include tree for
a window widened by the largest offset across ALL channels (up to 24h);
metadata is fetched in a second query scoped to the covering guide
groups, time-bounded to stay safe against GuideGroup recycling (mod
10000). Regression caught while testing: covering items are keyed by
(source channel, lookup time) so a source channel no longer inherits
its offset mirror's lookup result.
- GetChannelStatesForApi now carries Now explicitly (controller passes
DateTime.UtcNow), making boundary semantics testable: new tests pin
now == Start (playing) and now == Finish (not playing), plus mid-roll,
filler-only, and mirror-source-row cases.
- ChannelNowPlayingResponseModel title is non-nullable in the schema
(#nullable enable) — GetDisplayTitle always returns a string.
- GetState uses the typed-return controller convention; Playouts.Mapper
reverted to internal (handler lives in the same assembly).
- Spec doc notes the OnDemand drift caveat and filler semantics.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Living handoff document driving the sequential backend-gap work
(#97, #100-#111, merge pass, then UX screens). Each session works
one queue item and rewrites this file for the next.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Adds the Auto-tuning wizard as hero screen 5 (#69) and annotates the
Channels-list and Schedule-editor sections + prompts with the borrowed
enrichments: lineup health + resume badge (#72, #68), weighted
distribution / reroll / pad-to-boundary / seasonal (#70, #71, #77, #73).
Refs #59#69
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Replaces the inherited 4-editor channel-creation chain with a single
library-to-lineup builder: browse/search library with artwork, drag
shows into the lineup, friendly toggles (shuffle, always-on), one image
for logo+bug, technical settings via built-in/custom Channel Templates
with advanced override. Lists prerequisite backend gaps (composite
create endpoint, Channel Templates, library browse API, full image-type
support incl. transparent re-encode, unified logo/bug, always-on vs
on-demand) and a Claude Design prompt for the screen.
Refs #59
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Records the locked design direction for #59: React SPA on the REST API,
API-first (design pulls the API), Komodo-slick look, ChicoryTV dark +
chicory-blue brand. Includes the 4 hero screens with real domain
vocabulary and ready-to-paste Claude Design prompts.
Refs #59
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Should_Not_Crash_Mid_Roll_{Zero,One}_Chapters passed 'new PlayoutItem()'
to AddFiller, whose Start defaults to DateTime.MinValue. The mid-roll Pad
path (PlayoutModeSchedulerBase.cs:609) subtracts 'currentMinute' minutes
from StartOffset; since StartOffset does ToLocalTime(), a non-zero local
offset makes currentMinute non-zero, underflowing DateTimeOffset.MinValue
and throwing ArgumentOutOfRangeException. Under UTC the offset is 0 so it
stays in range (hence CI was green). Unreachable in production — real
playout items never start at MinValue — so this is a test-data artifact,
not a product bug.
Fix: seed the fixtures with a real Start (startState.CurrentTime.UtcDateTime),
matching the convention in the passing AddFiller tests. Verified across
UTC, Europe/Brussels, and Asia/Kolkata (+5:30); full Core.Tests now passes
under any timezone (484/0). Updated docs/contributing.md §8 per its §10
deviation policy.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Two-phase handoff for a parallel session: Phase 1 investigates the
existing API/CQRS/validation patterns and produces a design + increment
split (Channels-first); Phase 2 implements per vertical slice via
workflows/ultracode. refs #2
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Re-investigated ersatztv#1 against the live stack: ErsatzTV emits
request-host-driven URLs (44 cached guide fragments use {RequestBase},
zero baked localhost), and Jellyfin no longer tuners ErsatzTV directly —
it funnels through Dispatcharr. The historical localhost came from a
client fetching with Host: localhost:8409, a path that no longer exists.
Document the live topology + evidence + resolution; reframe the optional
base-URL setting as not-built; note the latent fragility. Remove the now
spent handoff doc.
refs #1
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- docker-build.yml: the image `build` job now `needs: [test, migrations]`, so a
broken migration blocks the image build (not just the test gate). main branch
protection also now requires the "EF migration integrity (SQLite + MySql)" check
on PR merges. (follow-through on #13)
- docs/handoffs/m3u-tvg-logo-fix.md: working brief for the next task (#1, M3U
tvg-logo localhost), incl. the finding that the issue's GenerateChannelLogoUrl
root-cause is a red herring (that localhost is the ffmpeg watermark path, fetched
in-container; investigate the request flow / forwarded headers / cached values
instead). References the #11 golden + #16 E2E regression nets.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
docs/contributing.md — a descriptive guide to the established patterns, derived from
the existing code: architecture/layering, CQRS handler conventions, LanguageExt
functional style, Blazor/MudBlazor UI, EF Core + dual-provider migrations, the FFmpeg
pipeline pattern, naming/formatting/analyzers, testing, and build/CI — each with
concrete file references — plus the deviation policy (match the established style;
diverge only with a concrete, stated reason). Several sections are now CI-enforced
(#12 layering, #15 analyzers, #13 migrations, #11 golden tests). CLAUDE.md points to
it so it's loaded as project guidance.
Survey across the sections done with parallel read-only agents; all file references
verified to exist.
Refs #10
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>