docs(compression): node 26 gave brotli a dictionary, so retire that limit - #305
Merged
Conversation
…imit Gotcha 14 said `node:zlib`'s brotli has NO dictionary parameter, listed the nine params as the whole list, and rested part of the dcz-over-dcb case on dcb needing the `brotli` CLI back in the build path. Node 26 takes a `dictionary` option (nodejs/node#61763, merged 2026-02-13) and `.node-version` is already 26, so CI has it too. Measured on v26.7.0, q11 over a 21KB target: 70 bytes with no dictionary against 18 with one. The CONCLUSION is untouched, because dcz won on decode and neither encoder's availability was ever the axis. What changed is one of the stated reasons. Four copies of the claim moved together, per gotcha 18's own lesson about grepping the tree when a note lands: CLAUDE.md twice, the build.mjs comment at the delta emitter, the roll-shell-dictionary.mjs header, and /garage/compression, which carried it in prose AND in an understanding-check explanation. While in that page, its dcz-vs-dcb table still held the pre-correction figures (79 vs 80 bytes, "about twice as fast"). The 2026-07-28 re-measure against real dictionaries never reached it: dcb is 245 bytes ahead across all 12 pairs (6.8%, winning 11 of 12) and dcz decodes 8.3x faster, not 2x. Both inputs to the original argument were wrong, in opposite directions, and the call survived on the structural point instead: decode scales with the RECONSTRUCTION, not the delta, so a smaller dcb delta never shrinks the decode gap. The quiz's correct answer restated the old figures verbatim and now states that. Adds one general rule, since three runtimes disagree on this single option: probe dictionary support, never infer it. node 26 honours it, workerd accepts it for zstd and silently ignores it, and bun does the same through 1.3.14 with the fix landing in oven-sh/bun#34427 (unreleased; verified on 1.4.0-canary.1). The failure is silent every time, because a frame compressed WITHOUT the dictionary still decodes fine WITH it, so the only signal is a byte count that never shrank. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Deploying with
|
| Status | Name | Latest Commit | Updated (UTC) |
|---|---|---|---|
| ✅ Deployment successful! View logs |
aadhar-sh | e5136ca | Aug 10 2026, 04:05 PM |
Wire-size diff vs merge base
Brotli q11, the quality the edge serves. Every number here is deterministic: Client assetsNo change. 12 files, 60.92 KiB Brotli total. PagesTotal Brotli: 470.81 KiB → 471.25 KiB (+0.44 KiB, +0.1%) across 46 files, 1 changed.
Worker bundleNo change. 268.62 KiB gzip. Dictionary deltas (
|
oddharsh
enabled auto-merge (squash)
August 10, 2026 16:04
oddharsh
added a commit
that referenced
this pull request
Aug 10, 2026
Two more instances on 2026-08-10 (#305, #307), so four across four unrelated PRs, every one alongside a CodeQL pass. #305 is what makes this worth writing down rather than just recounting. It changed documentation, four code COMMENTS and one quiz string, and failed the check identically to a build-script PR, with `title: null` and a lone annotation at `.github:211`. A diff with no executable change cannot carry a security finding, so a check that reddens on it is reporting on itself. That is a sharper discriminator than the API tell when the API tell is ambiguous. Also records where to confirm the stakes: the ruleset, not the check list. `validate` is the only required context, so this has been red on four PRs while gating none of them. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
oddharsh
added a commit
that referenced
this pull request
Aug 10, 2026
…d 2x faster (#307) * feat(build): a control for bun, which builds this byte-identically and 2x faster Bun 1.4 is worth a real answer rather than a guess, so `npm run bun:check` measures one instead. Same idiom as `kitesurf:check`: three questions, run when a bun release lands, not in CI. Measured 2026-08-10, node v26.7.0 against bun 1.4.0-canary.1+827475e21: ok zstd honours `dictionary` — 73 none / 24 good / 73 wrong ok build output is byte-identical — 1975 files, node 14.4s vs bun 7.1s ok contract suite passes under bun — 206 pass, 0 fail BYTE-IDENTICAL is the bar, and it is a lot higher than "the build succeeds". /a/ and /i/ are content-addressed, so one differing byte mints a new URL, orphans every a-dict snapshot naming the old hash, and moves the CSP hashes the documents are served under. Not adopting it, for three reasons and only the first is about bun. The newest STABLE bun is 1.3.14, which predates the dictionary fix (oven-sh/bun#34427) and silently ignores `zstdCompressSync`'s `dictionary`. wrangler, miniflare and workerd are the deploy path and the route oracle, node-pinned. And the win is seconds on a step CI already spends longer on in dry-runs. The failure would be LOUD, which the check now says out loud: build.mjs already feature-detects the same collapse and throws, so 1.3.14 kills the build rather than shipping no-op deltas. The engine is silent; this build is not. Verified in both directions — canary all green, 1.3.14 fails at question 1 before spending two builds, and the tree comes back clean either way. Also fixes a real defect bun found in our own suite. `withSecurityHeaders` rebuilds every response as `new Response(response.body, …)`, which per Fetch LOCKS the body, and one contract test pushed the same four case objects through it twice. Bun throws `Body object should not be disturbed or locked`; node's undici allows it. The assertions are about headers, so the leniency was never load-bearing — it just made the suite depend on which runtime ran it. Cases are built fresh per pass now, and the test passes on both. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * docs: gotcha 27 gains its cleanest control, a prose-only PR failing it Two more instances on 2026-08-10 (#305, #307), so four across four unrelated PRs, every one alongside a CodeQL pass. #305 is what makes this worth writing down rather than just recounting. It changed documentation, four code COMMENTS and one quiz string, and failed the check identically to a build-script PR, with `title: null` and a lone annotation at `.github:211`. A diff with no executable change cannot carry a security finding, so a check that reddens on it is reporting on itself. That is a sharper discriminator than the API tell when the API tell is ambiguous. Also records where to confirm the stakes: the ruleset, not the check list. `validate` is the only required context, so this has been red on four PRs while gating none of them. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> --------- Co-authored-by: Aadharsh Pannirselvam <19518661+oddharsh@users.noreply.github.com> Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Gotcha 14 said
node:zlib's brotli has NO dictionary parameter, listed the nine params as the whole list, and rested part of the dcz-over-dcb case on dcb needing thebrotliCLI back in the build path. That expired.Node 26 takes a
dictionaryoption (nodejs/node#61763, merged 2026-02-13), and.node-versionis already 26, so CI has it too. Measured on v26.7.0, q11 over a 21KB target:The conclusion is untouched. dcz won on decode, and neither encoder's availability was ever the axis. One of the stated reasons is what changed.
Four copies moved together
Gotcha 18's own lesson is to grep the tree when a note lands, since writing it down once left three other instances live for two days. So: CLAUDE.md twice, the
build.mjscomment at the delta emitter, theroll-shell-dictionary.mjsheader, and/garage/compression, which carried it in prose and in an understanding-check explanation.The page's table was stale in a second, bigger way
Its dcz-vs-dcb table still held the pre-correction figures (79 vs 80 bytes, "about twice as fast"). The 2026-07-28 re-measure against real dictionaries never reached the page:
Both inputs to the original argument were wrong, in opposite directions, and the call survived on the structural point instead: decode scales with the RECONSTRUCTION rather than the delta, so a smaller dcb delta never shrinks the decode gap. The quiz's correct answer restated the old figures verbatim and now states that.
One new rule
Three runtimes disagree on this single option, so: probe dictionary support, never infer it. node 26 honours it, workerd accepts it for zstd and silently ignores it, and bun does the same through 1.3.14 with the fix landing in oven-sh/bun#34427 (unreleased as of 1.3.14; verified here on
1.4.0-canary.1). The failure is silent every time, because a frame compressed WITHOUT the dictionary still decodes fine WITH it, so the only signal is a byte count that never shrank. The four-line control is in the note.Verification
npm test206/206,npm run buildclean (134 page deltas, 29 quiz payloads byte-equal),npm run pages:checkok,npm run perf-budgetgreen. The.mdtwin regenerates from source, so no hand-edited twin.🤖 Generated with Claude Code