11 KiB
HANDOFF — Session State
Branch / Commit State
main HEAD = 6d11e8b — #12 landed (per-element raster reuse: _elementRasters, steady-state compositor allocation ≈ 0; regression test; 289 tests). #11 landed as d35823a (release counter in wordmark; capture/camera/web rings 4→8 + Epoch — three-loop hunks dropped per plan). User tested take-15 frame (~clean). Recording output: webcam "black square" regression under the web overlay is OPEN (spin guard triggered; research first before next attempt).
today-slices branch @ ce87490 (#17) — preserves ALL 17 commits of the 7fcb2ad→ce87490 chain
(1 716a77f … 17 ce87490), including the two-loop producer, encoder staging fix, chat fix, e2e
harness. Nothing is lost; never hard-reset away from it without preserving.
ROLLBACK RECORD (2026-09-04)
- Rolled back to #9
c46f3a1viagit reset --hardonmain(user-directed) after the user confirmed #10 introduced the tearing. Rolling forward WITHOUT #10 (per user directive) starts from this commit. today-slices@ce87490preserves the entire 17-commit chain — the fallback recovery point.
REJECTED this session (2026-09-05) — web transparency fix FAILED, reverted
0f441d8 "composite consumes the FULL canvas" was wrong. Theory: the alpha-crop fed to the
compositor's UniformToFill zoomed opaque content over the whole element rect → black box. Fix: hand
the compositor the full transparent-background canvas (8-deep ring + Epoch), keep the alpha crop for
the preview/selection only. User tested: "didn't work at all, and broke additional crap." Reverted
as 1295e0e. Tree verified back at the #11 state; builds 0 warnings both projects.
Two hard facts the theory did NOT anticipate:
- An opaque dark band still covers the webcam region in the RECORDING (the "broke additional crap" may be the widget reappearing over the webcam in the preview/preview sizing — unconfirmed).
- The preview half looked fine while the recording half was dark, per the user, all along.
SPIN GUARD TRIGGERED (AGENTS.md Working rules): this is the second failed remedy for the same
symptom (web sources composite dark/opaque over other layers in the recording). Third-guessing over
the same code is forbidden. Next attempt MUST start with external research — how OBS / CEV /
other overlay toolchains keep browser-source transparency in the recorded output, cited in the
commit message AND MyMistakes.md before coding. Likely seams to revisit after that research:
CapturePreviewAsync PNG alpha → FormatConvertedBitmap w/ PixelFormats.Bgra32 (does the
premultiplied/straight-alpha conversion drop alpha?), and the preview-vs-recording consumer
difference (preview = WriteableBitmap on the dispatcher; recording = composite read on the pump
thread) — but NO code until the research says what to do. Also: the user's very first observation —
"webcam blacked out by the animated web resource layer" — may mean the widget's HOST page itself
paints an opaque/dark background (injection script wouldn't fix a page that sets its own bg) — verify
against a blank page, not an alert host, before blaming the compositor again.
The commit list (for picking this up anytime)
Numbered oldest→newest (#0 = the working baseline the user picked: 7fcb2ad "zero known failures era
begins"). #10 is the breaking commit (two-loop producer = the recorded-output tearing). #9 is the
last working commit. (SHAs = full 40-char hashes below):
| # | SHA | Subject |
|---|---|---|
| 0 | 7fcb2ad… |
baseline: "zero known failures era begins" (last build the user trusted before the chain) |
| 1 | 716a77f |
take-3 starvation fix (deadline pacing + row-blit) |
| 2 | 432adfd |
slice 2 — integer bilinear + pump scratch pool |
| 3 | 1c48849 |
slice 3 — chat raster cache |
| 4 | 27bf743 |
GUID build stamp |
| 5 | 6af2026 |
slice 5 — paste cache |
| 6 | 09a866e |
slice 6 — sleep quantum fix (timeBeginPeriod + spin tail) |
| 7 | eb4c379 |
slice 7 — pump off UI thread |
| 8 | fbc8562 |
slice 8 — screen-capture buffer ring + Epoch + gen2 stat |
| 9 | c46f3a1 |
docs — LAST WORKING (tree = slice 8); main sits here now |
| 10 | 8260474 |
⚠ BREAKS: two-loop producer — introduced the output tearing; never lands |
| 11 | 6fd1d9c |
#11 — release counter in wordmark + capture/camera/web rings 4→8 (FramePump hunk = two-loop-only, drop) |
| 12 | d1126dd |
#12 — compositor per-element raster reuse + MJPEG camera negotiation (FramePump hunk = two-loop-only, drop) |
| 13 | e4621c1 |
#13 — docs (HANDOFF rewrite) |
| 14 | b4f6a92 |
#14 — docs (recording audit) |
| 15 | 7bf00a9 |
#15 — e2e producer-smoothness bar (thresholds tuned to two-loop; re-tune on single loop) |
| 16 | 1a808ae |
#16 — chat fix: empty-buffer chat box composites |
| 17 | ce87490 |
#17 — encoder staging snapshot (the genuine tear cure; belt-and-suspenders on the single loop) |
Full SHAs: 0=7fcb2ad…(baseline) · 1=716a77f · 2=432adfd · 3=1c48849 · 4=27bf743 · 5=6af2026
· 6=09a866e · 7=eb4c379 · 8=fbc8562 · 9=c46f3a1 · 10=8260474 · 11=6fd1d9c · 12=d1126dd
· 13=e4621c1 · 14=b4f6a92 · 15=7bf00a9 · 16=1a808ae · 17=ce87490.
Roll-forward plan (user's method, ONE at a time, user tests each): apply #11 → #17, skipping #10 entirely; drop each commit's two-loop-only FramePump hunks.
What just happened (2026-09-04, bisect verdict)
The user bisected the 17-commit chain by directing resets of main + builds + live runs. Verdict:
- #10
8260474(slice 9, the two-loop producer) INTRODUCED the screen tearing over the layered elements in the recorded output file. #9 (c46f3a1) does NOT have it. Tomm every time. - Mechanism (closed): RenderLoopAsync owns a private 4-deep crop ring and publishes the newest
composite; OutputLoopAsync calls
SubmitFrameAsync(published.Frame)— and at #10 the encoder still writesframe.BgraPixelsDIRECTLY into the async stdin pipe (no copy). When the encoder/pipe stalls~50ms, the free-running render loop laps the 4-deep ring and REPAINTS the array being drained → the recorded frame is a mix of two composites = the tearing. Preview is immune (synchronous WriteableBitmap copy). The #10 code comment admits it: "torn frame if the renderer laps the ring during a >~50ms encoder stall — bounded cosmetic risk". #9's single serial loop cannot tear by construction (same thread owns render→submit→release).
- The genuine fix for the tear is
Service c87490's core: snapshotframe.BgraPixelsinto an encoder-owned staging buffer insideSubmitFrameAsyncBEFORE the async write. It was bundled into #17 and rejected wholesale with it.
DECISION (user directive): roll forward from #9 WITHOUT landing #10's two-loop producer. User's method: apply #11→#17 one at a time; FIRST tell him what the commit does/fixes; HE tests; we proceed or drop. Do exactly what is asked, no more, no suggestions, until the code is "unfucked".
Verified contents of #10 (for the roll-forward)
Unsafe (the tear, never lands): Services/Encoder/FramePump.cs — the whole two-loop rewrite.
Safe, producer-side allocation only (no render/submit-path contact):
Services/Audio/AudioMixer.cs—MasterOutputGain = 1.4fpre-limiter (+40% creator ask; pairedAudioPipelineTestsbound 0.55→0.7).Services/MediaCaptureFrameSource.cs— 4-deep camera ring +Epoch(ends ~110-220MB/s LOH churn).Services/WebView2Manager.cs— reused canvas scratch + 3-deep out-ring + reused WriteableBitmap (ends two fresh arrays + a new bitmap per capture tick).
What was attempted / aborted this session
Cherry-pick of 6fd1d9c (#11) onto #9 was started, hit conflicts, and was ABORTED on the user's order.
Cleanup done: cherry-pick --abort fails for -n (no sequencer state) → git reset --hard HEAD.
Tree verified clean at #9. Learned (record before re-applying): #11 on the #9 base = release
counter (#11's "non-ring half", [TopBar.xaml, ytLive.csproj, BuildStampTests.cs]) + ScreenCapture ring
4→8 + NEW camera ring + NEW WebView2 pooling (these blocks are ADDED fresh in #11 at depth 8, not just
widened) + docs. #11's FramePump hunk (ring = new byte[8][]) is two-loop-only → DROP. #12's FramePump
hunk (stats formatting) is two-loop-only (TryReportStats) → DROP. #13/#14 are docs-only. #15 e2e bar
was thresholded against two-loop measurements. #16 (chat) and #17 (encoder staging) are pump-independent
and apply cleanly.
OPEN — next, in order
- Webcam-black-square-under-web transparent: research FIRST. Second failed remedy → spin guard. Externally research how OBS/CEV keep browser-source transparency in recorded output; cite the URL in the commit AND MyMistakes before any code (see REJECTED section above). Do NOT touch the compositor/web code until research names a seam.
- #12 MJPEG camera negotiation —
MediaCaptureFrameSourceMJPEG negotiation before frame reader creation (SetMediaStreamPropertiesAsync → best MJPEG ≥640×360 @≥30fps, ≤1280 wide), startup.log truth line of actual media type. Stat formatting (:F1ms) already in #12 diff. Next to apply. - #13
e4621c1, #14b4f6a92— docs (TASKS restructure this session). - #15
7bf00a9— e2e smoothness bar (re-tune thresholds to single-loop measurements if needed). - #16
1a808ae— chat empty-buffer composite fix + test. - #17
ce87490— encoder staging snapshot (the genuine tear cure — applies on the single loop as belt-and-suspenders). - Unit B (top bar + session logic) — spec + settled decisions are in the old HANDOFF history on
today-slices; don't re-ask.
Landmines
- testhost shares startup.log with the app — filter by time when triaging.
- Stale testhost/exe locks the DLL (MSB3027):
taskkill /F /IM testhost.exe/ytLive.exefirst. - Do NOT run full-suite vstest (WASAPI hang, pre-existing); flow = clean build + per-class + scope-check.
- Pacing-seam fakes MUST await/yield (sync-completed task → pump runs inline on StartAsync → hang).
FakeEncodersnapshots submitted frames — keep any new fake encoder honest about buffer recycling.- Multi-stage fixed-point: shift only at the end (MyMistakes 2026-09-04).
- Real-
MainWindowtests:LayoutPathOverride+ temp DB mandatory;VolumePushOverridefor volume. - ffmpeg: month-end pinned build;
Startup.log"Recording saved:" lines show the real final path. - Kill the app (
/mnt/c/Windows/System32/taskkill.exe /F /IM ytLive.exe) before any build that replaces the running binary.
@ User note
Do EXACTLY what the user asks, no more, no suggestions. He is driving the unfuck personally: commit-by-commit roll-forward, he tests each, proceed or drop. Keep responses SHORT. He is high-voltage — do not spin, do not theorize, do not touch docs/commits he didn't order. Commit every work unit; push on his word only.