ai.md 1,308 → 228 lines. No prose rewritten: every block was moved as-is so nothing could be lost in translation, then replaced by a pointer. New files: - docs/ai/environment.md — LAN/SSH + Gitea push recipe, Windows dotnet build/run, test-suite map, hermetic real-MainWindow test rule - docs/ai/product.md — brand/naming, AI transparency, GPL licensing prohibitions, free-forever monetization, auth/account assumptions, packaging & distribution - docs/ai/youtube-api.md — go-live ordering, the broadcast-binding fix, the private-only lock, health parsing, end-stream pre-flight, Test mode, chat insert shape - docs/ai/overlays.md — social bar, native alert box (animations, clip, ticker), chat box conventions - docs/ai/ui-surface.md — control surface, scene strip, preview rail, hotkeys, broadcast drawer, toasts, transitions, brand logo, launch geometry, theme/properties conventions ai.md keeps the response/derivation rules, the architecture table, the four existing invariant stubs, and one pointer per moved subsystem. docs/ai/index.md routes every file with a "read when" column. References falsified by the move were fixed in the same commit: AGENTS.md build note → environment.md → Run, and the "Related" footers in frame-pipeline.md / output.md / capture.md now point at the owning files. Docs-only unit: scope-check run, no build (no source touched).
9.3 KiB
Environment, build & tests
Lookup material: LAN hosts, the Windows build commands, where the DB/auth/log live, and the test suite map. Read when you are about to build, run, or add a test — not on every session.
Paths and secrets belong with the runbook in
../../HANDOFF.md→ "Secrets / paths" until the queue trim moves them here.
LAN infrastructure (192.168.50.x)
All LAN credentials live in CREDENTIALS.md (gitignored). Server IPs and roles:
| Host | IP | Role |
|---|---|---|
| corsair | .132 | Gaming/streaming primary — RTX 5070 Ti (this machine) |
| llamavault | .87 | ODROID-HC4, 12TB RAID5, media storage (NFS). SSH: ssh mshallop@192.168.50.87. DHCP-bound — it moved off .86 after the 2026-10 power outage, so confirm with a ping before trusting any path. SMB is reached by the llamavault name, pinned in the Windows hosts file, never by literal IP: \\llamavault\llamaVault\... |
| pivault | .158 | RPi5 8GB, 10.83TB RAID5 NAS |
| jarvis | .210 | AMD Ryzen 5 5600X, RX 6600 XT — AI inference, SearXNG, Docker registry, Immich |
| ultron | .108 | AMD Ryzen 7 7840HS — AI orchestrator, llama-server, Qdrant |
| gordito | .144 | Home Assistant, Alarmo, Zigbee |
| officerfriendly | .26 | Pi-hole DNS |
| octopi | .197 | OctoPrint (Prusa MK4) |
| BigBlinkyRouter | .1 | ASUS ROG GT-BE98 Pro, WiFi 7 |
Web infra (DO droplet): llamacasty.com + llamachile.tube at 143.244.176.131.
Pushing to the Gitea instance (llgit.llamachile.tube). The repo is gramps/LlamaCasty
(renamed from gramps/ytLlive; the old path 301-redirects). Push over HTTPS, not SSH:
origin = https://llgit.llamachile.tube/gramps/LlamaCasty.git, with the credential helper
/home/gramps/bin/gitea-credential (registered as
credential.https://llgit.llamachile.tube.helper) reading the account password from the
gitignored CREDENTIALS.md on demand — nothing stored in plaintext, nothing in a command line.
The Gitea account password IS the portal password in CREDENTIALS.md, and the same password
authenticates the REST API (curl --config …/api/v1/user) — that API is the fastest way to ask
the server questions (the repo's real name and clone URL, the account's SSH keys, a token).
⛔ The SSH endpoint on llgit:2214 is not a usable push path: the account's SSH keys ARE
registered (key id 6 "Corsair VSC" = this machine's ~/.ssh/id_ed25519) yet the server refuses
the publickey, and the port later refused connections outright. Do not spend a session on key
archaeology — ask the API, then push over HTTPS.
Run
First time on a fresh clone (or if ffmpeg/ was deleted) — this is REQUIRED, not optional:
./scripts/fetch-ffmpeg.sh # ~65 MB once; verifies SHA-256, extracts 9 files into ./ffmpeg
FFmpeg is bundled, never downloaded at runtime (Windows Store policy 10.2.2 — the old runtime
download 404'd on 2026-09-01 and would fail certification). The binaries are gitignored because
135 MB of DLLs in git would tax every commit on this 9p tree; the pinned URL + SHA-256 in the script
are the audit trail. ytLive.csproj's GuardBundledFfmpeg target fails the build with that
exact instruction if the folder is missing. FFMPEG_ZIP=<path> reuses a pre-downloaded archive.
# From WSL, ALWAYS use the Windows dotnet host — never Linux `dotnet` for this project:
"/mnt/c/Program Files/dotnet/dotnet.exe" build "C:\Users\gramp\Documents\Code\projects\ytLive\ytLive.csproj"
"/mnt/c/Program Files/dotnet/dotnet.exe" run -c Release # the launch command (creator, 2026-10-16): Release, not Debug — perf takes must read Release numbers
Run-mode convention (creator directive 2026-10-16): the creator launches with -c Release
by DEFAULT. Debug runs happen ONLY when the AI explicitly names the Debug command for a
specific reason — never implicitly, and never for reading performance numbers (Debug pixel
paths are several × slower; every take in the 141ms saga read Debug numbers where Release is
the real reading).
Pre-commit gate (write it down — I forgot once): ALWAYS run
./scripts/verify.sh "<file1>" "<file2>" ... (declared scope) before committing. It forces a
clean build (0 warnings), the full test suite (passes only the 2 known pre-existing [FAIL]s), and
the scope check in one shot. An incremental build can report 0 Warning(s) while skipping
recompiles — that's how CS8601 slipped past on 2026-08-29. Never claim 0 warnings from anything but
verify.sh's clean build.
EnableWindowsTargeting=true in ytLive.csproj lets a cold restore work from WSL, but a Linux
dotnet run/build re-downloads 100M+ of windowsdesktop.app.* packs into the Linux NuGet cache
(which lacks them) over the slow 9p /mnt/c bridge — twice, because the WPF _wpftmp generated
project triggers a second restore (203s observed). The Windows cache has the SQLite packages and the
packs resolve from C:\Program Files\dotnet\packs, so the Windows host never re-downloads.
Never use --no-restore right after an interrupted restore — the stale project.assets.json
produces misleading NETSDK1064 "package not found" errors. Running requires Windows anyway.
Tests
xUnit in ytLive.Tests (net8.0-windows10.0.19041.0, matches the app TFM). Run on Windows —
WSL can't run net8.0-windows tests:
dotnet.exe vstest "C:\...\ytLive.Tests\bin\Debug\net8.0-windows10.0.19041.0\ytLive.Tests.dll"
Good Dog Rule: ONE integration test per change (no feature branches pre-1.0 — work lands on
main, see AGENTS.md). Current: TokenStore DPAPI
roundtrip/corrupt/missing/clear, OAuth exchange/refresh/ClearSession, CameraManager refcount +
frame pump + failure handling (fakes for the WinRT seams), real-MainWindow round-clip
interaction test, LayoutStore delete roundtrip, LayoutStore pre-round-rect-dims roundtrip,
LayoutStore background roundtrip, LayoutStore HasBackground roundtrip + v5→v6 non-Live backfill,
ScreenCaptureManager refcount + shared-bitmap + coalescing
(fake IScreenCaptureSource + a real background-STA Dispatcher), BackgroundTests, SceneCatalogTests,
the TASK 25 dirty-layout heal (BackgroundHealIntegrationTests), WebcamSafeguardTests,
SceneCompositorTests (full-scene composite + vertical tier), StretchMathTests, FfmpegLocatorTests,
FfmpegEncoderTests, FramePumpTests, the TASK 8/11/12 audio chain (AudioPipelineTests + MasterLimiter),
the Socials fediverse-heal roundtrip, AboutHubTests, NotificationAreaIntegrationTests (TASK 24),
GlobalHotkeyTests + HotkeyConfigTests (TASK 20), WebcamMenuGateTests (TASK 26), ChatLayerGateTests
(TASK 27), BroadcastPullOutTests (TASK 29), DefaultRecordFolder fallback (TASK 30), WebView2ManagerTests
(TASK 17), RecordingFileTests + OnAirSignTests (TASK 18), SessionTeardownTests +
WebcamOutputKeyTests + GameMeterHonestyTests (2026-09-01 recording-verification fixes) — ZERO
known failures as of 2026-09-01. AudioPipelineTests 25/25 green in 26ms —
the "known failing" Mix_HonorsProviderGains… and the class's notorious STANDALONE HANG shared
one root cause: TASK 22's _delayedMix (nullable, never initialized) was dereferenced
(delayed.Length on null) every live-mix tick — a swallowed NRE starved the pipe the tests
read and flooded startup.log. Test was right, code drifted from the map's own contract
(loopbackGain = GameAudioVolume — now honored; the "unity because loopback scales with the
endpoint" assumption was disproven by the creator's 20%-volume meter observation). The former
RoundClip "known failure" (stale-test layers: namescoped FindName + VisualTreeHelper.HitTest
where UIElement.InputHitTest models input — see MyMistakes) was fixed the same day.
Per-class runs through the Windows dotnet.exe host execute the RealApp/MainWindow suites fine;
only the FULL-suite run still hangs (WASAPI teardown, pre-existing) — and the whole-suite
"247 total" era count is stale; trust per-class results.
Reward-event capture (monetization awareness, see the Monetization section) will add its integration
tests here when it ships: one real chat-poll payload containing all seven reward event types →
assert the persisted canonical RewardEvent rows (type + amount/currency/tier/memberLevel/participant
ids) round-trip into SQLite.
Real-MainWindow tests MUST be hermetic (DB pollution bug)
The integration test boots a real MainWindow → MainViewModel → real LayoutStore
(%APPDATA%\ytLlive\ytLlive.db). Shutdown() on close saves the layout (full rewrite:
DELETE all scenes/sources, re-insert), so any source a test adds would be persisted over the
user's real ones — this happened and wiped the real webcam source (DeviceId replaced by the
test's fake test-camera). Rule: a test that constructs MainWindow MUST first set
MainViewModel.LayoutPathOverride to a temp DB path and reset it (plus
SqliteConnection.ClearAllPools() + delete) in finally. The seam is
internal static string? LayoutPathOverride (line ~529 in MainViewModel.cs),
ytLive.csproj has InternalsVisibleTo("ytLive.Tests").
The layout DB is a full rewrite per save (delete all, re-insert from memory), so
save/load round trips are exact: an element removed in the UI (RemoveElement →
scene.Elements.Remove → OnElementsChanged → debounced ScheduleSave, plus Shutdown on
close) does not come back after reload (LayoutStorePersistenceTests guards this).