[{"data":1,"prerenderedAt":1338},["ShallowReactive",2],{"work-\u002Fwork\u002Felectron-webfuse-repro-harness":3},{"id":4,"title":5,"body":6,"date":1320,"description":1321,"extension":1322,"externalUrl":1323,"featured":1324,"kind":1325,"meta":1326,"navigation":1327,"path":1328,"seo":1329,"stem":1330,"tags":1331,"__hash__":1337},"work\u002Fwork\u002Felectron-webfuse-repro-harness.md","Works in Chrome, breaks in Electron: bisecting a customer bug with a toggle harness",{"type":7,"value":8,"toc":1314},"minimark",[9,13,25,30,33,50,67,71,82,102,220,233,240,644,666,825,842,899,916,1257,1261,1303,1307,1310],[10,11,5],"h1",{"id":12},"works-in-chrome-breaks-in-electron-bisecting-a-customer-bug-with-a-toggle-harness",[14,15,16,17,24],"p",{},"A customer embedded ",[18,19,23],"a",{"href":20,"rel":21},"https:\u002F\u002Fwww.webfuse.com",[22],"nofollow","Webfuse"," inside their own ElectronJS wrapper and hit two bugs that never appeared in plain Chrome. This is the diagnostic instrument I built to answer them: a single-directory Electron app where every environmental variable that could differ between \"plain Chrome\" (works) and \"the customer's wrapper\" (breaks) is a toggle, with timestamped logging of the service-worker, navigation, console, and renderer-crash lifecycle. It is not a product. It exists to produce a defensible answer for a support thread, one flipped variable at a time.",[26,27,29],"h2",{"id":28},"the-problem","The problem",[14,31,32],{},"Two symptoms, reported only inside the Electron wrapper:",[34,35,36,44],"ol",{},[37,38,39,43],"li",{},[40,41,42],"strong",{},"Reload locked."," Once a Webfuse session loaded, the page could not be refreshed. Even Electron's native View -> Reload did nothing.",[37,45,46,49],{},[40,47,48],{},"Grey screen."," Certain sites - the customer's example was a public game site - rendered a grey screen with the session controls gone, while the customer's own site loaded fine.",[14,51,52,53,57,58,61,62,66],{},"\"Works in Chrome, breaks in Electron\" is a large search space: load scheme (https vs ",[54,55,56],"code",{},"file:\u002F\u002F"," vs custom protocol), session partition, ",[54,59,60],{},"webSecurity",", custom-protocol privileges, Node integration flags, and the Electron\u002FChromium version itself. The naive move - read the description, guess a cause, advise the customer - fails here for a specific reason. The two symptoms had two ",[63,64,65],"em",{},"independent"," root causes, and the second was not guessable from any code. It only appeared once the harness matched the customer's exact bundled Chromium version. Two plausible hypotheses were confirmed wrong only by trying to reproduce them.",[26,68,70],{"id":69},"the-design","The design",[14,72,73,74,77,78,81],{},"One ",[54,75,76],{},"main.js"," main process. Every suspect is an environment variable with a Chrome-like default, so a single run isolates exactly one variable against an otherwise identical baseline. ",[54,79,80],{},"package.json"," scripts name the interesting configurations. The investigation escalated fidelity in stages until the bug reproduced.",[14,83,84,87,88,90,91,94,95,94,98,101],{},[40,85,86],{},"Toggles, not branches."," Load mode, partition, ",[54,89,60],{},", protocol privilege, ",[54,92,93],{},"nodeIntegration",", ",[54,96,97],{},"nodeIntegrationInSubFrames",[54,99,100],{},"sandbox",", Electron version - each is one env var defaulting to the safe value. The alternative, a throwaway repro per hypothesis, loses the ability to A\u002FB one flag against a fixed baseline. That A\u002FB is the whole point of the instrument.",[103,104,109],"pre",{"className":105,"code":106,"language":107,"meta":108,"style":108},"language-js shiki shiki-themes material-theme-lighter github-light github-dark","webPreferences: {\n  session: ses,\n  webSecurity: WEB_SECURITY,\n  contextIsolation: true,\n  nodeIntegration: NODE_INTEGRATION,\n  nodeIntegrationInSubFrames: NODE_IN_SUBFRAMES,   \u002F\u002F injects Node into every iframe\n  sandbox: SANDBOX,\n},\n","js","",[54,110,111,127,142,156,170,183,201,214],{"__ignoreMap":108},[112,113,116,120,124],"span",{"class":114,"line":115},"line",1,[112,117,119],{"class":118},"sbgvK","webPreferences",[112,121,123],{"class":122},"sP7_E",":",[112,125,126],{"class":122}," {\n",[112,128,130,133,135,139],{"class":114,"line":129},2,[112,131,132],{"class":118},"  session",[112,134,123],{"class":122},[112,136,138],{"class":137},"su5hD"," ses",[112,140,141],{"class":122},",\n",[112,143,145,148,150,154],{"class":114,"line":144},3,[112,146,147],{"class":118},"  webSecurity",[112,149,123],{"class":122},[112,151,153],{"class":152},"s_hVV"," WEB_SECURITY",[112,155,141],{"class":122},[112,157,159,162,164,168],{"class":114,"line":158},4,[112,160,161],{"class":118},"  contextIsolation",[112,163,123],{"class":122},[112,165,167],{"class":166},"syTEX"," true",[112,169,141],{"class":122},[112,171,173,176,178,181],{"class":114,"line":172},5,[112,174,175],{"class":118},"  nodeIntegration",[112,177,123],{"class":122},[112,179,180],{"class":152}," NODE_INTEGRATION",[112,182,141],{"class":122},[112,184,186,189,191,194,197],{"class":114,"line":185},6,[112,187,188],{"class":118},"  nodeIntegrationInSubFrames",[112,190,123],{"class":122},[112,192,193],{"class":152}," NODE_IN_SUBFRAMES",[112,195,196],{"class":122},",",[112,198,200],{"class":199},"sutJx","   \u002F\u002F injects Node into every iframe\n",[112,202,204,207,209,212],{"class":114,"line":203},7,[112,205,206],{"class":118},"  sandbox",[112,208,123],{"class":122},[112,210,211],{"class":152}," SANDBOX",[112,213,141],{"class":122},[112,215,217],{"class":114,"line":216},8,[112,218,219],{"class":122},"},\n",[14,221,222,225,226,228,229,232],{},[40,223,224],{},"Reproduce before advising, and verify the fix by toggling it."," An early, plausible hypothesis was that ",[54,227,97],{}," injected Node globals into the Webfuse subframe. I abandoned it when flipping the supposed fix on and off did not change the symptom. The ",[54,230,231],{},"global"," seen in the Webfuse frame was Webfuse's own webpack polyfill, present regardless. The harness's ability to A\u002FB a flag is exactly what caught the false positive - a hypothesis that survives only because you never tested the off state is not a diagnosis.",[14,234,235,236,239],{},"To see whether Node globals actually reached the cross-origin Webfuse iframe - invisible to page-world JS - the probe walks the whole frame tree via ",[54,237,238],{},"WebFrameMain",", which can execute across origins:",[103,241,243],{"className":105,"code":242,"language":107,"meta":108,"style":108},"async function probeAllFrames(wc) {\n  const walk = (f) => (f ? [f, ...f.frames.flatMap(walk)] : []);\n  const frames = walk(wc.mainFrame);\n  for (const f of frames) {\n    const r = await f.executeJavaScript(\n      `({g: typeof global, r: typeof require, m: typeof module, p: typeof process})`, true);\n    const leaked = Object.entries(r).filter(([, v]) => v !== 'undefined').map(([k]) => k).join(',') || 'none';\n    log('PROBE frame', `leaked:[${leaked}]  ${(f.url || '').slice(0, 70)}`);\n    \u002F* real Node globals (require\u002Fmodule\u002Fprocess) never leaked; only `global`, which is\n       Webfuse's own webpack polyfill - present regardless of nodeIntegrationInSubFrames *\u002F\n  }\n}\n",[54,244,245,270,341,365,390,413,434,541,620,626,632,638],{"__ignoreMap":108},[112,246,247,251,254,258,261,265,268],{"class":114,"line":115},[112,248,250],{"class":249},"sbsja","async",[112,252,253],{"class":249}," function",[112,255,257],{"class":256},"sGLFI"," probeAllFrames",[112,259,260],{"class":122},"(",[112,262,264],{"class":263},"s99_P","wc",[112,266,267],{"class":122},")",[112,269,126],{"class":122},[112,271,272,275,279,283,286,289,291,294,297,299,302,305,307,309,312,314,317,320,322,325,327,330,333,335,338],{"class":114,"line":129},[112,273,274],{"class":249},"  const",[112,276,278],{"class":277},"sfCm-"," walk",[112,280,282],{"class":281},"smGrS"," =",[112,284,285],{"class":122}," (",[112,287,288],{"class":263},"f",[112,290,267],{"class":122},[112,292,293],{"class":249}," =>",[112,295,285],{"class":296},"skxfh",[112,298,288],{"class":137},[112,300,301],{"class":281}," ?",[112,303,304],{"class":296}," [",[112,306,288],{"class":137},[112,308,196],{"class":122},[112,310,311],{"class":281}," ...",[112,313,288],{"class":137},[112,315,316],{"class":122},".",[112,318,319],{"class":137},"frames",[112,321,316],{"class":122},[112,323,324],{"class":256},"flatMap",[112,326,260],{"class":296},[112,328,329],{"class":137},"walk",[112,331,332],{"class":296},")] ",[112,334,123],{"class":281},[112,336,337],{"class":296}," [])",[112,339,340],{"class":122},";\n",[112,342,343,345,348,350,352,354,356,358,361,363],{"class":114,"line":144},[112,344,274],{"class":249},[112,346,347],{"class":152}," frames",[112,349,282],{"class":281},[112,351,278],{"class":256},[112,353,260],{"class":296},[112,355,264],{"class":137},[112,357,316],{"class":122},[112,359,360],{"class":137},"mainFrame",[112,362,267],{"class":296},[112,364,340],{"class":122},[112,366,367,371,373,376,379,382,384,387],{"class":114,"line":158},[112,368,370],{"class":369},"sVHd0","  for",[112,372,285],{"class":296},[112,374,375],{"class":249},"const",[112,377,378],{"class":152}," f",[112,380,381],{"class":281}," of",[112,383,347],{"class":137},[112,385,386],{"class":296},") ",[112,388,389],{"class":122},"{\n",[112,391,392,395,398,400,403,405,407,410],{"class":114,"line":172},[112,393,394],{"class":249},"    const",[112,396,397],{"class":152}," r",[112,399,282],{"class":281},[112,401,402],{"class":369}," await",[112,404,378],{"class":137},[112,406,316],{"class":122},[112,408,409],{"class":256},"executeJavaScript",[112,411,412],{"class":296},"(\n",[112,414,415,419,423,426,428,430,432],{"class":114,"line":185},[112,416,418],{"class":417},"sjJ54","      `",[112,420,422],{"class":421},"s_sjI","({g: typeof global, r: typeof require, m: typeof module, p: typeof process})",[112,424,425],{"class":417},"`",[112,427,196],{"class":122},[112,429,167],{"class":166},[112,431,267],{"class":296},[112,433,340],{"class":122},[112,435,436,438,441,443,446,448,451,453,456,458,460,463,465,468,471,474,476,478,481,484,487,490,492,494,497,499,502,505,507,509,512,514,516,519,521,523,525,527,529,532,534,537,539],{"class":114,"line":203},[112,437,394],{"class":249},[112,439,440],{"class":152}," leaked",[112,442,282],{"class":281},[112,444,445],{"class":137}," Object",[112,447,316],{"class":122},[112,449,450],{"class":256},"entries",[112,452,260],{"class":296},[112,454,455],{"class":137},"r",[112,457,267],{"class":296},[112,459,316],{"class":122},[112,461,462],{"class":256},"filter",[112,464,260],{"class":296},[112,466,467],{"class":122},"([,",[112,469,470],{"class":263}," v",[112,472,473],{"class":122},"])",[112,475,293],{"class":249},[112,477,470],{"class":137},[112,479,480],{"class":281}," !==",[112,482,483],{"class":417}," '",[112,485,486],{"class":421},"undefined",[112,488,489],{"class":417},"'",[112,491,267],{"class":296},[112,493,316],{"class":122},[112,495,496],{"class":256},"map",[112,498,260],{"class":296},[112,500,501],{"class":122},"([",[112,503,504],{"class":263},"k",[112,506,473],{"class":122},[112,508,293],{"class":249},[112,510,511],{"class":137}," k",[112,513,267],{"class":296},[112,515,316],{"class":122},[112,517,518],{"class":256},"join",[112,520,260],{"class":296},[112,522,489],{"class":417},[112,524,196],{"class":421},[112,526,489],{"class":417},[112,528,386],{"class":296},[112,530,531],{"class":281},"||",[112,533,483],{"class":417},[112,535,536],{"class":421},"none",[112,538,489],{"class":417},[112,540,340],{"class":122},[112,542,543,546,548,550,553,555,557,560,563,566,569,572,575,577,580,582,584,587,590,593,595,597,600,602,606,608,611,613,616,618],{"class":114,"line":216},[112,544,545],{"class":256},"    log",[112,547,260],{"class":296},[112,549,489],{"class":417},[112,551,552],{"class":421},"PROBE frame",[112,554,489],{"class":417},[112,556,196],{"class":122},[112,558,559],{"class":417}," `",[112,561,562],{"class":421},"leaked:[",[112,564,565],{"class":417},"${",[112,567,568],{"class":137},"leaked",[112,570,571],{"class":417},"}",[112,573,574],{"class":421},"]  ",[112,576,565],{"class":417},[112,578,260],{"class":579},"sfo-9",[112,581,288],{"class":137},[112,583,316],{"class":417},[112,585,586],{"class":137},"url",[112,588,589],{"class":281}," ||",[112,591,592],{"class":417}," ''",[112,594,267],{"class":579},[112,596,316],{"class":417},[112,598,599],{"class":256},"slice",[112,601,260],{"class":579},[112,603,605],{"class":604},"srdBf","0",[112,607,196],{"class":417},[112,609,610],{"class":604}," 70",[112,612,267],{"class":579},[112,614,615],{"class":417},"}`",[112,617,267],{"class":296},[112,619,340],{"class":122},[112,621,623],{"class":114,"line":622},9,[112,624,625],{"class":199},"    \u002F* real Node globals (require\u002Fmodule\u002Fprocess) never leaked; only `global`, which is\n",[112,627,629],{"class":114,"line":628},10,[112,630,631],{"class":199},"       Webfuse's own webpack polyfill - present regardless of nodeIntegrationInSubFrames *\u002F\n",[112,633,635],{"class":114,"line":634},11,[112,636,637],{"class":122},"  }\n",[112,639,641],{"class":114,"line":640},12,[112,642,643],{"class":122},"}\n",[14,645,646,649,650,653,654,657,658,661,662,665],{},[40,647,648],{},"Instrumentation reveals mechanism, not just failure."," The reload lock was diagnosed from a ",[63,651,652],{},"negative"," signal: after a reload command, no ",[54,655,656],{},"did-start-navigation"," fired. That ruled out the service-worker theory - a SW intercept still starts navigation - and pointed at Electron's ",[54,659,660],{},"will-prevent-unload",", which silently cancels navigation when the page has a ",[54,663,664],{},"beforeunload"," handler. Webfuse installs one. In Chrome that produces a \"Leave site?\" dialog; in Electron the default is to swallow the reload with no signal at all.",[103,667,669],{"className":105,"code":668,"language":107,"meta":108,"style":108},"\u002F\u002F Webfuse installs a beforeunload handler. In Chrome that produces a \"Leave site?\"\n\u002F\u002F dialog; in Electron the DEFAULT is to silently CANCEL the navigation (reload does\n\u002F\u002F nothing, no did-start fires) unless you handle will-prevent-unload.\nwc.on('will-prevent-unload', (event) => {\n  if (process.env.ALLOW_UNLOAD === '1') {\n    log('!!! will-prevent-unload fired → ALLOW_UNLOAD=1, overriding to ALLOW the reload');\n    event.preventDefault();               \u002F\u002F let the reload\u002Fnavigation proceed\n  } else {\n    log('!!! will-prevent-unload fired → Electron default = CANCEL navigation (reload swallowed)');\n  }\n});\n",[54,670,671,676,681,686,716,750,767,786,796,813,817],{"__ignoreMap":108},[112,672,673],{"class":114,"line":115},[112,674,675],{"class":199},"\u002F\u002F Webfuse installs a beforeunload handler. In Chrome that produces a \"Leave site?\"\n",[112,677,678],{"class":114,"line":129},[112,679,680],{"class":199},"\u002F\u002F dialog; in Electron the DEFAULT is to silently CANCEL the navigation (reload does\n",[112,682,683],{"class":114,"line":144},[112,684,685],{"class":199},"\u002F\u002F nothing, no did-start fires) unless you handle will-prevent-unload.\n",[112,687,688,690,692,695,697,699,701,703,705,707,710,712,714],{"class":114,"line":158},[112,689,264],{"class":137},[112,691,316],{"class":122},[112,693,694],{"class":256},"on",[112,696,260],{"class":137},[112,698,489],{"class":417},[112,700,660],{"class":421},[112,702,489],{"class":417},[112,704,196],{"class":122},[112,706,285],{"class":122},[112,708,709],{"class":263},"event",[112,711,267],{"class":122},[112,713,293],{"class":249},[112,715,126],{"class":122},[112,717,718,721,723,726,728,731,733,736,739,741,744,746,748],{"class":114,"line":172},[112,719,720],{"class":369},"  if",[112,722,285],{"class":296},[112,724,725],{"class":137},"process",[112,727,316],{"class":122},[112,729,730],{"class":137},"env",[112,732,316],{"class":122},[112,734,735],{"class":152},"ALLOW_UNLOAD",[112,737,738],{"class":281}," ===",[112,740,483],{"class":417},[112,742,743],{"class":421},"1",[112,745,489],{"class":417},[112,747,386],{"class":296},[112,749,389],{"class":122},[112,751,752,754,756,758,761,763,765],{"class":114,"line":185},[112,753,545],{"class":256},[112,755,260],{"class":296},[112,757,489],{"class":417},[112,759,760],{"class":421},"!!! will-prevent-unload fired → ALLOW_UNLOAD=1, overriding to ALLOW the reload",[112,762,489],{"class":417},[112,764,267],{"class":296},[112,766,340],{"class":122},[112,768,769,772,774,777,780,783],{"class":114,"line":203},[112,770,771],{"class":137},"    event",[112,773,316],{"class":122},[112,775,776],{"class":256},"preventDefault",[112,778,779],{"class":296},"()",[112,781,782],{"class":122},";",[112,784,785],{"class":199},"               \u002F\u002F let the reload\u002Fnavigation proceed\n",[112,787,788,791,794],{"class":114,"line":216},[112,789,790],{"class":122},"  }",[112,792,793],{"class":369}," else",[112,795,126],{"class":122},[112,797,798,800,802,804,807,809,811],{"class":114,"line":622},[112,799,545],{"class":256},[112,801,260],{"class":296},[112,803,489],{"class":417},[112,805,806],{"class":421},"!!! will-prevent-unload fired → Electron default = CANCEL navigation (reload swallowed)",[112,808,489],{"class":417},[112,810,267],{"class":296},[112,812,340],{"class":122},[112,814,815],{"class":114,"line":628},[112,816,637],{"class":122},[112,818,819,821,823],{"class":114,"line":634},[112,820,571],{"class":122},[112,822,267],{"class":137},[112,824,340],{"class":122},[14,826,827,830,831,834,835,837,838,841],{},[40,828,829],{},"Faithful architecture mirroring, escalated in steps."," The first mode ran Webfuse as the top frame over https. No grey screen. So I added a ",[54,832,833],{},"localhost"," mode: a top-level page over a real secure origin embeds Webfuse in an iframe, matching the customer's \"React app -> Webfuse iframe via magic link\" exactly, so ",[54,836,97],{}," actually applies to the Webfuse frame. Still no repro. The last untested variable was the Electron version itself. A side-by-side Electron 31.3.1 install - Chromium 126, the customer's version - reproduced the grey screen that Electron 42 (Chromium 148) did not. ",[54,839,840],{},"bisect.sh"," runs the repro against any Electron release to pin where the fix landed:",[103,843,847],{"className":844,"code":845,"language":846,"meta":108,"style":108},"language-sh shiki shiki-themes material-theme-lighter github-light github-dark","# Usage: .\u002Fbisect.sh 37   (broken on 31\u002FChromium126, works on 42\u002FChromium148)\nV=\"${1:?usage: .\u002Fbisect.sh \u003Celectron-version>}\"\nDIR=\".ev\u002F$V\"\nif [ ! -x \"$DIR\u002Fnode_modules\u002F.bin\u002Felectron\" ]; then\n  printf '{\"devDependencies\":{\"electron\":\"%s\"},\"trustedDependencies\":[\"electron\"]}\\n' \"$V\" > \"$DIR\u002Fpackage.json\"\n  ( cd \"$DIR\" && bun install )\n  [ -d \"$DIR\u002Fnode_modules\u002Felectron\u002Fdist\" ] || node \"$DIR\u002Fnode_modules\u002Felectron\u002Finstall.js\"\nfi\nLOAD_MODE=localhost NODE_INTEGRATION=1 NODE_IN_SUBFRAMES=1 SANDBOX=off ALLOW_UNLOAD=1 \\\n  \"$DIR\u002Fnode_modules\u002F.bin\u002Felectron\" .\n","sh",[54,848,849,854,859,864,869,874,879,884,889,894],{"__ignoreMap":108},[112,850,851],{"class":114,"line":115},[112,852,853],{},"# Usage: .\u002Fbisect.sh 37   (broken on 31\u002FChromium126, works on 42\u002FChromium148)\n",[112,855,856],{"class":114,"line":129},[112,857,858],{},"V=\"${1:?usage: .\u002Fbisect.sh \u003Celectron-version>}\"\n",[112,860,861],{"class":114,"line":144},[112,862,863],{},"DIR=\".ev\u002F$V\"\n",[112,865,866],{"class":114,"line":158},[112,867,868],{},"if [ ! -x \"$DIR\u002Fnode_modules\u002F.bin\u002Felectron\" ]; then\n",[112,870,871],{"class":114,"line":172},[112,872,873],{},"  printf '{\"devDependencies\":{\"electron\":\"%s\"},\"trustedDependencies\":[\"electron\"]}\\n' \"$V\" > \"$DIR\u002Fpackage.json\"\n",[112,875,876],{"class":114,"line":185},[112,877,878],{},"  ( cd \"$DIR\" && bun install )\n",[112,880,881],{"class":114,"line":203},[112,882,883],{},"  [ -d \"$DIR\u002Fnode_modules\u002Felectron\u002Fdist\" ] || node \"$DIR\u002Fnode_modules\u002Felectron\u002Finstall.js\"\n",[112,885,886],{"class":114,"line":216},[112,887,888],{},"fi\n",[112,890,891],{"class":114,"line":622},[112,892,893],{},"LOAD_MODE=localhost NODE_INTEGRATION=1 NODE_IN_SUBFRAMES=1 SANDBOX=off ALLOW_UNLOAD=1 \\\n",[112,895,896],{"class":114,"line":628},[112,897,898],{},"  \"$DIR\u002Fnode_modules\u002F.bin\u002Felectron\" .\n",[14,900,901,902,905,906,909,910,913,914,123],{},"A few ergonomics kept the loop tight. Magic links are minted fresh on every launch - no REST call, no mid-test expiry. A smoke path (",[54,903,904],{},"AUTO_QUIT_MS",") allows headless boot checks with no window left on screen. And a hand-rolled ",[54,907,908],{},".env"," loader was needed because the bun-launched ",[54,911,912],{},"electron"," child did not inherit bun's auto-loaded ",[54,915,908],{},[103,917,919],{"className":105,"code":918,"language":107,"meta":108,"style":108},"function loadDotEnv() {\n  const raw = fs.readFileSync(path.join(__dirname, '.env'), 'utf8');\n  for (const line of raw.split('\\n')) {\n    const m = line.match(\u002F^\\s*([\\w.]+)\\s*=\\s*(.*)\\s*$\u002F);\n    if (!m || line.trim().startsWith('#')) continue;\n    if (process.env[m[1]] === undefined) process.env[m[1]] = m[2].replace(\u002F^['\"]|['\"]$\u002Fg, '');\n  }\n}\n",[54,920,921,933,987,1021,1103,1148,1249,1253],{"__ignoreMap":108},[112,922,923,926,929,931],{"class":114,"line":115},[112,924,925],{"class":249},"function",[112,927,928],{"class":256}," loadDotEnv",[112,930,779],{"class":122},[112,932,126],{"class":122},[112,934,935,937,940,942,945,947,950,952,955,957,959,961,964,966,968,970,972,974,976,978,981,983,985],{"class":114,"line":129},[112,936,274],{"class":249},[112,938,939],{"class":152}," raw",[112,941,282],{"class":281},[112,943,944],{"class":137}," fs",[112,946,316],{"class":122},[112,948,949],{"class":256},"readFileSync",[112,951,260],{"class":296},[112,953,954],{"class":137},"path",[112,956,316],{"class":122},[112,958,518],{"class":256},[112,960,260],{"class":296},[112,962,963],{"class":137},"__dirname",[112,965,196],{"class":122},[112,967,483],{"class":417},[112,969,908],{"class":421},[112,971,489],{"class":417},[112,973,267],{"class":296},[112,975,196],{"class":122},[112,977,483],{"class":417},[112,979,980],{"class":421},"utf8",[112,982,489],{"class":417},[112,984,267],{"class":296},[112,986,340],{"class":122},[112,988,989,991,993,995,998,1000,1002,1004,1007,1009,1011,1014,1016,1019],{"class":114,"line":144},[112,990,370],{"class":369},[112,992,285],{"class":296},[112,994,375],{"class":249},[112,996,997],{"class":152}," line",[112,999,381],{"class":281},[112,1001,939],{"class":137},[112,1003,316],{"class":122},[112,1005,1006],{"class":256},"split",[112,1008,260],{"class":296},[112,1010,489],{"class":417},[112,1012,1013],{"class":152},"\\n",[112,1015,489],{"class":417},[112,1017,1018],{"class":296},")) ",[112,1020,389],{"class":122},[112,1022,1023,1025,1028,1030,1032,1034,1037,1039,1042,1045,1049,1052,1055,1059,1062,1065,1068,1070,1072,1074,1078,1080,1082,1084,1086,1088,1090,1092,1094,1097,1099,1101],{"class":114,"line":158},[112,1024,394],{"class":249},[112,1026,1027],{"class":152}," m",[112,1029,282],{"class":281},[112,1031,997],{"class":137},[112,1033,316],{"class":122},[112,1035,1036],{"class":256},"match",[112,1038,260],{"class":296},[112,1040,1041],{"class":417},"\u002F",[112,1043,1044],{"class":369},"^",[112,1046,1048],{"class":1047},"stzsN","\\s",[112,1050,1051],{"class":281},"*",[112,1053,260],{"class":1054},"s-KJb",[112,1056,1058],{"class":1057},"s39Yj","[",[112,1060,1061],{"class":1047},"\\w.",[112,1063,1064],{"class":1057},"]",[112,1066,1067],{"class":281},"+",[112,1069,267],{"class":1054},[112,1071,1048],{"class":1047},[112,1073,1051],{"class":281},[112,1075,1077],{"class":1076},"sQRbd","=",[112,1079,1048],{"class":1047},[112,1081,1051],{"class":281},[112,1083,260],{"class":1054},[112,1085,316],{"class":1047},[112,1087,1051],{"class":281},[112,1089,267],{"class":1054},[112,1091,1048],{"class":1047},[112,1093,1051],{"class":281},[112,1095,1096],{"class":369},"$",[112,1098,1041],{"class":417},[112,1100,267],{"class":296},[112,1102,340],{"class":122},[112,1104,1105,1108,1110,1113,1116,1118,1120,1122,1125,1127,1129,1132,1134,1136,1139,1141,1143,1146],{"class":114,"line":172},[112,1106,1107],{"class":369},"    if",[112,1109,285],{"class":296},[112,1111,1112],{"class":281},"!",[112,1114,1115],{"class":137},"m",[112,1117,589],{"class":281},[112,1119,997],{"class":137},[112,1121,316],{"class":122},[112,1123,1124],{"class":256},"trim",[112,1126,779],{"class":296},[112,1128,316],{"class":122},[112,1130,1131],{"class":256},"startsWith",[112,1133,260],{"class":296},[112,1135,489],{"class":417},[112,1137,1138],{"class":421},"#",[112,1140,489],{"class":417},[112,1142,1018],{"class":296},[112,1144,1145],{"class":369},"continue",[112,1147,340],{"class":122},[112,1149,1150,1152,1154,1156,1158,1160,1162,1164,1166,1168,1171,1174,1177,1179,1181,1183,1185,1187,1189,1191,1193,1195,1197,1199,1201,1204,1206,1208,1211,1213,1215,1217,1219,1222,1224,1227,1229,1231,1233,1235,1237,1241,1243,1245,1247],{"class":114,"line":185},[112,1151,1107],{"class":369},[112,1153,285],{"class":296},[112,1155,725],{"class":137},[112,1157,316],{"class":122},[112,1159,730],{"class":137},[112,1161,1058],{"class":296},[112,1163,1115],{"class":137},[112,1165,1058],{"class":296},[112,1167,743],{"class":604},[112,1169,1170],{"class":296},"]] ",[112,1172,1173],{"class":281},"===",[112,1175,1176],{"class":1057}," undefined",[112,1178,386],{"class":296},[112,1180,725],{"class":137},[112,1182,316],{"class":122},[112,1184,730],{"class":137},[112,1186,1058],{"class":296},[112,1188,1115],{"class":137},[112,1190,1058],{"class":296},[112,1192,743],{"class":604},[112,1194,1170],{"class":296},[112,1196,1077],{"class":281},[112,1198,1027],{"class":137},[112,1200,1058],{"class":296},[112,1202,1203],{"class":604},"2",[112,1205,1064],{"class":296},[112,1207,316],{"class":122},[112,1209,1210],{"class":256},"replace",[112,1212,260],{"class":296},[112,1214,1041],{"class":417},[112,1216,1044],{"class":369},[112,1218,1058],{"class":1057},[112,1220,1221],{"class":1047},"'\"",[112,1223,1064],{"class":1057},[112,1225,1226],{"class":281},"|",[112,1228,1058],{"class":1057},[112,1230,1221],{"class":1047},[112,1232,1064],{"class":1057},[112,1234,1096],{"class":369},[112,1236,1041],{"class":417},[112,1238,1240],{"class":1239},"sw1J6","g",[112,1242,196],{"class":122},[112,1244,592],{"class":417},[112,1246,267],{"class":296},[112,1248,340],{"class":122},[112,1250,1251],{"class":114,"line":203},[112,1252,637],{"class":122},[112,1254,1255],{"class":114,"line":216},[112,1256,643],{"class":122},[26,1258,1260],{"id":1259},"results","Results",[1262,1263,1264,1277,1287,1296],"ul",{},[37,1265,1266,1269,1270,1272,1273,1276],{},[40,1267,1268],{},"Reload lock:"," root-caused to Electron's default ",[54,1271,660],{}," behavior. One-line fix - ",[54,1274,1275],{},"event.preventDefault()",". The customer confirmed it \"works perfectly.\"",[37,1278,1279,1282,1283,1286],{},[40,1280,1281],{},"Grey screen:"," root-caused to a ",[40,1284,1285],{},"Chromium version bug"," - reproduced on Electron 31.3.1 \u002F Chromium 126, gone on Electron 42 \u002F Chromium 148. Fix: upgrade Electron. Confirmed by reproducing in both directions in the harness.",[37,1288,1289,1292,1293,1295],{},[40,1290,1291],{},"Two false hypotheses ruled out with evidence"," before landing on the real cause: a secure-context \u002F service-worker failure (the customer's context was secure), and ",[54,1294,97],{}," Node injection (disproven by the frame probe and a failed fix toggle).",[37,1297,1298,1299,1302],{},"Harness fidelity escalated through ",[40,1300,1301],{},"three stages"," - direct https, then localhost + iframe, then a matched Electron version - before the grey screen reproduced. The diagnostic was built and validated the same day.",[26,1304,1306],{"id":1305},"the-principle","The principle",[14,1308,1309],{},"When something works here but breaks there, turn every difference between the two environments into an independent toggle over one shared baseline, and refuse to name a cause until flipping that toggle both reproduces and removes the symptom. A hypothesis that survives only because you never tested the off state is not a diagnosis - it is a guess that hasn't been caught yet.",[1311,1312,1313],"style",{},"html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sbgvK, html code.shiki .sbgvK{--shiki-light:#E2931D;--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sP7_E, html code.shiki .sP7_E{--shiki-light:#39ADB5;--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .su5hD, html code.shiki .su5hD{--shiki-light:#90A4AE;--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .s_hVV, html code.shiki .s_hVV{--shiki-light:#90A4AE;--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .syTEX, html code.shiki .syTEX{--shiki-light:#FF5370;--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .sutJx, html code.shiki .sutJx{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#6A737D;--shiki-default-font-style:inherit;--shiki-dark:#6A737D;--shiki-dark-font-style:inherit}html pre.shiki code .sbsja, html code.shiki .sbsja{--shiki-light:#9C3EDA;--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sGLFI, html code.shiki .sGLFI{--shiki-light:#6182B8;--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .s99_P, html code.shiki .s99_P{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#E36209;--shiki-default-font-style:inherit;--shiki-dark:#FFAB70;--shiki-dark-font-style:inherit}html pre.shiki code .sfCm-, html code.shiki .sfCm-{--shiki-light:#90A4AE;--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .smGrS, html code.shiki .smGrS{--shiki-light:#39ADB5;--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .skxfh, html code.shiki .skxfh{--shiki-light:#E53935;--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sVHd0, html code.shiki .sVHd0{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#D73A49;--shiki-default-font-style:inherit;--shiki-dark:#F97583;--shiki-dark-font-style:inherit}html pre.shiki code .sjJ54, html code.shiki .sjJ54{--shiki-light:#39ADB5;--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .s_sjI, html code.shiki .s_sjI{--shiki-light:#91B859;--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .sfo-9, html code.shiki .sfo-9{--shiki-light:#90A4AE;--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .srdBf, html code.shiki .srdBf{--shiki-light:#F76D47;--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .stzsN, html code.shiki .stzsN{--shiki-light:#91B859;--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .s-KJb, html code.shiki .s-KJb{--shiki-light:#39ADB5;--shiki-default:#032F62;--shiki-dark:#DBEDFF}html pre.shiki code .s39Yj, html code.shiki .s39Yj{--shiki-light:#39ADB5;--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .sQRbd, html code.shiki .sQRbd{--shiki-light:#91B859;--shiki-default:#032F62;--shiki-dark:#DBEDFF}html pre.shiki code .sw1J6, html code.shiki .sw1J6{--shiki-light:#F76D47;--shiki-default:#D73A49;--shiki-dark:#F97583}",{"title":108,"searchDepth":129,"depth":129,"links":1315},[1316,1317,1318,1319],{"id":28,"depth":129,"text":29},{"id":69,"depth":129,"text":70},{"id":1259,"depth":129,"text":1260},{"id":1305,"depth":129,"text":1306},"2026-07-14","A single-file Electron harness that turned every difference between Chrome and a customer's wrapper into one toggle - and root-caused two independent bugs, one of which was a Chromium version regression no code could reveal.","md",null,false,"case-study",{},true,"\u002Fwork\u002Felectron-webfuse-repro-harness",{"title":5,"description":1321},"work\u002Felectron-webfuse-repro-harness",[1332,1333,23,1334,1335,1336],"Electron","Chromium","Debugging","Reproduction","Bun","0HCIz2_HWKOap14j0I7cg-pT5k32N_oZSltDN_Rprrw",1785421333422]