- Add ElementKind (Static/Dynamic) to SceneElement base; Source/WebcamSceneConfig classify - Services/SceneGraph.cs: owns the Scenes collection (ViewModel's Scenes delegates to it), the element mutation surface (Add/Insert/Remove/Move, each invalidating the bake), and the queries that were scattered LINQ (GetBackground/GetWebcam/GetChatBoxes/GetSplitPoint/IsStatic) - SceneCompositor: split-aware BakeStaticBase + CompositeLayers + Render(.., staticBase, split); builds/caches the static base below the split point in source-rect space - FramePump: optional SceneGraph -> optimized bake+composite path; falls back to full render - MainViewModel: routes element mutations through the graph; invalidates the bake on static layout/opacity/visibility/useDefaultBackground changes and after background heal/Ensure - Integration test SceneGraphTests.BakedStaticBase_WithDynamicLayer_CompositesCorrectly Derivative survey (mandated): tried before writing — OBS does per-source opacity/visibility caching and static-scene baking; this mirrors OBS's 'cached static source' optimization. 3 documented defensive deviations from the spec (ChatOverlayLayer stays decoupled; background helpers stay VM-static for direct testability; full facade peel deferred post-1.0) in TASKS.md + ai.md. Scope check passed; clean build 0 warnings.
8.8 KiB
HANDOFF — Session State
Branch / Commit State
main. TASK 31 (SceneGraph + baked-crust compositor) implemented, tests green,
docs updated — uncommitted, ready to commit as one work unit (pending push at
the user's checkpoint).
Pushed (reached origin/main == 85893ea, incl. tags): Phase 1 commit 0 +
refactor commits 1..11 + Phase 2 commits 12..18 + the Controls/index.md
docs commit cb54637 + Phase 3 splits A–E (tags refactor-commit-A..E) +
Commit F (Recording, refactor-commit-F) + Commit G (ChatOverlayLayer,
refactor-commit-G). Everything before TASK 31 is pushed; nothing local-only.
Commit log (all pushed):
| Commit | Hash | Tag | Payload |
|---|---|---|---|
| docs | cb54637 |
— | Controls/index.md |
| A | 6944db8 |
refactor-commit-A |
split MainViewModel.cs core → 495 |
| B | 31362d1 |
refactor-commit-B |
split MainViewModel.Streaming.cs → 330 |
| C | fab2e09 |
refactor-commit-C |
split MainViewModel.Background.cs → 418 |
| D | d3271a0 |
refactor-commit-D |
trim SocialsDialogViewModel.cs → 334 |
| E | dcb3637 |
refactor-commit-E |
split LayoutStore.cs → 6 partials |
| F | 3107f92 |
refactor-commit-F |
extract recording concern → MainViewModel.Recording.cs |
| G | 85893ea |
refactor-commit-G |
first true decomposition → Services/ChatOverlayLayer.cs |
What's In Flight
TASK 31 (SceneGraph + baked-crust compositor) implemented — uncommitted, tests green.
Files touched (see git status): Models/SceneElement.cs, Models/Source.cs,
Models/WebcamSceneConfig.cs, Services/SceneGraph.cs (new), Services/Compositor/SceneCompositor.cs,
Services/Encoder/FramePump.cs, ViewModels/MainViewModel.cs, MainViewModel.Scenes.cs,
MainViewModel.Sources.cs, MainViewModel.Webcam.cs, MainViewModel.Background.cs,
ytLive.Tests/SceneGraphTests.cs (new), ai.md, TASKS.md, HANDOFF.md.
Verification: SceneCompositorTests 4, StretchMathTests 4, BackgroundTests 16,
SceneCatalogTests 18, LayoutStorePersistenceTests 12, FramePumpTests 9,
SceneGraphTests 1 — all green. RealAppHost GUI tests hang headless (pre-existing).
Next: run scripts/verify.sh/scope-check.sh, commit, await user's push go-ahead.
The directive (2026-08-31, user): rewrite the project, breaking files into functional components to compliment AI retrieval/processing — NOT line-count chasing. Line count is a guideline for context management, not a design goal.
What was done:
- Phase 3 (A–E): every production
.cs≤ 500, grouped by concern. These are partial shuffles — same class, same shared state. Marginal for retrieval. - Commit F: recording-output concern →
MainViewModel.Recording.cs(partial). - Commit G — the real win:
Services/ChatOverlayLayer.csis a genuine owner-state component (buffer + renderer + timers + preview + liveRenderFrame);MainViewModel.Chat.cs194 → 44 (thin binding facade). First true decomposition.
Expert diagnostic (recorded in ai.md → Key patterns): the remaining partials
(Audio, Webcam, Background, Scenes, Socials…) are binding glue over already-
extracted services (AudioMixer, CameraManager, ScreenCaptureManager,
ChatBoxRenderer, SocialValidator). They have no cohesive owner-state blob to
peel — forcing extraction adds coupling. Chat was the one clean peel.
Next (recommended): TASK 31 landed the core of the scene-graph architecture
(the SceneGraph owner-object owning the collection + mutation surface, and the
baked-crust compositor optimization) without the full binding-surface peel. The
remaining step is the ViewModel-as-thin-facade migration (moving the many
Scenes/StagedScene/LiveScene binding call sites onto the graph) — deferred
as post-1.0 in TASKS.md because it is exactly the "unsupervised blind peel"
regression risk. Do it at a real Windows session where the GUI tests can run.
Pending bus: ViewModels/index.md Phase 3 tracker has rows A–E (closed);
Phase 3 marked COMPLETE. Watch-list (under limit, no action): Controls/PreviewPane.xaml.cs
(464), ViewModels/MainViewModel.Audio.cs (408).
Landmines
- Pre-existing test failures (do not chase):
AudioPipelineTests.Mix_HonorsProviderGains_AndGameMute_KillsTheLoopback+RoundClipInteractionTests.Round_Clip_Corner_Is_Grabbable_And_Shape_Is_Circle. - Tests occasionally abort early (236/237) — known flake, accepted by
verify.sh. - App running locks
ytLive.exe→ build fails MSB3027. Kill PID before building. - WinForm crash from
Window_PreviewMouseLeftButtonDown— handled in-side the preview/left panel; don't add handlers that derefOriginalSourceunguarded. - Controls cannot see
Window.Resources(converter rule, Commit 13): any StaticResource the window once supplied must live at app level inThemes/Controls.xaml. The 7 converters were promoted there. - Some control XAML needs its own
xmlns:models/xmlns:Helpers— a moved block lost its namespace when it left MainWindow's root (LeftPanel hit this, MC2000 "key null"). Always add the model/helper xmlns on each control root. - Name-looked-up host stubs must stay in the window:
WebViewHostPanel(ctorInitWebView2(WebViewHostPanel)) andToastArea(NotificationService.InArea("ToastArea")) are resolved by name from the window — keep them as window-root children, don't bury them in a control. OverlayHost deliberately left them in place (Commit 18) for this reason. - Pre-commit gate:
scripts/verify.sh(0-warning build + full tests) andscripts/scope-check.sh "file1" …— scope-check REQUIRES explicit file args. - Build from WSL with the Windows dotnet host only (see
AGENTS.md).
Todo state
- MainViewModel refactor: Phase 1 complete (11/11 partials), pushed.
- MainWindow.xaml refactor: Phase 2 complete (6/6 controls), pushed at
1f4624c. - 500-line compliance: Phase 3 complete (A–E, all ≤500), pushed.
- True decomposition: Commit G —
Services/ChatOverlayLayer.csshipped + pushed (first owner-state component; pattern recorded inai.md). - TASK 31 — SceneGraph + baked-crust compositor: implemented, tests green, uncommitted (core optimization + collection/mutation ownership + integration test; see
TASKS.mdfor the 3 documented defensive deviations). The full ViewModel-as-thin-facade peel (moving the entire binding surface) is intentionally deferred post-1.0 — flagged inTASKS.md. - TASK 3: 27/30 (preview compositor 16, text source 17, alerts 20 still open).
- TASK 4: ✅ shipped. TASK 9: items 1–3 shipped; 4–7 open. TASK 10: steps 1–7; Velopack pending.
- TASK 18: shipped, creator verification pending. TASK 19/23, 20, 21-A: shipped.
- TASK 22 (audio sync offset): not started — required for v1.
Session summary (2026-08-31)
- Ran the 500-line Phase 3 split to completion (A–E), then the continuation.
- Re-structuring principle (user correction, 2026-08-31): split by functionality, so an AI can process one concern per file; the 500-line count is a ceiling, NOT the target. Do not pad or reshape code to a number.
- Correction of a false premise: my plan to "extract FFmpegEncoder /
StreamHealthMonitor / FramePump" was a no-op — those already exist as
Services/Encoder/*. The honest seams were (F) the recording concern and (G) the chat overlay. - Commit G = first TRUE decomposition:
ChatOverlayLayerowns chat state + behavior;MainViewModel.Chat.cs194 → 44 thin facade. This is the pattern that actually compliments AI retrieval (one self-contained unit per feature), unlike the partial shuffles of A–E, which only marginalize context because every partial still shares the god-object's state. - Accident caught & recovered (Commit D): a
> ViewModels/SocialsDialogViewModel.cswrite that truncated the file it was reading left it at 1 line; restored viagit checkout --, re-ran from temp files. Lesson inMyMistakes.md: neverawk … > SRCwhile awking SRC; stage cuts into temp files, then assemble. - Everything through G is pushed to
origin/main(85893ea) with tags A–G. - TASK 31 implemented (uncommitted, tests green): added
ElementKind(Static/Dynamic); createdServices/SceneGraph.csowning the collection + mutation surface + bake cache; madeSceneCompositorsplit-aware (BakeStaticBase/CompositeLayers/Render(staticBase,split)); wiredFramePumpto the optimized path; invalidated the bake on static mutations; addedSceneGraphTests.BakedStaticBase_WithDynamicLayer_CompositesCorrectly. 3 documented defensive deviations from the spec (ChatOverlayLayer stays decoupled; background helpers stay VM-static for direct testability; full facade peel deferred post-1.0). SeeTASKS.md→ TASK 31.