Files
ytLlive/HANDOFF.md
T

93 lines
7.1 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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 21 — real logo + creator-hub About: SHIPPED** (this session). The About overlay is now the
**creator hub**: the real logo ("llama fortnite superman logo" copied from `\\llamavault``Assets/llama-logo.png`,
the creator's own art — no third-party license), plus hub links — **llama chile shop on YouTube**
(`MainViewModel.ChannelUrl`, public now), **Mastodon** (`https://mastodon.llamachile.tube/@gramps`),
**Buy me a coffee** (`https://buymeacoffee.com/llamachiley` — LIVE), and **Unlock Premium** (greyed
"coming soon"; the itch.io product URL is a **tabled seam**, `MainViewModel.PremiumUrl` — the itch.io
account was just created and needs a setup session later). **Licenses & legal** flips the About overlay
to an in-app scrolling panel loading the shipped `THIRD-PARTY-NOTICES.txt` (`ShowLicenses()` reads from
the executable dir on first open, graceful "not found" fallback — never the OS viewer); "← Back to
About" (`BackToAbout_Click`) returns. Integration test (ONE): `AboutHubTests` drives the real window,
asserting the hub opens, the link URLs are real, licensing loads the shipped notices text, and back
returns to the hub. **174 tests passing, 0 warnings.**
- **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; **174/174 tests pass** (173 + the new AboutHubTests).
- **Uncommitted (this session's TASK 21 batch):** `MainWindow.xaml`, `ViewModels/MainViewModel.cs`,
`MainWindow.xaml.cs`, `Assets/llama-logo.png` (replaced with the real logo), `ytLive.Tests/AboutHubTests.cs`
(new), `TASKS.md`, `HANDOFF.md`. ai.md + ViewModels/index.md updates pending — **commit + push
before continuing.**
- **Next step:** commit + push TASK 21, 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 22 (voice filters)**,
and **setting up the itch.io product page** (the creator just made the account; the premium-unlock
URL then lands in `MainViewModel.PremiumUrl` and the About hub's "Unlock Premium" button lights up).
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`.