5a1993b6db
A webcam dragged between sources reverted to the bottom of the stack on every relaunch: Source and WebcamSceneConfig each carried independent per-type SortOrder counters, and Load appended all Sources before all configs. Save now stamps both tables' SortOrder from the element's index within scene.Elements; Load merges the two tables' rows by that shared z (sources-first tie-break preserves legacy rows). Cross-type reorder now survives a fresh LayoutStore reload. Task 37 queued: defaults vs current layout split (creator directive) — capture out-of-scope work in TASKS.md rather than folding it in.
133 lines
9.5 KiB
Markdown
133 lines
9.5 KiB
Markdown
# HANDOFF — 2026-09-20 (cross-table z-order bug fixed: webcam between sources survives reload; defaults/current split queued as TASK 37)
|
||
|
||
## Branch / Commit State
|
||
|
||
`main` HEAD = **the cross-type ordering fix** (local — no push). This is a CONTINUATION of the
|
||
2026-09-18 drag-reorder work: the creator kept insisting the reorder "doesn't save" even after
|
||
commit `f91bf87`. Their repro was real and the per-table `SortOrder` theory was the actual mechanism.
|
||
|
||
**No push yet.** Working tree clean after the ordering-fix commit.
|
||
|
||
## ✅ Committed — cross-type z-order: webcam between sources survives save→reload (2026-09-20)
|
||
|
||
**The actual bug the creator reproduced** (`f91bf87` shipped the immediate-save at drop, but the
|
||
reorder still reverted on restart): `Source` and `WebcamSceneConfig` are separate tables with
|
||
**independent per-type `SortOrder` counters**. Save stamped sources 0..n and configs 0..m;
|
||
`LayoutStore.Load` appended **all Sources first, then all WebcamSceneConfigs**. A webcam dragged
|
||
between two sources was structurally unrecoverable — the live scene (Background, YouTube Chat,
|
||
Image, Web Resource-0 sources + one HD Pro Webcam C920 config) reverted the Webcam to the bottom
|
||
of the stack on every relaunch.
|
||
|
||
**Fix:** one shared z-space per scene. `Save` stamps both tables' `SortOrder` from the element's
|
||
index within `scene.Elements` (the non-source branches bump the counter too); `Load` merges the two
|
||
tables' rows **by that shared z** instead of appending tables. Legacy pre-unification rows can tie at
|
||
0 — ties break sources-first, so old data reads back exactly as it saved.
|
||
|
||
**Good Dog test** `CrossTypeReorder_BelowTheWebcam_SurvivesFreshReload` (`LayerReorderPersistenceTests`):
|
||
seeds the builder's real Live layout (4 sources + `HD Pro Webcam C920` config row), reorders
|
||
"Web Resource-0" DOWN below the webcam, calls `OnSceneElementsReordered`, then opens a **brand-new
|
||
`LayoutStore` on the same temp DB — the restart** — and asserts the cross-type order holds. **309/309
|
||
tests pass, build 0 warnings.**
|
||
|
||
**Also committed:** TASK 37 queued — **defaults vs current layout split** (creator directive
|
||
2026-09-20): the DB should hold the static brandable `default` layout; the `current` layout carries
|
||
the build-id (loaded only when it matches `BuildStamp.Id`), enabling one-click revert. Conceptual
|
||
shape `scene[0-4].layerList.[default|current].elementList`; config-file vs second-rows-in-DB not yet
|
||
decided (`TASKS/task-37-defaults-current-split.md` has both + facts). **Process rule established:**
|
||
capture out-of-scope needed work in TASKS.md rather than bolting it onto the in-flight change.
|
||
|
||
## ✅ Committed earlier — layer-list drag-to-reorder persists (2026-09-18, `f91bf87` + below)
|
||
|
||
`StagedScene.Elements` reorder (RemoveAt/Insert) bypasses SceneGraph's mutation surface, so the drop
|
||
must be trapped + persisted. Fix (2026-09-18): the drag sets `_dragReordered` and `EndListDrag`
|
||
funnels the drop through `MainViewModel.OnSceneElementsReordered()` (Sources.cs) =
|
||
`SceneGraph.InvalidateBake(staged) + SaveLayoutNow()` — an IMMEDIATE save at drop (no debounce
|
||
window). The single write stores BOTH the layer list (SortOrder) AND the preview layout (X/Y/W/H in
|
||
the same rows). Tests `LayerReorderPersistenceTests` (real App + temp DB): (1) exact code-behind
|
||
mutation + geometry assert; (2) **real-gesture** test `RealMouseDrag_OnTheLayerList_PersistsTheReorder`
|
||
(`SetCursorPos` + `mouse_event` on a shown Topmost window) — needs an interactive desktop session
|
||
(covered/locked session = pointer no-op = flake). Both green in the 309. **This fix was correct but
|
||
INSUFFICIENT — the cross-table bug above is what the builder actually hit.**
|
||
|
||
## ✅ Committed earlier — webcam app-default gate + attainability (`9d00955`, `75723ac`)
|
||
|
||
Webcam is an **app-level resource** — ONE app-wide default, usable in every scene that can host it,
|
||
NOT blocked by other scenes' placements (creator ruling; supersedes TASK 26). Per-scene gate
|
||
`CanAddWebcam` = scene has no config && attainable (`CameraManager.IsRunning(_webcam.DeviceId)` —
|
||
a live lock, not a saved identity). Add places the default directly (no picker). Identity survives
|
||
last-placement removal; startup lays an app-wide BASE lock (refcount keeps the session alive);
|
||
adopts a solo camera as default. Dynamic why-gray tooltip. Tests: `WebcamMenuGateTests`,
|
||
`WebcamStartupResourceTests`. The DB truth lives at `%APPDATA%\ytLlive\ytLlive.db` (NOT `layout.db`).
|
||
|
||
## ✅ Committed earlier — Rename Recording dialog taller + full-screen hook unhooked (2026-09-18)
|
||
|
||
`RenameRecordingDialog.xaml` Height 230 → 276 → **304** (the +20% revealed the Cancel/Save row also
|
||
obscured); Save button named `SaveRecordingButton`. Test `RenameRecordingDialogSizingTests` asserts
|
||
textbox bottom + Save button bottom inside the client area. **Teardown leak fix:** `Shutdown()` now
|
||
calls `_fullScreenDetector.StopWatching()` — the global `EVENT_SYSTEM_FOREGROUND` hook was never
|
||
unhooked, invoking a GC'd `WinEventProc` on the next foreground event → test-host abort.
|
||
|
||
## ⚠️ Open items
|
||
|
||
- **Build-id on command-line exit (creator demand, 2026-09-20):** when the app exits under
|
||
`dotnet run`, output the build number (`BuildStamp.Id` — a fresh per-build GUID, logged in
|
||
`App.xaml.cs:32` as `Build {Id} (compiled {BuiltLocal})`) as return data on the command line.
|
||
NOT yet implemented — queued, do not fold into another slice.
|
||
- **TASK 37** — defaults/current layout split (see above + `TASKS/task-37-defaults-current-split.md`).
|
||
- **Webcam take fix 2 of 2:** native WMF death when `MediaCapture.Failed` fires mid-stream and
|
||
`SafeStopAsync` disposes reader+capture while the frame thread sits in `TryAcquireLatestFrame`. No
|
||
repro/test — deferred per spin-guard.
|
||
- **Multi-camera selector slice (user-queued):** ≥2 webcams → web-cam selection in the App Settings
|
||
dialog (gear). Currently ≥2 cameras → nothing shows in settings.
|
||
- **Static (+) catalog slice (user-queued):** all catalog rows always listed, greyed with named
|
||
reason tooltips.
|
||
- **Device re-verify (one take session):** slice-18 cache verdicts; webcam fresh-launch with the
|
||
browser holding the camera → red alert that clears after Retry once the browser closes; Live must
|
||
OFFER Web Cam while Chat holds it.
|
||
- **No push yet** — after the take verdict, decide push with the user.
|
||
|
||
## Open threads (carried)
|
||
|
||
- Audio-silence verification — fixed (`724af14`); creator heard real audio.
|
||
- Webcam MJPG missing / ~10–14Hz, truncation-with-dynamic-scenes — queued.
|
||
- Sync control user-doc tutorial — REQUIRED before 1.0 (creator directive; TASK 22).
|
||
- Signed A/V sync: verify the negative (advance) direction on device.
|
||
- The 2026-09-18 "reorder doesn't save" stale-binary theory — RETRACTED 2026-09-20. The builder was
|
||
right; the cross-table z-order bug above is the real mechanism. Never re-air the stale-binary claim.
|
||
|
||
## Landmines
|
||
|
||
- testhost shares startup.log — filter by time; the app also writes fake-device failures
|
||
('test-camera', 'dev-physical-9') from CameraManager unit tests.
|
||
- `cmd.exe /c "taskkill /F /IM ytLive.exe"` (WSL double-slashes mangle) before rebuilds — a live
|
||
app process locks `ytLive.exe` (MSB3021).
|
||
- Build/tests: **Windows dotnet host** (`/mnt/c/Program Files/dotnet/dotnet.exe`). 0 warnings —
|
||
only `./scripts/verify.sh "<files>"`'s clean build counts. Running `ytLive.csproj` alone does NOT
|
||
rebuild `ytLive.Tests.dll` — run the Tests csproj before `vstest`. Known audio flake:
|
||
`Mix_HonorsProviderGains_AndGameMute_KillsTheLoopback` — stale loopback sample in the pipe when the
|
||
mute flips. Unrelated; do not "fix" inside another slice.
|
||
- **Assert footguns in this repo's xUnit:** `Assert.Null(x, "msg")`/`Assert.Single(coll, "msg")` do
|
||
NOT take a message — the 2-arg overloads mean something else (predicate/item). Empty message = 1 arg.
|
||
- The webcam's real state lives in `%APPDATA%\ytLlive\ytLlive.db` (`layout.db` there is a 0-byte
|
||
legacy file). sqlite3 at `/home/gramps/android-sdk/platform-tools/sqlite3`.
|
||
- FramePump tests asserting per-frame CONTENT need a STABLE scene; cache-sensitive assertions can't
|
||
use a call-count resolver flip (tick resolves twice) — key alternation on `OutputIndex`.
|
||
- ffmpeg/ffprobe: `/mnt/c/Program Files/Krita (x64)/bin/` with Windows paths.
|
||
- `MyMistakes.md`: WINRT resource-allocation RECIPE, freeze-audit RECIPE, A/V sync recipe, deadline
|
||
pacing, CoreMessaging DQ, WGC-CLIP + slice blocks. Grep before re-deriving.
|
||
- **Test-host crash (fixed):** GC'd `WinEventProc` — `Shutdown()` unhooks the foreground-event hook;
|
||
never re-introduce `StartWatching` without matching teardown; dialogs with root-relative `/Assets/…`
|
||
Icon can't be instantiated by tests (use `/ytLive;component/…`).
|
||
- **Scene.Elements layer tests:** any seeded scene with `HasBackground=1` loads a healed
|
||
`Background` element pinned at `Elements[0]` (created by `EnsureBackground` on `StagedScene` set) —
|
||
compute expected layer orders from the live collection, never hardcode indices.
|
||
- **Cross-type element order tests** must assert through a FRESH `LayoutStore` (the restart), not the
|
||
running app's store — that's the read-back path the builder's bug exercised.
|
||
- `C:\tmpout` is for ffmpeg evidence artifacts; keep them out of the repo.
|
||
|
||
## Next step
|
||
|
||
The ordering fix is the builder-facing half of the "webcam between sources" bug — a manual take
|
||
(relaunch, live view, drag Web Resource-0 below the webcam, restart, expect it pinned there) closes
|
||
it. Then the copy with the builder on any remaining repro, then decide push. TASK 37 (defaults vs
|
||
current) and the build-id-on-exit demand are queued for a later slice — do not fold them in here. |