17 KiB
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 =3a77662= HEAD — up to date, nothing waiting. The vv feature (Units 1+2) was pushed at the feature close (2026-10-21 — the creator approved the Unit 1 push after testing it, and the completed feature is a milestone). Push only at sub-milestones/milestones, at the creator's word.- The push path changed:
originis nowhttps://llgit.llamachile.tube/gramps/LlamaCasty.git(HTTPS + the credential helper/home/gramps/bin/gitea-credential, which reads the password from the gitignoredCREDENTIALS.md; plaingit pushworks). The old SSH remote was broken twice over: the repo was renamedgramps/ytLlive→gramps/LlamaCasty, andllgit:2214refuses 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 inMyMistakes.md→ 2026-10-21.
- The push path changed:
- 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) andytLive.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). SeeMyMistakes.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 inMyMistakes.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.csApplyStreamQuality→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.xaml144–230) and compositor (Services/Compositor/SceneCompositor.cs389–397) both read the element's ownX/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:
WebcamSceneConfigtable; new columns viaMigrateWebcamConfigTable()as idempotentALTER TABLE … ADD COLUMNguarded onPRAGMA table_info+LayoutStore.Save.csINSERT andLayoutStore.Load.csSELECT. - Precedent for remembered geometry:
RectWidth/RectHeight+ToggleClipShape(Models/SceneElement.cs124–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+ClipShapeexactly 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 transientLandscape*fields while the vertical tier is live.BeginVerticalLayout/EndVerticalLayout/CommitLiveLayoutToVerticaldo the swap.Models/SceneElement.cs:X/Y/Width/Heightare nowvirtualso 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.csApplyStreamQualityis 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'sIsWebcamSelected(NOTSelectedElement.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 theClipShapeproperty → a locked camera cannot become Round by any writer (the toggle, the Shape button, the placement swap).SceneElement.ClipShapeandSceneElement.ToggleClipShape()arevirtualfor this. The lock is cleared before the 16:9 layout comes back, so the desktop shape is restored. - The circle is not OFFERED:
MainViewModel.CanToggleClipShapeis false for a Chat camera in the vertical tier → the Shape button in the webcam's properties disappears (not just disabled).MainViewModel.csraises it on the selection change;MainViewModel.Streaming.cson 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).
ResolveVerticalCameraLayoutnow 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 throughMoveWebcamInVerticalTier(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)
- ✅ 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. - ✅ 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.combehind a maintenance page (503) and their Fastly services forauth.gog.com/api.gog.com/remote-config.gog.comreturn500 "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 ofwww.gog.com/en/at 06:56 UTC is HTTP 200 with the full live storefront.status.gog.comis NXDOMAIN globally. Local exoneration chain: genuineGOG sp. z o.o → DigiCertcert (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 + thewebfetch-is-not-an- independent-egress trap:MyMistakes.md→ 2026-10-21 GOG Galaxy "offline". Galaxy 2.0 logs live inC:\ProgramData\GOG.com\Galaxy\logs\(NOT%LOCALAPPDATA%). ⚠️ that folder also holds a 2.7 GBcef.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_PersistsTheReorderto the list. - A pre-existing stash is still sitting there:
stash@{0}= "WIP on main:0464593Compositor: 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.mdpassword lines). - TASK 56 secret scan before
gramps/LlamaCastygoes public. - TASK 55 disk hygiene (
startup.loguncapped — 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_PersistsTheReorderis NOT "passes in isolation" any more. During this session it FAILED ALONE on a clean tree at58b9a7d(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-2suffix). - Conversation history:
~/.local/share/opencode/opencode.db, tablesession_message— creator turns aretype='user'with text indata.text; my turns aretype='assistant'with reasoning AND the rendered reply concatenated intocontent[0].text. The reply marker's verbatim string is recorded inMyMistakes.md. - LAN credentials:
CREDENTIALS.md(gitignored) — ⚠️ stale for HA: it says192.168.50.144, the real Home Assistant is192.168.50.145(homeassistant.local, HAOS 18.2, Core 2026.10.1, SSHgramps/einstein,/configroot-owned).gorditois.144. Fix this file when next editing it. - Google Cloud OAuth project number
198374416003, owned byllamachile.shop@gmail.com— verify athttps://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,396dnssec=3rows 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.conftoPollIntervalMinSec=16/PollIntervalMaxSec=64/ConnectionRetrySec=8/SaveIntervalSec=30, and/or add a DS3231 RTC (i2c-toolsalready installed). - LAN DNS routing vs creator intent: DHCP still hands out
.26+.1(clients query Pi-hole directly) whereas the creator wants everything →.1with BBR forwarding. Alsodnssec = trueon Pi-hole duplicates dnscrypt-proxy'srequire_dnssec = true. Both offered-for-veto, not done. - Router is the DHCP server (ASUS ROG GT-BE98 Pro
.1) — Pi-hole.26is DNS only, itsdhcp.leasesis 0 bytes.