Skip to content

fix(cli): fold canvas pixel hashes into the frozen-sweep fingerprint#2253

Merged
vanceingalls merged 1 commit into
mainfrom
07-11-fix_cli_fold_canvas_pixel_hashes_into_the_frozen-sweep_fingerprint
Jul 11, 2026
Merged

fix(cli): fold canvas pixel hashes into the frozen-sweep fingerprint#2253
vanceingalls merged 1 commit into
mainfrom
07-11-fix_cli_fold_canvas_pixel_hashes_into_the_frozen-sweep_fingerprint

Conversation

@vanceingalls

@vanceingalls vanceingalls commented Jul 11, 2026

Copy link
Copy Markdown
Collaborator

What

Folds a canvas-pixel hash into the frozen-sweep audit fingerprint (__hyperframesLayoutGeometry in layout-audit.browser.js). Each visible <canvas> is downsampled to 8x8 and its pixels hashed into the per-sample signature; tainted/zero-sized/unreadable canvases hash to a constant.

Why

Wild report: strict check reported sweep_static for a seek-correct, pixel-only canvas animation (native 4K WebGL PNG-sequence render) — a transparent moving DOM sentinel was needed to make it pass. The frozen-sweep guard's fingerprint was geometry+opacity of DOM elements only, blind to a canvas repainting without any element moving.

Validation

Built a minimal pure-canvas-motion fixture (zero DOM motion, only a canvas repaint) and confirmed: WITHOUT the fix, check reports sweep_static (bug reproduced); WITH the fix, it doesn't. Also confirmed no new false positive on a DOM-motion fixture. CLI build clean.

🤖 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.

#2253 — fold canvas pixel hashes into the frozen-sweep fingerprint

Verdict: LGTM 🟢

Fixes false-positive sweep_static for WebGL compositions where motion is pixel-only (no DOM elements move). canvasPixelHash downsamples each visible canvas to 8x8 and folds a simple hash (hash * 31 + data[i]) into the geometry fingerprint as c:${hash}.

Key safety properties: tainted/zero-sized/unreadable canvases hash to "x" (constant), so they can't introduce false NEGATIVES for DOM-motion comps — they're no worse than the current code where canvas content is invisible to the fingerprint. The try/catch around getImageData handles cross-origin tainted canvases gracefully.

Review by Miga

miguel-heygen
miguel-heygen previously approved these changes Jul 11, 2026

@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 17db454. Canvas hashing is bounded to an 8x8 readback, visibility-filtered, and fail-silent for tainted/unreadable canvases without weakening DOM-motion detection. Required checks are green. APPROVE.

@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 17db454.

Right diagnosis — a canvas repainting without any element moving is exactly the class the geometry+opacity fingerprint was blind to, and folding a downsampled pixel hash into the same per-sample signature is the least-invasive way to close it. try/catch around drawImage covers tainted/zero-sized/unreadable canvases with a constant hash — no worse than pre-PR for those cases, no new false negative for DOM-motion comps.

One thing worth thinking about before landing:

Question — WebGL preserveDrawingBuffer: false and readback stability. ctx.drawImage(canvas, 0, 0, 8, 8) on a WebGL canvas reads the current composited pixels. Chrome guarantees this for WebGL contexts created with preserveDrawingBuffer: true (or read within the same event-loop turn as the GL draw call). For preserveDrawingBuffer: false (the default), the browser MAY clear the drawing buffer between composites — subsequent drawImage reads can return blank or the last-composited-frame depending on state.

For a WebGL comp that renders every frame (your positive-repro case), each sample is fresh — hashes differ across seek samples where the app draws different content, hashes match where it doesn't. Works.

For a WebGL comp that draws ONCE and stops (splash/still-frame): the front-buffer state at sample N vs N+1 could differ purely because of buffer preservation, not because pixels changed. Risk case: same static canvas across two samples produces hash A then hash "x" (or A then constant-black), and the fingerprint diff false-positives motion → misses the sweep_static detection it was meant to catch.

Vance validated the positive (pure-canvas motion detected) and one negative (DOM-motion still detected). The specific "static WebGL canvas, single draw, subsequent seeks" negative isn't in the validation notes. Worth spot-checking that one before rollout, since misclassifying static-WebGL as motion would flip the sign of the fix for that authoring pattern. off.getContext("2d").drawImage(webglCanvas, ...) is likely stable across the same event-loop turn as the seek → screenshot cycle, but the seek→sample gap may cross a composite boundary.

LGTM from my side.

Review by Rames D Jusso

jrusso1020
jrusso1020 previously approved these changes Jul 11, 2026

@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 and Magi both cleared, CI clean at 17db454.

@vanceingalls vanceingalls force-pushed the 07-11-fix_cli_fold_canvas_pixel_hashes_into_the_frozen-sweep_fingerprint branch from 17db454 to a9d320d Compare July 11, 2026 20:46
@vanceingalls vanceingalls force-pushed the 07-11-fix_cli_fold_canvas_pixel_hashes_into_the_frozen-sweep_fingerprint branch from a9d320d to 7f47b36 Compare July 11, 2026 21:06
@vanceingalls vanceingalls force-pushed the 07-11-fix_cli_fold_canvas_pixel_hashes_into_the_frozen-sweep_fingerprint branch from 7f47b36 to 2935a46 Compare July 11, 2026 21:20
@vanceingalls vanceingalls force-pushed the 07-11-fix_cli_fold_canvas_pixel_hashes_into_the_frozen-sweep_fingerprint branch from 2935a46 to 3338a6f Compare July 11, 2026 21:49
@vanceingalls vanceingalls changed the base branch from graphite-base/2253 to main July 11, 2026 22:23
@vanceingalls vanceingalls dismissed stale reviews from jrusso1020 and miguel-heygen July 11, 2026 22:23

The base branch was changed.

@vanceingalls vanceingalls merged commit 28f492d into main Jul 11, 2026
27 checks passed
@vanceingalls vanceingalls deleted the 07-11-fix_cli_fold_canvas_pixel_hashes_into_the_frozen-sweep_fingerprint branch July 11, 2026 22:24
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