YouTube Chat layer: one-per-layout gate + legacy label heal (TASK 27)

This commit is contained in:
2026-08-24 09:08:03 -07:00
parent ef710c5537
commit 708b336da7
6 changed files with 169 additions and 27 deletions
+17 -25
View File
@@ -1,44 +1,36 @@
# HANDOFF — Session State
## Branch
`task26-webcam-menu-gate` @ working tree — **code + docs complete, NOT yet committed** (commit on your word). Parent: `task25-backgrounds` @ `7dd140a`, committed and pushed, clean.
`task26-webcam-menu-gate` @ `ef710c5` — TASK 26 committed (not pushed). Working tree now carries **TASK 27, NOT yet committed** (commit on your word). Parent chain: `task25-backgrounds` @ `7dd140a` (pushed), still unmerged to main.
## Shipped: TASK 27 — YouTube Chat layer gate + legacy label heal
Creator follow-up to TASK 26: "do the same thing with the YouTube chat layer" + make the Layers-row label match the (+) picklist.
1. **Gate:** `CanAddYouTubeChat` = `StagedScene != null && !Scenes.Any(s => s.Elements.OfType<Source>().Any(e => e.Type == SourceType.ChatBox))` — one chat layer per layout; the (+) item greys out while any scene carries one. Raised in the StagedScene setter + `OnElementsChanged`; `AddSource` refuses a second as defense in depth. XAML `IsEnabled` + tooltip "One chat layer at a time — it's already in your stream".
2. **Label heal:** layers added by commit `65641d8` were named "Chat"; LoadLayout heals exactly that un-renamed default to "YouTube Chat" so the Layers row matches the picklist. Creator renames untouched; idempotent.
3. **Test (Good Dog Rule — ONE):** `ChatLayerGateTests.LegacyChatName_Heals_And_CanAddYouTubeChat_Gates_On_The_Existing_Layer` — real window + temp DB with a legacy "Chat" ChatBox row in Starting: healed on load, persisted on save, greyed cross-scene + in-scene, re-enabled after `RemoveSourceCommand`.
4. **Suite: 225 total, 224 pass** — only failure is the known pre-existing `AudioPipelineTests.Mix_HonorsProviderGains_AndGameMute_KillsTheLoopback`. Build 0 warnings.
5. **Docs same commit:** ai.md (chat rule + heal next to the webcam gate), TASKS.md TASK 3 row 28, this file.
## Shipped: TASK 26 — webcam (+) menu gates on the app-wide identity
Creator's visual pass of TASK 25 came back green except one item: the (+) menu's Webcam row wasn't
greyed out when a webcam was already deployed in another scene. Root cause: `CanAddWebcamToStagedScene`
only checked the staged scene's config; the truth is the `_webcam` singleton.
1. **Renamed + regated:** `CanAddWebcamToStagedScene` → **`CanAddWebcam`** =
`StagedScene != null && _webcam == null` (`MainViewModel.cs:308`). Raised at all five sites:
StagedScene setter, `ReacquireWebcam` (layout load), `OnElementsChanged`, `_webcam` create
(`AddWebcamToStagedSceneAsync`), last-config removal (`RemoveElement`).
2. **XAML:** the Webcam MenuItem's `IsEnabled` binding renamed to match; tooltip verbatim.
3. **Test (Good Dog Rule — ONE):** `WebcamMenuGateTests.CanAddWebcam_Gates_On_The_AppWide_Webcam_Identity`
— real window + temp DB seeded with a `Webcam` row + one config in Starting; asserts greyed while
Live is staged (the old gate's false positive), greyed while Starting staged, re-enabled after
`RemoveSourceCommand` drops the last config, and zero `Webcam` rows after save.
4. **Suite: 224 total, 223 pass.** The only failure is the known pre-existing
`AudioPipelineTests.Mix_HonorsProviderGains_AndGameMute_KillsTheLoopback` (unrelated).
Build 0 warnings.
5. **Docs same commit:** ai.md (gate rule + rename; dropped the stale "empty-canvas right-click
Show Webcam" claim — that XAML never shipped), TASKS.md TASK 3 row 27, this file.
6. **Dead code flagged, not touched:** `ShowWebcamCommand` / `CanShowWebcamInStagedScene` are wired
in the VM but bound nowhere — audit item, separate branch if wanted.
`CanAddWebcamToStagedScene` → **`CanAddWebcam`** = `StagedScene != null && _webcam == null` (`MainViewModel.cs:308`), raised at StagedScene setter / `ReacquireWebcam` / `OnElementsChanged` / `_webcam` create / last-config removal; XAML binding renamed, tooltip verbatim. ONE integration test `WebcamMenuGateTests.CanAddWebcam_Gates_On_The_AppWide_Webcam_Identity`. Committed `ef710c5` with ai.md/TASKS row 27/HANDOFF docs.
## ⚠️ Landmines
- **User verdict (2026-08-23):** "The AI wrote fucked up code after repeatedly and intentionally misunderstanding instructions." Extends the ai.md 2026-08-22 audit flag — trust nothing from those sessions without manual verification.
- **Pre-existing test failure is ONE** (`AudioPipelineTests.Mix_HonorsProviderGains_AndGameMute_KillsTheLoopback`) — not caused by this branch.
- **Pre-existing test failure is ONE** (`AudioPipelineTests.Mix_HonorsProviderGains_AndGameMute_KillsTheLoopback`) — not caused by these branches.
- **Real-MainWindow tests MUST set `LayoutPathOverride`** (ai.md) — DB pollution wiped real data once.
- **`Source.Type` setter derives `IsBackground`** (Models/Source.cs:40) — any Type change must re-set IsBackground after.
- **vstest quirk:** only one `--tests:` param allowed; plain substrings work, alternation regexes silently match nothing.
- Notification.Wpf gotchas (ai.md/TASKS.md): area routing by XAML `Name`; unknown AreaName silently drops; `NeverExpires()` = `TimeSpan.MaxValue`; `ToHex()` = `#AARRGGBB`.
- **Dead code flagged, not touched:** `ShowWebcamCommand` / `CanShowWebcamInStagedScene` are wired in the VM but bound nowhere — audit item, separate branch if wanted.
## Next steps
1. Commit this branch (code + docs, one commit), then merge/PR per Good Dog Rule (one integration test ✓).
2. Creator visual pass on Windows: add a webcam to any scene → the (+) menu's Webcam row greys out everywhere; remove it → row re-enables.
3. Then merge `task25-backgrounds` → main (still pending from last session), relaunch app once so the real `%APPDATA%` DB self-heals.
1. Commit TASK 27 (code + docs, one commit), push both branches.
2. Creator visual pass on Windows: add YouTube Chat to any scene → (+) item greys everywhere; legacy "Chat"-named row (if present) shows as "YouTube Chat"; remove → re-enables.
3. Merge `task25-backgrounds` + `task26-webcam-menu-gate` + this branch to main per Good Dog Rule (one integration test each ✓); relaunch app once so the real `%APPDATA%` DB self-heals (backgrounds + chat label).
## Build
```bash