TASK 4 ship step 7: one-click go live + private-only enforcement (req 8) — the Go Live dialog is locked to Private (GoLiveViewModel.Visibility is a get-only string, no dropdown; settings' dead Default Visibility dropdown + Visibilities/DefaultStreamVisibility removed); YouTubeStreamService.CreateBroadcast always sends privacyStatus='private' and gained an injectable HttpClient? http = null seam; BeginGoLive fires CreateBroadcastAsync which stashes _currentBroadcastId for TASK 5's bind/transition (failure → StreamStatus.Error via AppLog, never a crash) alongside the frame pump, and StopStream clears it; the REC sign shows a dark-red PRIVATE badge next to REC whenever the live stream is private (IsLivePrivate) — the broadcast-insert integration test asserts the request body carries privacyStatus private (RecordingHandler seam) + a no-session guard test — 173 tests passing, 0 warnings

This commit is contained in:
2026-08-14 09:20:03 -07:00
parent 0b71b030e4
commit 801219ec46
11 changed files with 167 additions and 83 deletions
+33 -47
View File
@@ -5,49 +5,42 @@
> a problem. Conventions: [`schema.md`](schema.md). Rewrite this file at session
> end, compaction, or any interruption.
## Session state (last updated: 2026-08-13)
## Session state (last updated: 2026-08-14)
- **Branch:** `main`, in sync with `origin/main`.
- **TASK 8 (new, after TASK 7 shipped):** `AudioLevelMeter.ToDisplay` gained **+10 dB input
amplification** so the meters use the full bar — speech peaks (~0.2 RMS) now read ~0.93 (red)
and normal speech (~0.05) ~0.73 (yellow) at maxed volume; ≤0.001 linear still reads 0 (idle
never shows noise). One knob shared by the mic bar and game bar; `× MicVolume` untouched.
Tests updated + new `ToDisplay_Pushes_Speech_Peaks_Into_Red_At_Maxed_Volume`. **Uncommitted**
(pending user review): `Services/Audio/AudioLevelMeter.cs`, `ytLive.Tests/AudioMixerTests.cs`,
`ai.md`, `Services/index.md`, `ViewModels/index.md`, `TASKS.md`, `HANDOFF.md`. Meter tests:
7/7 passing, build 0 warnings.
- **This session (TASK 7 — UI polish batch, gramps's 6-point review):**
1. **Scenes list cleaned:** the per-row edit/trash/visibility icons and the inline
rename TextBox are gone. Scenes are pure selection rows; `IsHidden` stays
persisted and still dims a hidden row to 45%. Removed dead surface:
`EditSceneCommand`/`RemoveSceneCommand`/`ToggleSceneVisibilityCommand` +
`BeginEditScene`/`ToggleSceneVisibility`/`RemoveScene` handlers + `Scene.IsEditing`.
2. **Sources list upgraded:** each row now has edit + visibility eye + trash. New
`EditElementCommand` (`SceneElement.IsEditing`inline rename TextBox, Enter/Esc/
lost-focus commits) and `ToggleElementVisibilityCommand` (flips `SceneElement.IsVisible`);
the eye style now binds `IsVisible` and hidden rows dim to 45%.
3. **Duplicate naming:** shared `NextSourceName(scene, baseName)``Image`, `Image2`,
`Image3`… (no space), next free number derived from actual names so deletions never
collide. Used by both `AddSource` and `AddReusedImage`.
4. **Social bar:** `MaxWidth=200` + `CharacterEllipsis` removed from BOTH
`SocialBarRenderer.cs` and the preview DataTemplate — full validated handle renders.
5. **Panels:** left 220 / right 300 fixed widths are deliberate — panels never re-layout
on resize; the preview absorbs it. No change.
6. **Focus-loss capture lag:** recorded in `ai.md` as a known OS limit (DWM/WGC
throttling when unfocused + GPU readback contention + the `_framePending` /
`DispatcherPriority.Render` gates). NOT an in-app throttle; deferred by user decision.
- **Test infra change:** the two real-WPF-App tests (round-clip + new source-naming) now
share `RealAppHost` — a dedicated STA thread owning the single `App` — via the `RealApp`
serial collection. WPF allows exactly one `Application` per AppDomain; never add a test
that calls `new App()` directly again — marshal onto `RealAppHost` instead.
- **Uncommitted:** `MainWindow.xaml` + `MainWindow.xaml.cs` (rows), `ViewModels/MainViewModel.cs`
(commands + naming), `Models/SceneElement.cs` (+`IsEditing`), `Models/Scene.cs` (`IsEditing`),
`Services/Compositor/SocialBarRenderer.cs` (no truncation), `ytLive.Tests/SourceNamingTests.cs`
(new), `ytLive.Tests/RealAppCollection.cs` (new), `ytLive.Tests/RoundClipInteractionTests.cs`
(now uses the shared host), `TASKS.md` (TASK 7 ✅), `ai.md`, `ViewModels/index.md`, `HANDOFF`.
- **Verified:** build 0 warnings / 0 errors; **170/170 tests pass** (169 + the naming test).
- **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(...)`.
- 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`/
@@ -78,13 +71,6 @@
(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.
- **Next step:** commit + push this TASK 7 batch (one commit). Then the remaining TASK 4
requirement is **ship step 7 — one-click go live + private-only enforcement** (honor the
dialog's chosen visibility / enforce `privacyStatus = "private"`; also fixes the REC sign's
private state — see round-3 note). 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).
- **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`);