fix(core): stop compressing discovered resources in place - #2381
Conversation
Under PERCY_GZIP, processSnapshotResources compressed each resource by overwriting `resource.content` and `resource.sha`. Those objects are the same ones held by the build-wide resource cache, so the first snapshot's upload pass replaced every cached body with its gzip bytes. Every later snapshot then had those entries replayed to the browser via Fetch.fulfillRequest using the resource's original headers -- e.g. `content-type: text/css` with no `content-encoding`. The browser sniffs a body that does not match the declared type and blocks it (ERR_BLOCKED_BY_ORB), so the stylesheet is never parsed. Anything it references is therefore never requested and never captured: a stylesheet carrying @font-face yields no webfont for any snapshot after the first, and the renderer 404s the font it still needs. Compress a copy and leave the cached entry holding the original bytes. Cache hit rates are unchanged; only the mutation is gone.
Claude Code PR ReviewPR: #2381 • Head: d3e1410 • Reviewers: stack-code-reviewer SummaryStops Review Table
FindingsNo confirmed findings. Investigated and not confirmed
The reviewer ran the spec with a bare Re-run through the repo's own runner at this exact commit:
So the test is a valid regression test that fails on the unfixed code and passes on the fixed code, and the reviewer's observation was an artifact of the invocation, not of the diff. The rest of that reviewer's analysis was independently spot-checked and holds: One note on the coverage annotation, non-blocking: swapping Verdict: PASS |
The istanbul pragma still named a variable the previous commit deleted, and the size comment restated what the code does rather than why. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Problem
With
PERCY_GZIPenabled, webfonts declared via@font-facewere captured for the first snapshot of a build and missing from every snapshot after it, so the renderer 404'd fonts the page still needed (icon fonts render as blank boxes).Root cause
processSnapshotResourcescompressed resources in place:Those objects are the same ones held by the build-wide resource cache —
network.intercept.saveResourcestores the object itself, not a copy. So the first snapshot's upload pass replaced every cached body with its gzip bytes.Every later snapshot then had those entries replayed to the browser via
Fetch.fulfillRequestusing the resource's original headers, e.g.content-type: text/csswith nocontent-encoding. Compressed bytes are not parseable as the text type they are labelled with, so the stylesheet yields no rules — and anything it references is therefore never requested and never captured.A stylesheet carrying
@font-faceconsequently produces no webfont for any snapshot after the first. Only stylesheet-referenced resources are affected: DOM<img>URLs are found by the preload scanner directly in markup, whereas@font-faceandbackground-imageURLs require the CSS to be parsed first.Confirmed in the logs: the stylesheet is gzipped exactly once, then served from cache for the rest of the build, with
isGzipped()true on it from then on — i.e. the cached entry itself had been mutated.Fix
Compress a copy and leave the cached entry holding the original bytes. Cache hit rates are unchanged; only the mutation is gone.
Verification
Real builds replaying a reporter's own serialized DOMs and config (
PERCY_GZIP=true, 11 snapshots), identical except for this change:Cache hits stay at 41–44 per snapshot in both, so caching still works. A control run with
PERCY_GZIPunset never exhibited the bug.New regression test
keeps cached resources uncompressed when PERCY_GZIP is enabled, verified in both directions: it fails on the unfixed code — snapshot two's manifest contains the root,img.gifandstyle.cssbut nofont.woff— and passes with the fix.