Files
LlamaCasty/HANDOFF.md
T
gramps a814bcd502 fix(chat): stop painting what YouTube removed, and retire the local word filter
TASK 49 is retired (creator ruling): YouTube owns chat moderation. Its blocked-words filter and
hold-for-review run upstream of `liveChatMessages.list` — held messages are never returned
(https://stackoverflow.com/questions/74511524/) — so a second local list would duplicate a switch
the creator already owns, with a second set of false positives and a second support surface.

What we actually owed the platform, and had wrong:

- The parser added every item to the overlay. A deleted message arrives as a `tombstone` carrying
  no text at all ("it has since been deleted … only liveChatId, type and publishedAt are present" —
  https://developers.google.com/youtube/data/live/reference/rest/v3/liveChatMessages), so every
  deletion rendered as a BLANK chat row. `hasDisplayContent:false` messages and chat-mode events
  did the same.
- Deletion events (`messageDeletedEvent`/`messageRetractedEvent`/`userBannedEvent`) are documented
  but have not been delivered to clients for years (https://stackoverflow.com/questions/44346893/),
  so nothing is built on them; they are skipped if they ever arrive.
- Found while testing: Super Chat read `displayString`, but YouTube's field is `amountDisplayString`
  — every amount silently fell back to a recomposed string, which mangles non-dollar locales. The
  model's own comment said this was the reason the field exists.

Test: `ChatModerationParseTests` — tombstone, no-display-content, chat-mode events all produce no
row; a real Super Chat still parses its pre-rendered amount (constraint 4: reward fields are never
reinterpreted).

Docs: TASK 49 marked retired with the citations, the rule recorded in decisions.md, HANDOFF updated.
verify.sh: 550 passed, 0 failed, 0 warnings.
2026-11-05 08:39:46 -08:00

2.3 KiB
Raw Blame History

HANDOFF — 2026-11-05 (TASK 64 — green/blue screen for clips)

State

  • main. TASK 21 complete (video file layer + its soundtrack). TASK 64 shipped through slice E: a clip with a green or blue backdrop is offered a key on add ("Looks like a green screen — Remove it"), accepting it keys the background inside the decode pipe (bundled ffmpeg colorkey/despill), with Amount/Softness/Fringe in the panel and the settings persisted. Sessions are keyed by path + look, so identical looks share one decoder.
  • Detection stays silent on lawns/pitches (confidence = coverage × hue tightness) and ignores letterbox bars. It never acts on its own.
  • Slice F shipped too: App Settings → Video clips → "Auto Green/Blue Screen" (off by default, high confidence only, and the panel states when a key was automatic) and alert clips key through the same settings block (one alert box per layout).
  • Tests: ChromaKeyDetectorTests + ChromaKeyApplyTests (pipe asks ffmpeg to key; offer → accept → keyed session feeds the resolver; survives save; auto-apply; alert clip keyed). App build 0 warnings.
  • TASK 49 retired (creator ruling): YouTube owns chat moderation — blocked words and hold-for-review run before the API. What we owed it was not a wordlist but not painting what YouTube removed: the chat parser now skips tombstone (a deleted message's text is never sent) and hasDisplayContent:false. Same commit fixed a live bug: Super Chat read displayString instead of amountDisplayString, so every amount fell back to a recomposed string.

Next action

  • TASK 64 is complete. Next queued: TASK 49 (chat profanity filter). Day 3's TASK 32/33 resilience slices remain outstanding — 32·4 (fragmented MP4) is the smallest.
  • Do not push absent a checkpoint request.

Landmines

  • LayerReorderPersistenceTests.RealMouseDrag_OnTheLayerList_PersistsTheReorder injects physical mouse input. Do not run it while the desktop is in use.
  • Integration tests constructing MainWindow must set a temporary LayoutPathOverride and clear it in finally.
  • Media sessions are keyed by path + look (MainViewModel.MediaSessionKey); never acquire by raw path, and release through ReleaseMediaIfUnused.
  • Bundled FFmpeg stays outside the single-file exe (ExcludeFromSingleFile="true").