diff options
Diffstat (limited to 'CLAUDE.md')
| -rw-r--r-- | CLAUDE.md | 41 |
1 files changed, 41 insertions, 0 deletions
@@ -511,6 +511,47 @@ anything that assumes one key per person. Nothing errored. When a chain degrades, check what the floor costs on every platform that will reach it +- **A service worker with nothing to do is killed, and a streaming response is + not "something to do".** Firefox terminates an idle worker after about thirty + seconds; `event.respondWith(new Response(stream))` does not extend its life + while the page is still writing to that stream. So the reader vanished + mid-file and `writable.write()` **never resolved and never rejected** — no + error, no log, no failed transfer, just a progress bar that stopped near the + end. Measured 2026-09-08 in Firefox 154, writing 1 MB every 2 s: stalled at + 17 MB after 59 s; with a 10-second ping to the worker, 40 MB in 80 s, + complete. The page pings while it writes and the worker answers, because + receiving a message is the event that resets the timer. + Three things this cost, all worth remembering. **The first stress probe wrote + 450 MB in two seconds and passed** — fast enough to hide the bug entirely, so + a probe for anything time-based has to be paced like the real thing. **The + node was innocent and three measurements proved it** (615 MB pulled whole + over MNP, three files interleaved on one connection, three concurrent worker + streams), which is exactly what made the fault unfindable: nothing was wrong + anywhere anyone looked. And **the empty console was the evidence**, not the + absence of it: `_sendAndWait` logs every timeout, so silence eliminated + everything that reports itself and left the one `await` on that path with no + bound. Every await on a download path is now bounded and says which chunk it + gave up on — an unbounded one is a freeze nobody can report + +- **Three headers decide whether a page may frame itself, and they must agree.** + The same streamed download navigates a hidden iframe to `/_mbdl/<id>`. + `frame-src` was reCAPTCHA's two origins with no `'self'`, `frame-ancestors` + was `'none'`, and `X-Frame-Options` was `DENY`. Each was fixed in turn, each + time costing a redeploy and a retest, and **all three were visible in one + `curl -I` against the deployed hub** — which is where that should have + started. `'self'`/`SAMEORIGIN` refuse every foreign origin exactly as + `'none'`/`DENY` do; what they add is this origin framing itself, which is all + the download needed. When a symptom points at a mechanism, enumerate + everything that governs that mechanism and check the set at once + +- **`encodeURIComponent` does not escape `'`, and `'` is RFC 5987's delimiter.** + `Content-Disposition: filename*=UTF-8''<value>` became unparseable for any + name with an apostrophe, so the browser named the file after the URL: 449 MB + of film arrived complete and correct, called `mtsshk9w-ohqty535`. `(`, `)` and + `*` are excluded from attr-char for the same reason. A plain ASCII + `filename=` rides alongside now, so the next surprise loses accents rather + than the name + - **Two elements each claiming `100vh - 52px`, one inside the other's padding.** `.layout` and `.page-center` both reserved the viewport below the header, and `main`'s `24px` top and bottom were added on top — a permanent 48px scrollbar |