# HANDOFF — 2026-10-21 (vv webcam Unit 1 shipped; the previous HA-lights session's state below) ## At a glance - **Branch** main, tracking `origin/main`. **origin/main = `931ab46`; HEAD = `58b9a7d` + this session's commit** — **9 ahead, 0 behind** (nothing pushed; push only at sub-milestones/milestones, at the creator's word). This file cannot record its own commit's hash, so the tip is named by subject. - Local-only commits: `3cb29ea`, `a3a4681`, `84cbec2`, `cdf82f6`, `2a15be2`, `d1e73a3`, `fb851f0`, `b1d77ee`, `58b9a7d`, plus this session's. Always check `git log origin/main..HEAD` — never trust a handoff's own "0 ahead" claim. - **This session shipped vv webcam Unit 1** (the vertical 9:16 tier keeps its OWN camera placement per scene; 16:9 restores the desktop layout exactly). Code + memory in ONE commit; the ONE Good Dog test is `ytLive.Tests/VerticalWebcamLayoutTests.cs`. **Unit 2 (vv · Chat) is next** — see the vv section. - **The previous session (HA lights) produced NO ytLive code** — it was spent on the creator's Home Assistant light alert system (off-repo, `192.168.50.145`). See `MyMistakes.md` → 2026-10-21: the plan was one tweak to a verified-working script; the script was rewritten twice, the alert path broke, and the day was burned. **The HA work landed OK in the end** (recipes + measurements now in `MyMistakes.md`). Records below so the next session does not re-hunt. ## HA light alert system — current, verified state (2026-10-21) Five Tapo bulbs: `light.br1`, `light.br2`, `light.officeone`, `light.officetwo`, `light.death_star_light`. Purpose: the creator is **deaf** — lights are the alert channel; the phone (`notify.mobile_app_mis_lx9`) is secondary. **The iPhone is gone — never notify it.** | State | br1 / officeone | br2 / officetwo | Death Star | |---|---|---|---| | Idle | off | off | off | | Pump running | off | **off** | **blue only** (creator directive) | | Leak | warm-white 100% | purple | purple | | Alarm | warm-white 100% | red | red | | Doorbell | green, 8s | green, 8s | green, 8s | | Doorbell during an alert | — | suppressed — | — | | Clear | off | off | off | **Architecture:** `script.alerts_arm` / `alerts_clear` write only `input_text.light_alert` (`none|leak|alarm|motion`, restart-surviving). **`script.alerts_render_lights` is the single writer of the bulbs** — flat top-level `if`/`else` blocks (nested `if` and `choose:` both silently lit nothing; do not "improve" the shape). Priority: an alert always beats pump status. **⛔ THE ENTITY-ID TRAP (cost this session hours):** the YAML key is `alerts_render` but the entity id is **`script.alerts_render_lights`**. `script.turn_on` on a non-existent entity returns **HTTP 200 and does nothing** — no error, no log. All callers now use the `_lights` id. Check this first on any "script does nothing" symptom. **Pump status** is a *status*, not an alert (auto-clearing), and is keyed off `sensor.water_pump_current_consumption` — **never** `switch.water_pump_2`, which was observed latched `on` for hours at 0.0A draw and painted the bedroom blue at 5am. **HA files touched** (`/config`, root-owned → writes need `sudo`): `scripts.yaml`, `automations.yaml`, `configuration.yaml` (added `input_text.light_alert` / `_prev`). Backups: `scripts.yaml.bak-alertbuild`, `automations.yaml.bak-alertbuild`, `automations.yaml.bak-notify-20261020`, `configuration.yaml.bak-tplinkfix`. Zombie entities deleted from the registry: `script.leak_strobe`, `script.doorbell_flash` (old one), `automation.leak_strobe_notify_clear_restore`, `script.alerts_render_lights_2`. **Leak sensor is healthy** (`binary_sensor.pump_leak_moisture` = off, battery 100, signal 3) — an earlier "unavailable" claim was a bad read during a reload; `alert_leak_on` now also triggers on the latched `sensor.pump_leak_last_water_leak_alert`. ## vv webcam feature — locked rules (creator, 2026-10-17) — **Unit 1 shipped 2026-10-21** | mode | shape | size | position | |---|---|---|---| | 16:9 | as today | as today | as today | | **vv · Live** | **circle OR rect, creator's choice** | small tile | one of six spots: top/bottom × L/C/R | | **vv · Chat** | **rect only — no circle option** | normal, or enlarged to fill one area (607×540) | enlarged → top or bottom | | Starting / BRB / Ending | unchanged | unchanged | unchanged | - **Placement is set by tap/click-drag with snap, exactly like the socials today**, plus manual entry in properties. **vv keeps its own camera position, size and shape per scene** — switching back to 16:9 restores the desktop layout exactly. - **No explicit borderless/no-background mode is needed** (round + border/bg options already allow it). - Creator's "square" means the rectangular view (the camera's own aspect), not a literal 1:1 box. ### What the code already gives us (do not re-hunt) - **vv = the active tier's centred band crop.** 1080p60-vertical: master band **x 656.25–1263.75 (607.5 wide), full height**, upscaled to 1080×1920 (`MainViewModel.Streaming.cs` `ApplyStreamQuality` → `OutputRect*`). The band splits into **two 607×540 areas** (top/bottom). - **The Live webcam sits at x≈1425 → entirely outside the band** — in vv it is invisible. That is the whole motivation. - **Geometry only — no render-path change.** Preview (`Controls/PreviewPane.xaml` 144–230) and compositor (`Services/Compositor/SceneCompositor.cs` 389–397) both read the element's own `X/Y/W/H` + `ClipShape`. - **Round / border / background are independent:** `ClipShape.Round`; `HasBorder` = width > 0 AND valid 6-hex colour; `BackgroundEffect` = None/Remove/Blur. - **Per-scene camera size policy exists**: `MaxWebcamWidthFor/HeightFor` (Chat 1358×764, others 960×540) + `ClampWebcamToBounds`. **A 607×540 tile sits inside both → no clamp change.** - **Storage:** `WebcamSceneConfig` table; new columns via `MigrateWebcamConfigTable()` as idempotent `ALTER TABLE … ADD COLUMN` guarded on `PRAGMA table_info` + `LayoutStore.Save.cs` INSERT and `LayoutStore.Load.cs` SELECT. - **Precedent for remembered geometry:** `RectWidth/RectHeight` + `ToggleClipShape` (`Models/SceneElement.cs` 124–152). ### What shipped in Unit 1 (2026-10-21) — do not re-hunt - **Geometry only — NO render-path change.** The tier switch swaps the element's LIVE geometry; the preview and the compositor read `X/Y/W/H` + `ClipShape` exactly as before. - `Models/VerticalCameraLayout.cs` (new): the pure math — band x 656.25, w 607.5, h 1080; two 607×540 areas; six spots; default tile **270×152 master px** (= 480×270 in the 1080×1920 output), **bottom-right** spot; inset 32 master px. - `Models/WebcamSceneConfig.cs`: the vertical placement is persisted in **seven additive columns** (`VerticalX/Y/Width/Height/VerticalClipShape/VerticalRectWidth/VerticalRectHeight`, NULL = "this scene has no vertical placement yet"); the 16:9 layout stays in the old columns and is **parked** in the transient `Landscape*` fields while the vertical tier is live. `BeginVerticalLayout` / `EndVerticalLayout` / `CommitLiveLayoutToVertical` do the swap. - `Models/SceneElement.cs`: `X/Y/Width/Height` are now **`virtual`** so the webcam clamps EVERY write (drag, resize, typed fields) into the band — outside the band = invisible in vv. - `ViewModels/MainViewModel.Webcam.cs`: `IsVerticalTier`/`VerticalBand` + `ResolveVerticalCameraLayouts` (the tier swap; the **Live scene gets its tile automatically** on the first vertical switch) + `MoveWebcamInVerticalTier`/`SnapWebcamInVerticalTier` (the six-spot snap, by the tile's CENTRE). `MainViewModel.Streaming.cs` `ApplyStreamQuality` is the tier-change hook. - `Controls/LeftPanel.xaml`: the webcam properties gained a **Geometry** section (X/Y/W/H) bound to the live placement, gated on the VM's `IsWebcamSelected` (NOT `SelectedElement.IsWebcam` — the TASK 51 test finds that panel by binding text and would match twice). - `Controls/PreviewPane.xaml.cs`: dragging/resizing a webcam under the vertical tier routes through the VM's snap instead of free-floating over the master. - Test seam: the drag lives in a **VM method**, so the test drives it without synthesising mouse input. ### Units (each: ONE Good Dog integration test, own commit, memory in the same commit) 1. ✅ **vv camera layout (Unit 1)** — shipped 2026-10-21. Stored separately per scene, resolved when the tier changes. Live: small tile (circle or rect) snapped to one of six spots; drag like the socials + properties entry. *Test:* `ytLive.Tests/VerticalWebcamLayoutTests.cs` — with the vertical tier active, dragging the Live camera to top-right places it top-right inside the band; switching back to 16:9 restores the original layout exactly; both placements survive a save + reload. **PASSES.** 2. ☐ **vv · Chat (Unit 2)** — rect enforced with the circle option unavailable, plus enlarge-to-one-area (607×540) snapping top or bottom. *Test:* in Chat under the vertical tier, enlarge sets the full top area and the circle option is not offered. **Not started.** ## Next step - **vv Unit 2 (the Chat scene)** — the only queued piece of this feature. Requirements are locked in `TASKS/task-57-vertical-camera-layout.md`: rect ONLY in the vertical tier (no circle option offered), size either normal or enlarged to fill one 607×540 area, same six-spot snap; and the Chat scene's 16:9 exemption from the half-frame cap (`MaxWebcamWidthFor/HeightFor`) must not leak into the vertical tier. - **Creator check (needs the creator's hands):** switch the resolution dropdown to "Vertical 1080p60", confirm the Live camera tile lands inside the band and the Shape button toggles circle/rect there, then switch back and confirm the desktop layout is untouched. The unit test covers the geometry, not the eyeball. - Nothing else is queued for llamacasty — the creator queues work. ## Open (operational) - **GOG Galaxy launcher shows "offline" (2026-10-21) — GOG's own overnight outage, NOT Windows.** GOG put `www.gog.com` behind a maintenance page (503) and their Fastly services for `auth.gog.com` / `api.gog.com` / `remote-config.gog.com` return `500 "Fastly error: unknown domain"`; the page itself says *"Galaxy Launcher will also be down."* **Window: 00:00–08:11 local on 2026-10-21** — the Wayback snapshot of `www.gog.com/en/` at 06:56 UTC is HTTP 200 with the full live storefront. `status.gog.com` is NXDOMAIN globally. Local exoneration chain: genuine `GOG sp. z o.o → DigiCert` cert (no MITM), local DNS == Google's authoritative answers, real Fastly POP headers, and GOG's own non-Fastly servers still answering. **Nothing to fix locally** — wait for GOG. Galaxy's logs (incl. the rotated ones) show no API-status failures between Oct 10 and Oct 20. Diagnosis recipe + the `webfetch`-is-not-an- independent-egress trap: `MyMistakes.md` → 2026-10-21 GOG Galaxy "offline". Galaxy 2.0 logs live in `C:\ProgramData\GOG.com\Galaxy\logs\` (NOT `%LOCALAPPDATA%`). ⚠️ that folder also holds a **2.7 GB `cef.log`** (since Jul 13) — disk hygiene, see TASK 55. - **verify.sh's known-failure whitelist is too loose** (confirmed twice 2026-10-16): require EVERY failure line to be whitelisted, and add `MasterLimiter_CapsTheLiveMix_OnThePipe` + `RealMouseDrag_OnTheLayerList_PersistsTheReorder` to the list. - **A pre-existing stash is still sitting there:** `stash@{0}` = "WIP on main: 0464593 Compositor: fast-path opaque 1:1 when source fills canvas" (NOT this session's; do not drop blindly). - The judder reproduction take in the FAILING geometry (vertical 1080p60 first) is still outstanding — no fix until a take names the stage. - ⛔ Sudo password rotation pending (user; then update both `CREDENTIALS.md` password lines). - TASK 56 secret scan before `gramps/LlamaCasty` goes public. - TASK 55 disk hygiene (`startup.log` uncapped — 23MB and growing every 5s). ## Tests - Last full run: **429/429 passed, 0 warnings** (2026-10-21, this session — the vv Unit 1 gate run). - Known flaky-by-environment (all pass in isolation): `AudioPipelineTests.Mix_HonorsProviderGains…`, `MasterLimiter_CapsTheLiveMix_OnThePipe`, `LayerReorderPersistenceTests.RealMouseDrag…`, `Round_Clip_Corner…`. - ⚠️ **`LayerReorderPersistenceTests.RealMouseDrag_OnTheLayerList_PersistsTheReorder` is NOT "passes in isolation" any more.** During this session it FAILED ALONE on a **clean tree** at `58b9a7d` (the vv work stashed) with the same "Expected: ImgC / Actual: ImgA" — i.e. it is an environment/real-mouse flake, **not** a regression from the vv work — and it PASSES in the full-suite run (429/429). Treat it as environment-dependent, and don't chase it as a regression without stashing first. ## Secrets / paths (do not re-hunt) - Layout DB: `%APPDATA%\ytLlive\ytLlive.db` (SQLite, full-rewrite per save). Auth: `%APPDATA%\ytLlive\ytLlive.auth` (DPAPI). Startup log: `%APPDATA%\ytLlive\startup.log` (23MB). - Recordings land in `C:\Users\gramp\Downloads\recordings\` (`ty--