Implements #74 — a per-channel "On Now / Next" transient EPG overlay burned onto the transcoded stream at each program change, reusing the existing SkiaSharp graphics engine.
What shipped
ChannelGraphicsElement join (5th, mirrors DecoGraphicsElement) + dual-provider migration — the first channel-level graphics-element attachment (previously only Playout/ProgramScheduleItem/BlockItem/Deco).
Selector base layer: GraphicsElementSelector appends channel elements at the fall-through; a deco in Override/Disable (and Merge/Override during filler without UseGraphicsElementsDuringFiller) suppresses them; HLS-Direct renders none. Eager-loaded in FFmpegProcessHandler.
Seeded built-in on-now-next.yml text element — materialized once (marker graphics.on_now_next_seeded, adopt-not-clobber à la #67); row created by the existing refresh.
REST: channel graphicsElementIds: int[] (full-replace, reconciled) + server-derived builtIn on GraphicsElementResponseModel (filename discriminator, not name).
SPA: Branding-tab "Show On Now / Next overlay" toggle mirroring the logo-bug switch; draft round-trips graphicsElementIds.
Live-E2E (running instance): seed builtIn:true + channel PUT graphicsElementIds attach/detach round-trip. Visual pixel render deferred to post-merge on a content-bearing instance (see Done-when).
Implements #74 — a per-channel "On Now / Next" transient EPG overlay burned onto the transcoded stream at each program change, reusing the existing SkiaSharp graphics engine.
## What shipped
- **`ChannelGraphicsElement` join** (5th, mirrors `DecoGraphicsElement`) + dual-provider migration — the first *channel-level* graphics-element attachment (previously only Playout/ProgramScheduleItem/BlockItem/Deco).
- **Selector base layer**: `GraphicsElementSelector` appends channel elements at the fall-through; a deco in Override/Disable (and Merge/Override during filler without `UseGraphicsElementsDuringFiller`) suppresses them; HLS-Direct renders none. Eager-loaded in `FFmpegProcessHandler`.
- **Seeded built-in `on-now-next.yml`** text element — materialized once (marker `graphics.on_now_next_seeded`, adopt-not-clobber à la #67); row created by the existing refresh.
- **REST**: channel `graphicsElementIds: int[]` (full-replace, reconciled) + server-derived `builtIn` on `GraphicsElementResponseModel` (filename discriminator, not name).
- **SPA**: Branding-tab "Show On Now / Next overlay" toggle mirroring the logo-bug switch; draft round-trips `graphicsElementIds`.
- **Docs + decision record** `graphics.channel-level-attachment`.
## Verification
- .NET suite 182/182; web tests 25/25 (4 new toggle tests, incl. round-trip); `decisions_validate` OK; format + BOM gates clean.
- Whole-branch adversarial review (Opus): **MERGEABLE**, 0 Critical/Important; 5 Minors deferred to follow-up #568.
- Live-E2E (running instance): seed `builtIn:true` + channel PUT `graphicsElementIds` attach/detach round-trip. Visual pixel render deferred to post-merge on a content-bearing instance (see Done-when).
Design + plan: `docs/superpowers/{specs,plans}/2026-07-22-on-now-next-overlay*`.
fixes #74
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Implements #74 — a per-channel "On Now / Next" transient EPG overlay burned onto the transcoded stream at each program change, reusing the existing SkiaSharp graphics engine.
What shipped
ChannelGraphicsElementjoin (5th, mirrorsDecoGraphicsElement) + dual-provider migration — the first channel-level graphics-element attachment (previously only Playout/ProgramScheduleItem/BlockItem/Deco).GraphicsElementSelectorappends channel elements at the fall-through; a deco in Override/Disable (and Merge/Override during filler withoutUseGraphicsElementsDuringFiller) suppresses them; HLS-Direct renders none. Eager-loaded inFFmpegProcessHandler.on-now-next.ymltext element — materialized once (markergraphics.on_now_next_seeded, adopt-not-clobber à la #67); row created by the existing refresh.graphicsElementIds: int[](full-replace, reconciled) + server-derivedbuiltInonGraphicsElementResponseModel(filename discriminator, not name).graphicsElementIds.graphics.channel-level-attachment.Verification
decisions_validateOK; format + BOM gates clean.builtIn:true+ channel PUTgraphicsElementIdsattach/detach round-trip. Visual pixel render deferred to post-merge on a content-bearing instance (see Done-when).Design + plan:
docs/superpowers/{specs,plans}/2026-07-22-on-now-next-overlay*.fixes #74
🤖 Generated with Claude Code
Review-verdict: MERGEABLE @
b7d58bbfWhole-branch adversarial review (Opus) over the full branch diff: 0 Critical/Important, 5 deferrable Minors (follow-up #568). Precedence, reconcile, seed lifecycle, migration parity, and security all traced clean. Write-path live-E2E confirmed. Visual pixel render deferred to post-merge (Done-when).