Skip to content

fix(producer,engine): gate drawelement on ancestor background-image + tail verify sample#2247

Merged
vanceingalls merged 1 commit into
mainfrom
07-11-fix_producer_engine_gate_drawelement_on_ancestor_background-image_tail_verify_sample
Jul 11, 2026
Merged

fix(producer,engine): gate drawelement on ancestor background-image + tail verify sample#2247
vanceingalls merged 1 commit into
mainfrom
07-11-fix_producer_engine_gate_drawelement_on_ancestor_background-image_tail_verify_sample

Conversation

@vanceingalls

@vanceingalls vanceingalls commented Jul 11, 2026

Copy link
Copy Markdown
Collaborator

What

Two fixes from adversarial testing of the DE parallel router (10 hostile comps, routed vs screenshot-baseline PSNR). The router itself held — both bugs are in general drawElement fast capture, and one slipped past self-verify.

1. Compile gate: ancestor background-image (producer)

drawElementService's per-frame ancestor fill replicates what lies behind the captured subtree by walking up the DOM for the nearest non-transparent backgroundColor. A background-image (linear-gradient, url()) on body/html/a wrapper reads as transparent in that scan, so a deeper ancestor's solid color paints instead wherever the subtree leaves pixels uncovered.

Measured repro: body linear-gradient + html solid color + an element shrinking late in the comp → DE paints the html purple instead of the body gradient. 30.9 dB min frame vs baseline, visually unmistakable. Identical damage single-worker and parallel — general DE bug, in every wild DE render matching this (very common) authoring pattern.

Fix: detectAncestorBackgroundImage() in the compiler (DOM-aware — inline styles on the root's ancestor chain + <style> rules resolved via querySelectorAll, so class-selected wrappers are covered; backgrounds inside the root are deliberately not matched). New compile gate ancestor_background_image, same shape as the 3D/mix-blend gates, bypass HF_FAST_CAPTURE_ANCESTOR_BG=true.

2. Self-verify tail sample (engine)

The verify grid sampled at (i+1)/(k+1) → [20/40/60/80]% of the timeline. The damage above starts at ~79% and peaks after the last sample — verification passed on output that bottomed at 30.9 dB (threshold 32 dB would have caught it, it just never looked there).

Fix: computeDeVerifySampleFractions() — first k−1 samples evenly spaced, last pinned at 95%. Default k=4 grid becomes [25/50/75/95]%. Kills the whole late-onset damage class, not just this repro.

Validation

  • Repro comp (body gradient + shrink reveal): now gates → baseline route, 54.7 dB avg vs ground truth (was 41.6 avg / 30.9 min with the purple surround)
  • Control comp (nested stacked fades, routed): still routes, verify grid [90, 180, 270, 342] of 360, passes, 60.6 dB avg — unchanged
  • Full adversarial matrix context: 6/10 comps routed clean (49–68 dB min), blend/3D gated correctly, animated-canvas damage caught by verify at 16.5 dB with clean revert, video comps route legitimately (frames pre-extracted)
  • Tests: 7 new detection cases (htmlCompiler.test.ts), 5 new grid cases (frameCapture-verifySampleFractions.test.ts); compileStage.test.ts + frameCapture.test.ts suites green

🤖 Generated with Claude Code

@miga-heygen miga-heygen left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

#2247 — gate drawelement on ancestor background-image + tail verify sample

Verdict: LGTM 🟢 (CI failures look like format/lint — likely needs a rebase or format pass)

Two independent fixes for the same failure mode (30.9 dB body-gradient damage):

1. Ancestor background-image detection. detectAncestorBackgroundImage walks the ancestor chain of the composition root (body → html → wrapper divs) checking inline styles AND <style> rule selectors via querySelectorAll through linkedom. Correctly ignores elements INSIDE the root (those are painted by drawElement). The regex BACKGROUND_IMAGE_DECL_PATTERN matches gradient/url but not plain background-color. Test coverage is thorough: rule-selected body gradient, url() on html, inline style, class-selected wrapper, negative cases (child elements, plain color, no root).

When detected, compileStage.ts gates drawElement off — same shape as the existing 3D-transform and mix-blend-mode gates. Env override HF_FAST_CAPTURE_ANCESTOR_BG=true for R&D bypass.

2. Tail-pinned verify sample grid. computeDeVerifySampleFractions replaces the old (i+1)/(k+1) grid with first k-1 evenly spaced + last pinned at 95%. The old grid's final sample sat at 80%, missing late-onset damage (a body-gradient drop starting at ~79% passed verification). Tests prove: strictly increasing, within (0,1), correct single-sample case, empty on disabled.

Both fixes attack the same measured failure from different directions — detection prevents the bad path, tail sampling catches it if it slips through.

Review by Miga

@miguel-heygen miguel-heygen left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed current head 0809c98 and diff. Ancestor background-image detection walks only the root ancestor chain and tests cover body/html/inline/class wrappers; sample grid is bounded and tail-pinned at 95%. All completed checks are green, but the required Windows test job is still pending, so I am not approving prematurely. Please re-request review once that required job is green.

@james-russo-rames-d-jusso james-russo-rames-d-jusso left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed at 0809c98.

Both fixes land at the right layer.

Fix 1 — ancestor background-image gate. The rootcausing is exactly right: drawElementService's ancestor fill walks up for the nearest non-transparent backgroundColor, so a gradient/url on <body>/<html>/a wrapper reads as transparent and a deeper solid paints instead. Detection walks the ancestor chain for both inline styles AND <style> rules (resolved via querySelectorAll — so class-selected wrappers are covered, not just literal html/body). Backgrounds inside the root are deliberately not matched. Same gate shape as the 3D/mix-blend neighbors (packages/producer/src/services/render/stages/compileStage.ts:265-278), HF_FAST_CAPTURE_ANCESTOR_BG=true bypass for R&D. The gate flows through as deCompileGate = "ancestor_background_image" to perfSummary.drawElement.compileGate, so route-decline aggregates are already differentiable — good.

Fix 2 — tail verify sample. computeDeVerifySampleFractions(k) moves the last sample to 95%. The k=4 default becomes [0.25, 0.5, 0.75, 0.95] — retains three equidistant heads (very close to the old grid's density there) and pins a real tail. The k=1 → [0.95] and k<=0 → [] edges are covered in the test set. The strictly increasing within (0, 1) invariant test is nice.

BACKGROUND_IMAGE_DECL_PATTERN matches gradient( and url(. -webkit-image-set(), cross-fade(), element(), paint() — the CSS Images-4 image-notation menagerie — are not matched. All rare in HF authoring by convention, so noting it not flagging it.

LGTM from my side.

Review by Rames D Jusso

@jrusso1020 jrusso1020 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approving per the channel stamp-flow: Rames-D cleared it and the required Windows check is now green at 0809c98 (that pending check was the only thing holding it).

@vanceingalls vanceingalls merged commit e37ebe7 into main Jul 11, 2026
59 of 76 checks passed
@vanceingalls vanceingalls deleted the 07-11-fix_producer_engine_gate_drawelement_on_ancestor_background-image_tail_verify_sample branch July 11, 2026 21:13
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants