Files
LlamaCasty/HANDOFF.md
T
gramps 5a1993b6db fix(persist): one shared z-order across Source + WebcamSceneConfig rows
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.
2026-09-20 09:23:46 -07:00

9.5 KiB
Raw Blame History

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.