The creator approved the push and it failed on the old SSH remote. The causes: the repo was RENAMED gramps/ytLlive -> gramps/LlamaCasty (the old path 301-redirects), and llgit:2214 refuses the publickey of this machine's key even though that key IS registered on the account (id 6, "Corsair VSC"). The account password in CREDENTIALS.md is also the Gitea account password, so the HTTPS push went through immediately: 931ab46..6be062c main -> main. origin is now https://llgit.llamachile.tube/gramps/LlamaCasty.git with the credential helper /home/gramps/bin/gitea-credential, which reads the password from the gitignored CREDENTIALS.md on demand - plain `git push` works, no plaintext secret on disk, no secret in any command line. Records the recipe and the lesson in MyMistakes.md (ask the REST API before doing transport archaeology: one authenticated curl answered everything that ten minutes of ssh -v could not) and the push mechanics in ai.md + HANDOFF.md. Docs only - no code touched, no build/test gate applies.
205 lines
15 KiB
Markdown
205 lines
15 KiB
Markdown
# 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 = `6be062c` = HEAD — up to date, nothing
|
||
waiting.** 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 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-<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.
|