# HANDOFF — session state > Current operational state, read right after `TASKS.md`. Trust this file as the > truth of what is in flight — do not re-derive from git/fs unless it points at > a problem. Conventions: [`schema.md`](schema.md). Rewrite this file at session > end, compaction, or any interruption. ## Session state (last updated: 2026-08-14) - **Branch:** `main`, in sync with `origin/main`. - **TASK 8 — meter scaling amplification:** SHIPPED + pushed (`0b71b03`). `AudioLevelMeter.ToDisplay` adds **+10 dB input amplification** before the −60..0 dBFS log map: speech peaks (~0.2 RMS) read ~0.93 (red) and normal speech (~0.05) ~0.73 (yellow) at maxed volume; ≤0.001 linear still reads 0 (never idles on background noise). One knob shared by the mic + game bars; `× MicVolume` untouched. - **TASK 4 ship step 7 — one-click go live + private-only enforcement: SHIPPED** (this session; the final open TASK 4 requirement). Double-enforced privacy: 1. **Dialog locked to Private** — `GoLiveViewModel.Visibility` is a get-only `"Private"`, the visibility ComboBox is gone (replaced by a "Streams always start Private" note); settings' dead "Default Visibility" dropdown + `MainViewModel.Visibilities`/`DefaultStreamVisibility` removed. 2. **Service forces private** — `YouTubeStreamService.CreateBroadcast` always sends `privacyStatus="private"`; ctor gained `HttpClient? http = null` seam for tests. 3. **Wired into go-live** — `BeginGoLive` fires `CreateBroadcastAsync` (stores `_currentBroadcastId` for TASK 5's bind/transition; failure → `StreamStatus.Error` via AppLog, never a crash) + `_framePump.StartAsync()`; `StopStream` clears `_currentBroadcastId`. 4. **PRIVATE badge** — dark-red bordered, next to REC in the top bar, `Visibility="{Binding IsLivePrivate}"` (MainWindow.xaml ~110). 5. **Integration test** — `ytLive.Tests/YouTubeStreamServiceTests.cs`: the broadcast-insert request must carry `"privacyStatus":"private"` (RecordingHandler seam) + a no-session → null guard test. - **Verified:** build 0 warnings / 0 errors; **173/173 tests pass** (171 + the 2 stream-service tests). - **Uncommitted (this session's ship step 7 batch):** `GoLiveWindow.xaml`, `ViewModels/GoLiveViewModel.cs`, `ViewModels/MainViewModel.cs`, `Services/YouTubeStreamService.cs`, `MainWindow.xaml`, `ytLive.Tests/YouTubeStreamServiceTests.cs` (new), `TASKS.md`, `ai.md`, `ViewModels/index.md`, `Services/index.md`, `HANDOFF.md`. **Commit + push before continuing.** - **Next step:** commit + push ship step 7, then **TASK 5 — reusable `variable` stream** (create once per channel, cache ingestion URL, bind to broadcast, feed `_rtmpUrlProvider` so the pump actually pushes — `_currentBroadcastId` is already stashed for it). Also queued: task 21 (logo + About hub), task 22 (voice filters). Nothing else queued — do not expand the task queue on your own. Optional, not queued: rewriting the healed entry's `ProfileUrl` to `https://mastodon.llamachile.tube/@gramps` (user must say the word). - **Landmines:** - Never add another test that constructs `new App()` — use `RealAppHost.Run(...)` (shared STA host for the one WPF App per AppDomain; round-clip + source-naming tests). - Never set a local `Canvas.SetTop` on the social bar — a local value permanently overrides `{Binding SocialBarTop}` (the `ClearValue` lesson from 5.5). - `AudioMixer` meter `Push` is unconditional **by design now**: `OnMicSample`/ `OnLoopbackSample` compute the level first, then raise the event — a `?.Invoke(meter.Push(...))` short-circuit skipped the meter update when nothing was subscribed (found by `RestartMic_ResetsLevel`, fixed). - `MicConnected` comes from the source `Started` event, raised right after `StartRecording()` succeeds — tests must `MarkStarted()` the fake source before asserting connection state. - The mic dot is red until a resource connects (see contract below) — green only after `Started`, yellow on `Failed`. - Zero mic devices at startup = red dot AND the mixer is never started, so loopback + the game bar can't run either (no capture at all) — acceptable. - The game detector is polled on the UI thread via a 250ms `DispatcherTimer`; `OnGameAudioPollTick` wraps `Poll()` in try/catch + `AppLog`. - The pump reads the active scene on a background thread while the UI can still edit it — a concurrent-mutation exception is contained (logged + `Failed` + the pump stops), not a crash. - `StopAsync` must stop the encoder (closes stdin) **before** awaiting the pump loop — closing stdin unblocks a write stuck on pipe backpressure; the reverse order deadlocks. - Tests never instantiate `MainViewModel` directly except via a real `MainWindow` on the `RealAppHost` STA thread (round-clip + naming), which never go live. - Sandbox can't reach outbound HTTPS — `HttpSocialValidator` stub-handler tests only, never the real instance. - **Mic status contract (creator's rule, verified — do NOT "fix"):** the status dot is **red until a mic resource is actually connected**. `MicStatus` starts `NotConnected` (red); it goes green ONLY when the mixer's `MicConnected` fires, which comes strictly from the mic source's `Started` event raised after `StartRecording()` succeeds. Zero devices at startup → stays red and the mixer is never started; capture failure → yellow. - **Secret/DB/port facts live:** OAuth client id/secret in `Helpers/OAuthCredentials.cs`; OAuth session token in `Helpers/TokenStore.cs` (DPAPI → `%APPDATA%\ytLlive\ytLlive.auth`); layout DB `%APPDATA%\ytLlive\ytLlive.db` (schema v8; `SocialEntry.Software` column is a column-presence migration like the others, no version bump); OAuth callback `http://localhost:8765/oauth2/callback`; crash log `%APPDATA%\ytLlive\startup.log`.