# HANDOFF — 2026-09-14 (signed audio sync SHIPPED + PUSHED: −500..+500, negative advances by eating the stream head; slider locked while live/recording) ## Branch / Commit State `main` HEAD = **`b22d08e`** (signed audio-sync, committed AND **pushed** to origin/main with the user's greenlight — 29 commits pushed in one go). Working tree **clean**. DB now: **`Audio.SyncOffsetMs = 0`** (confirmed via sqlite3 — the creator slid the sync to 0 and the write finally stuck; sound is correct at 0). The 0.54s residual in the 1128 take ≈ 300ms injected offset (leftover DB value) + ~240ms natural (partly webcam clap-quantization ±40–85ms, partly measurement). ## ⚠️ Branding (2026-09-14, creator-corrected): product = **llamacasty**, internals = ytLive The product is **llamacasty**; the repo path, csproj `AssemblyName`/`RootNamespace`, DB/log paths (`%APPDATA%\ytLlive\...`), and most code names are the legacy **ytLive/ytLlive**. User-facing language must say "llamacasty"; code/assembly/repo names stay ytLive. Full detail in `ai.md` → Brand → "Product name vs repo/assembly branding". ## ✅ SHIPPED (this dirty tree) — signed audio sync, −500..+500 **What:** the audio-sync control is now a SIGNED offset. Positive = delay the mix (audio runs AHEAD of video — existing `AudioSyncDelay` behavior, unchanged and live-reactive). Negative = **advance** the audio (audio runs BEHIND video): OBS's "eat the head of the buffer" fix — the mixer drops the first |N| ms of the written stream at the pipe, re-anchoring the audio stream so every event lands |N| ms EARLIER relative to video. **Mechanism (AudioMixer):** `StartLive` arms `_advanceSamplesRemaining = |N| ms → samples` at go-live (a negative offset can only eat the HEAD of the stream; it is armed once, not live-reactive). `LiveLoopAsync` skips `min(budget, mixBuffer.Length)` samples off each write head while the budget lasts — the pipe writer accepts a partial chunk via `AsMemory(writeFrom)`. Positive path untouched (delay line still re-reads the Func every tick). **UI/plumbing:** - `MainViewModel.Audio.cs` — clamp `Math.Clamp(value, -500, 500)`, doc updated. - `LayoutStore.Settings.cs` — `LoadAudioSyncOffsetMs`/`SaveAudioSyncOffsetMs` clamp −500..500. - `PreviewPane.xaml` — label **SYNC → "AUDIO SYNC"**, `Minimum="-500"`, tooltip explains both directions (calibrate with a clap: clap late → negative; early → positive), and **`IsEnabled="{Binding IsEditMode}"`** — the slider locks during live AND recording (gun safety, same property `IsRecording`/`StreamStatus` already raise PropertyChanged for). - `AudioSyncDelay` unchanged (still clamps negative→0 internally; header doc updated to point at the mixer for the advance side). **Regression test (the ONE integration test for this change):** `StartLive_NegativeOffset_AdvancesAudio_ByDroppingTheStreamHead` in `AudioPipelineTests.cs` — −40 ms advance (8-tick budget at 5ms interval), emits 6×0.9 fresh right after StartLive (≤ budget, so the head MUST be eaten) then a long 0.2 bed; asserts the wire max ≈ 0.2 (< 0.3). Fails loudly if −N no longer drops the head (0.9 leaks). **User directives (this session):** - Signed −500..+500 with positive=delay / negative=advance, relabel "AUDIO SYNC", default 0. - **Lock the sync control when live or recording** (done — `IsEditMode`). - **NOTE ONLY, no fix:** the live recording is completely different from the "recording results" shown in Chat view (recorded in `bugs.md` — do not rediscover as a surprise). - **Before 1.0:** write a detailed USER-DOC tutorial on the audio-sync feature (see TASKS.md note; add it to the gold-pass/1.0 checklist). `docs/` currently holds only the README image. ## Take verification so far (ring-backlog fix) Take `ty-20260914-1128-0000-2.mp4` (1920×1080@60fps, 356 frames / 5.93s): audio clap RMS peak 3.260s; video diff-frame 163 → 2.717s; **offset ≈ +0.54s** — collapsed from +2.11/2.22s; the remaining ~300ms was the baked-in `SyncOffsetMs=300` (now 0). Both streams `start_time=0` — not an `avoid_negative_ts` artifact. ## Open threads - **Verify signed sync on device:** re-run the clap take with a NEGATIVE offset to confirm the advance direction end-to-end (the regression test proves the mixer; a take proves the file). - **Audio-silence verification** — fixed code (`724af14`) confirmed; creator heard real audio. - **Webcam MJPG missing / ~10–14Hz**, layer SortOrder, truncation-with-dynamic-scenes — queued. - **Sync control tutorial in user docs — REQUIRED before 1.0** (creator directive). ## Landmines - testhost shares startup.log — filter by time. - `cmd.exe /c "taskkill /F /IM ytLive.exe"` (WSL double-slashes mangle) before rebuilds. - Build/tests: **Windows dotnet host** (`/mnt/c/Program Files/dotnet/dotnet.exe`). 0 warnings. - ffmpeg/ffprobe: `/mnt/c/Program Files/Krita (x64)/bin/` with Windows paths. - `MyMistakes.md` has the **audio/video sync measurement recipe** (claps + cross-correlation) — grep it before re-deriving. - sqlite3 lives at `/home/gramps/android-sdk/platform-tools/sqlite3` (WSL) for the DB at `/mnt/c/Users/gramp/AppData/Roaming/ytLlive/ytLlive.db`. ## Next step Open queue question: user-doc tutorial for the audio-sync feature (creator directive, REQUIRED before 1.0 — recorded in `TASKS/task-22-audio-sync-offset.md`). Pre-flight item to confirm next session: creator wanted to discuss whether other root-level md files (beyond `TASKS.md`) should be split one-topic-per-file like `TASKS/`.