# HANDOFF — 2026-09-10 ## Branch / Commit State `main` HEAD = `c45cbc9` (slice 10 committed), **ahead of origin by 15, NOT pushing** — **user ruling (2026-09-10): do not push until the web-overlay transparency AND audio silence issues are addressed.** Both are open (see Still Open). Working tree clean. ## The timing saga — where it stands - **slice 9 (committed `bd396e4`)** fixed the DURATION (count-based CFR, deadline never rebased, `-re` removed): file length == wall time by frame-count construction. - **But the CONTENT still hiccuped** — the creator read "1...23...4...56..." in the recording, and the aggregates (301/300, uniform PTS, 15.6s wall vs 15.74s file) could NOT see it. Root cause finally measured in `FfmpegEncoder.SubmitFrameAsync`: it BLOCKED on `WriteAsync(8.3MB)+FlushAsync` when ffmpeg lagged the pipe, and slice 9's burst `while` loop then re-wrote that SAME composite for every crossed slot — frozen runs. - **slice 10 (current, uncommitted)** — the OBS `obs-encoder.c` shape: 1. `FfmpegEncoder.SubmitFrameAsync` is an ENQUEUE into a bounded `Channel` (cap 120) drained by its own task; the pump NEVER blocks on the pipe. 2. Queue full → drop the NEWEST frame + count (`IFfmpegEncoder.DroppedFrames`). Stop flushes the whole queue, then EOF. 3. Pump: ONE fresh composite per iteration (burst loop deleted) — no stale re-write. 4. **Burned-in frame counter** (the new judge, replaces the WSL ticker): 6-digit dot-matrix strip, white box, bottom-right of every composite. Decoding the file reads +1/frame; jumps = counted drops. Clock-independent. 5. Stats: `worst submit`, `dropped N`, `stalls K`; stall log names iterations > 2× interval. ## NEXT STEP (ONE user run required — the take that closes timing) Record ~20s (WSL ticker visible in the preview is optional now — the burned counter is the judge), then check: - **decode the recording and read the bottom-right frame counter** (probe a few frames widely spaced + the same region across a densely-sampled range): the number advances **exactly +1 per frame**, jumping only where drops are countable - `%APPDATA%\ytLlive\startup.log`: `FramePump stats:` ≈ n/n per 5s (n=300 @60fps), `dropped 0`, `stalls 0`, `worst submit ≈ 1-3ms` - NO "FramePump stall:" lines, NO frozen-content runs in the playback - Extract a few frames around any suspicious moment and correlation-check the strip. ## Committed so far this session (before slice 10) - `d212d5a` — tools: WSL ticker (`tools/ticker.c` + binary) - `bd396e4` — fix(rec): CFR emission + `-re` removal + docs (ai.md slice 9, MyMistakes, HANDOFF). Cites libobs video-io.c. - Do NOT push until the user says (multi-commit local only). ## Still Open - Web overlay: pre-parse transparency injection NOT yet proven — recorded diagnostic PNGs (`%TEMP%\ytLive-web-.png`) were 100% alpha=0 AND RGB=0 (an EMPTY capture). Frozen pending the timing verification. - Audio silence: silent audio (full-length, -91dB) in the take-15 file — the named-pipe audio delivers ~nothing. Separate from timing; queued follow-up, NOT this change. - WSL-ticker-under-heavy-load reliability check: informational (burned counter is judge). - Pre-existing: `Composite_FullScene_MasterPixels` line 109 pixel (1380,700) — verbose cyan-vs-magenta; fails with slice-9 stashed; untouched by both slices. Separate compositor investigation. DO NOT fix in the timing work. ## Landmines - testhost shares startup.log with app — filter by time when triaging - testhost/exe lock DLLs: `taskkill /F /IM testhost.exe /IM ytLive.exe` before rebuild - Build via the Windows dotnet host: `/mnt/c/Program Files/dotnet/dotnet.exe build "C:\Users\gramp\Documents\Code\projects\ytLive\ytLive.csproj"` - No Linux ffmpeg / no sudo on this box — probing uses `/mnt/c/Program Files/Krita (x64)/bin/ffmpeg.exe` - `tools/ticker`: constant ~+982ms offset on the FIRST line is cosmetic (t0 a beat late)