Files
LlamaCasty/HANDOFF.md
T
gramps 3a77662a95 vv webcam Unit 2: the Chat scene in the vertical tier - rect only, tile or one whole 607x540 area
The creator's locked table (2026-10-17) gives the Chat scene its own rules in
the 9:16 tier: RECT ONLY - the circle option is not offered - and a size that
is either the normal small tile or ENLARGED to fill one whole 607x540 area
(top or bottom). Unit 1 shipped the per-scene placement; this unit is the Chat
scene's own variant of it.

The rule lives on the MODEL, not on the button:
- Models/SceneElement.cs: ClipShape and ToggleClipShape() are now virtual.
- Models/WebcamSceneConfig.cs: VerticalShapeLocked (set per scene by the VM)
  OVERRIDES the ClipShape property, so no writer can slip a circle in - the
  toggle, the Shape button and the placement swap all go through it. The lock
  is cleared BEFORE the 16:9 layout comes back, so the desktop shape returns.
- ViewModels/MainViewModel.Webcam.cs: CanToggleClipShape (the circle is NOT
  offered in the vertical Chat layout - the Shape button disappears, it is not
  merely greyed out), IsChatSceneCamera, ResizeWebcamInVerticalTier.
- Models/VerticalCameraLayout.cs: FillsArea (the "enlarged" state, derived -
  no new column) and SizeFor - the Chat camera's TWO sizes, the tile or the
  whole area, whichever is nearer the requested width, so the resize handle
  has two detents instead of any size in between.
- An enlarged camera FILLS the area its centre is in (top or bottom), so
  dragging it up/down swaps the areas.
- The Chat camera is auto-tiled like the Live one when the vertical tier is
  picked - otherwise the vertical chat stream has no camera at all.
- Controls/PreviewPane.xaml.cs: the resize routes through the VM; the drag
  assigns, lets the model clamp into the band, then snaps (so the enlarged
  camera can be dragged from the top area to the bottom one).

Test: ytLive.Tests/VerticalChatCameraLayoutTests.cs - the ONE Good Dog test for
the unit: the vertical tier is picked -> the Chat camera comes into the band as
a small RECT tile -> the circle option is not offered and cannot be set even on
the model -> dragged up and enlarged it fills the TOP area exactly -> back to
16:9 the desktop layout is restored exactly and the circle option is offered
again. 430/430 tests pass, 0 warnings.

Research (the creator's table is the established practice, not our invention):
- Streamlabs' vertical guide: "splitting the screen in half horizontally,
  placing the camera feed in the top half and the main content on the bottom
  (or vice versa) may be the bet", and a just-chatting vertical wants the
  camera "to take up most of the screen"
  (streamlabs.com/content-hub/post/overlays-vertical-and-horizontal-streaming)
- Bloopbot's vertical overlay docs describe the same per-layer model we use at
  scene granularity: "move or resize a layer on Vertical and it stops being
  Auto-placed. It keeps the place you gave it, and the horizontal layout is not
  touched" (bloopbot.com/docs/vertical-overlays-twitch-dual-format) - our
  swap-at-tier-change, stored per scene.
- Landmine recorded: "about the bottom 560 px of the vertical video can be
  covered by Twitch's chat and player controls on phones" (Bloopbot) - the Chat
  camera's enlarged BOTTOM area may be half-hidden by the platform chrome;
  TASK 53's safe-zone work decides that.

Memory in the same commit: ai.md (the Chat rules, the shape lock, the tests),
TASKS.md + TASKS/task-57-vertical-camera-layout.md (TASK 57 complete), HANDOFF.md,
MyMistakes.md (3 recipes: lock the model property not the UI button; two detents
for a discrete size set; the edit tool cannot match an oldString with an em
dash), Models/ViewModels/Controls index rows.
2026-10-21 19:23:29 -07:00

17 KiB
Raw Blame History

HANDOFF — 2026-10-21 (vv webcam Units 1+2 shipped; the previous HA-lights session's state below)

At a glance

  • Branch main, tracking origin/main. origin/main = ccc1db5 = the last pushed commit; the vv Unit 2 work is committed on top of it. The vv Unit 1 commit was pushed on the creator's approval (2026-10-21, after the creator tested the placement). Push only at sub-milestones/milestones, at the creator's word.
    • The push path changed: origin is now https://llgit.llamachile.tube/gramps/LlamaCasty.git (HTTPS + the credential helper /home/gramps/bin/gitea-credential, which reads the password from the gitignored CREDENTIALS.md; plain git push works). The old SSH remote was broken twice over: the repo was renamed gramps/ytLlive → gramps/LlamaCasty, and llgit:2214 refuses the publickey of this machine's key even though it IS registered (account key id 6 "Corsair VSC"). Ask the Gitea REST API, don't do key archaeology — recipe in MyMistakes.md → 2026-10-21.
  • This session shipped vv webcam Units 1+2 (the vertical 9:16 tier keeps its OWN camera placement per scene; 16:9 restores the desktop layout exactly; the Chat scene in vv is rect-only and can be enlarged to fill a whole 607×540 area). Code + memory in ONE commit each; the ONE Good Dog tests are ytLive.Tests/VerticalWebcamLayoutTests.cs (Unit 1) and ytLive.Tests/VerticalChatCameraLayoutTests.cs (Unit 2). Only the creator's hands-on check is left — 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) — Units 1+2 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.

What shipped in Unit 2 (the Chat scene, 2026-10-21) — do not re-hunt

  • Rect only, and the lock is on the MODEL, not the UI. WebcamSceneConfig.VerticalShapeLocked (set per scene by the VM) overrides the ClipShape property → a locked camera cannot become Round by any writer (the toggle, the Shape button, the placement swap). SceneElement.ClipShape and SceneElement.ToggleClipShape() are virtual for this. The lock is cleared before the 16:9 layout comes back, so the desktop shape is restored.
  • The circle is not OFFERED: MainViewModel.CanToggleClipShape is false for a Chat camera in the vertical tier → the Shape button in the webcam's properties disappears (not just disabled). MainViewModel.cs raises it on the selection change; MainViewModel.Streaming.cs on the tier change.
  • Two sizes, snapped by the nearer one: VerticalCameraLayout.SizeFor(band, requestedWidth) → the small tile (270×152) or one whole area (607.5×540). The resize handle therefore has two detents instead of any size in between.
  • An enlarged camera fills the area its centre is in — top or bottom (VerticalCameraLayout.AreaFor).
  • The Chat camera is auto-tiled like the Live one when the vertical tier is picked (otherwise the vertical chat stream has no camera at all). ResolveVerticalCameraLayout now auto-tiles Live and Chat; Starting/BRB/Ending keep their 16:9 placement.
  • The preview's resize routes through MainViewModel.ResizeWebcamInVerticalTier (the Chat camera's discrete size + the snap); the drag routes through MoveWebcamInVerticalTier (assign → clamp → snap, so the enlarged camera can be dragged from the top area to the bottom one).
  • Test: ytLive.Tests/VerticalChatCameraLayoutTests.cs — PASSES (in Chat under the vertical tier, enlarging fills the full top area and the circle option is not offered).

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) — shipped 2026-10-21. Rect enforced with the circle option unavailable, plus enlarge-to-one-area (607×540) filling the top or the bottom area by where the camera is. Test: ytLive.Tests/VerticalChatCameraLayoutTests.cs — in Chat under the vertical tier, enlarge sets the full top area and the circle option is not offered. PASSES.

Next step

  • Creator check (needs the creator's hands) — the last piece of the vv feature: switch the resolution dropdown to "Vertical 1080p60", confirm the Live camera tile lands inside the band and the Shape button toggles circle/rect there, open the Chat scene and confirm the Shape button is gone (rect only) and that dragging the tile up and enlarging it fills a whole 607×540 area, then switch back and confirm the desktop layout is untouched. The unit tests cover 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/430 passed, 0 warnings (2026-10-21, this session — the vv Unit 2 gate run). The one failure was LayerReorderPersistenceTests.RealMouseDrag_OnTheLayerList_PersistsTheReorder, which passes in isolation (re-run: 3/3) — the known real-mouse flake, not a regression.
  • 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-<date>-<time>-<ms>-<seq>.mp4; a duplicate name gets a -2 suffix).
  • Conversation history: ~/.local/share/opencode/opencode.db, table session_message — creator turns are type='user' with text in data.text; my turns are type='assistant' with reasoning AND the rendered reply concatenated into content[0].text. The reply marker's verbatim string is recorded in MyMistakes.md.
  • LAN credentials: CREDENTIALS.md (gitignored) — ⚠️ stale for HA: it says 192.168.50.144, the real Home Assistant is 192.168.50.145 (homeassistant.local, HAOS 18.2, Core 2026.10.1, SSH gramps/einstein, /config root-owned). gordito is .144. Fix this file when next editing it.
  • Google Cloud OAuth project number 198374416003, owned by llamachile.shop@gmail.com — verify at https://console.cloud.google.com/apis/credentials?project=198374416003.

Open (operational) — officerFriendly / PS5 "DNS errors" (2026-10-17, off-repo)

  • Root cause: a ~43 h-wrong system clock on officerFriendly × dnssec = true. Pi 400, no RTC hardware at all, healthy oscillator (−7.39 ppm). Journal shows +54 min, +7 h, +42.9 h steps. pihole-FTL.db: 1,688,396 dnssec=3 rows on 10-11, 839,276 on 10-12 vs ≤23 other days, hitting PSN endpoints = the PS5's "DNS errors". KSK-rollover theory retracted.
  • Pending creator decision (do not apply unilaterally): tighten /etc/systemd/timesyncd.conf.d/lan.conf to PollIntervalMinSec=16 / PollIntervalMaxSec=64 / ConnectionRetrySec=8 / SaveIntervalSec=30, and/or add a DS3231 RTC (i2c-tools already installed).
  • LAN DNS routing vs creator intent: DHCP still hands out .26 + .1 (clients query Pi-hole directly) whereas the creator wants everything → .1 with BBR forwarding. Also dnssec = true on Pi-hole duplicates dnscrypt-proxy's require_dnssec = true. Both offered-for-veto, not done.
  • Router is the DHCP server (ASUS ROG GT-BE98 Pro .1) — Pi-hole .26 is DNS only, its dhcp.leases is 0 bytes.