# MyMistakes.md > **Two jobs**, distinguished by heading: > > 1. **Per-task failure log** β€” updated before every commit touching that task: > current iteration + why the last one failed. On task complete, committed AND > pushed β†’ truncate to this stub. A new task does NOT seed this file until its > first failure. > 2. **Recipes registry** (DERIVED-SOLUTION RULE, see `AGENTS.md` πŸ”¬) β€” the durable > home for one-off derived solutions, recipes, and how-tos. The moment you work > out a reusable solution, write it here **in the same session**. GREP THIS FILE > FIRST when you hit a "I've done this before but have to figure it out again" > wall. Recipe entries stay permanently (they are NOT truncated on task > completion) β€” only the failure log truncates. ## πŸ”¬ Recipes registry ### WINRT RESOURCE-ALLOCATION CALLS: WRAP PER-CALL, DEGRADE TO NEXT OPTION (RECIPE) Creator callout (2026-09-15): WinRT/COM calls that allocate or start a resource β€” `InitializeAsync`, `CreateFrameReaderAsync`, `StartAsync`, `CreateReaderAsync` & friends β€” are THROW-HEAVY. An unsupported subtype/format, a device that just vanished, an access mode rejected mid-flight: these surface as `ArgumentException`/`E_INVALIDARG` ("value does not fall within the expected range") or HRESULTs, NOT as a returned status you can switch on. Relying on ONE outer catch to "handle failures" is not handling β€” one rejected call inside a fallback ladder aborts the whole ladder and every untried option. Rule: - Each allocation/start call inside a try/catch of its OWN, so a throw on candidate N falls through to candidate N+1 (log each rejection with its message/status; collect them for the final error string). - `return`/`break` on success must be reached WITHOUT passing through a `finally` that disposes the resource you just committed (classic reader/capture dispose-after-commit bug). - Unsubscribe + dispose the partial resource in the catch block when the subscription happened before the throwing call. - The outer catch stays as the LAST-RESORT net for device-level errors, not the primary one. - Same discipline applies to the frame-consumption side: teardown races reader threads (see HANDOFF crash follow-up) β€” a frame callback can't assume the pipeline is alive. Applied in `MediaCaptureFrameSource`'s reader-subtype ladder (2026-09-15, webcam-take fix). ### SPIN GUARD β†’ RESOLVED β€” web overlay transparency + bounding box (RECIPE) **THE ONE ROOT CAUSE THAT EXPLAINS EVERY FAILED TAKE:** WebView2's `CapturePreviewAsync` produces an **OPAQUE** PNG. From the WebView2 spec (sender: MicrosoftEdge/WebView2Feedback `specs/BackgroundColor.md`): "WebView will always honor a webpage's background content." `DefaultBackgroundColor = Transparent` only shows through pages with NO background style β€” the widget's own CSS paints html/body opaque. Every take below built on the false premise "the capture has transparent margins, alpha=0"; it never did. `FindContentBounds` then had no alpha-0 margins to find β†’ wrong crop β†’ black bounding box. The compositor blend saw alpha=255 β†’ black over webcam = "transparency broken". Same bug, three symptoms. **THE FIX (the OBS way, applied 2026-09-08):** inject the transparency BEFORE the page parses using `CoreWebView2.AddScriptToExecuteOnDocumentCreatedAsync` β€” documented to run "before the HTML document has been parsed and before any other script included by the HTML document is run" (learn.microsoft.com/dotnet/api/microsoft.web.webview2.core.corewebview2.addscripttoexecuteondocumentcreatedasync). The old `ExecuteScriptAsync` on NavigationStarting/NavigationCompleted ran AFTER page scripts/CSS β†’ widget page repainted background opaque β†’ lost the fight. OBS browser sources do the same via a pre-parse user.css. Kept the nav handlers as a post-load re-assertion. **Take timeline (the honest record):** - `bccdb48` (Aug 28): added FindContentBounds crop β€” correct idea (tight bbox, no dead space), but the capture was OPAQUE so the bbox math was built on nothing. - `5348b5c` (Aug 28, 2 min later): reverted to full-frame no-crop β€” looked "good" for a full-bleed widget, but floating widgets regained dead space ("ghost boundary"). - take-21 (`e002847`) full canvas β†’ shrunken/offset widget (UnifomToFill of whole canvas into a small element = lost resizing). - take-22 (`ed9d7c1`) crop width used as canvas stride for buffer indexing β†’ garbage. - take-23 (`f6802c7`) stride fixed with `src.Width`; PasteKey lacked CropBounds β†’ stale raster cache β†’ stale crop. - take-24 (`081e4c1`) CropBounds in PasteKey β€” STILL BROKEN because the SOURCE ALPHA WAS NEVER REAL. - take-25 (**ME, this session β€” the user's "RE-INTRODUCING THE BOUNDING-BOX PROBLEM"**): I removed FindContentBounds + the crop path entirely, betting full-canvas UniformToFill was the answer. It WASN'T β€” the source is opaque-black, so the element rendered as a SOLID BLACK BOX (the user's screenshot: "a black box in the lower right corner"). Killed the crop β†’ dead space returned AND black box. The compositor math was ALREADY correct; gutting it was vandalism in response to a source-level bug. **Rules, self-inflicted:** 1. Instrument FIRST. This session added: first-capture PNG dump of the raw WebView2 PNG (to `%TEMP%\ytLive-web-.png`) + alpha min/max/mean/%zero + FindContentBounds result logged to startup.log once per session. That's the diff between a five-take loop and a five-minute diagnosis. 2. When the same symptom loops across takes, the PREMISE is wrong, not the code β€” the capture being transparent was the load-bearing premise and it was never verified. 3. Do not delete code paths that fix one axis (crop=bbox) while debugging another (source alpha). Revert scope creep; keep layer contributions separable. **2026-09-10 follow-up β†’ NOW VERIFIED and hardened.** The 12:54 and 13:50 sessions showed the REAL widget document capturing TRANSPARENT (widget dumps `w1..5` for `8d7234ec`: alpha 100% zero, content 12Γ—3; whole-file decodes of `%TEMP%\ytLive-web-*.png` alpha max = 0) while the RECORDING kept showing a black, opaque box over the element rect (crisp edges at the exact rect 1231,679 705Γ—396 β€” NOT the desktop showing through). Concluson: branch (a) β€” the OLD inline `element.style.background='transparent'` injection only wins while the page has nothing to paint; once the widget connects and paints its own container background-COLOR, the capture goes opaque again β†’ black box. The OBS-validated answer (valid for arbitrary pages for a decade) is an injected pre-parse `