fix(fetch): degrade to the lower tier on the domain-marked path when no browser engine (PX2 RC gate arm) - #625
Merged
KnockOutEZ merged 2 commits intoSep 8, 2026
Conversation
…no browser engine A host marked `preferPlaywright` starts at the browser tier, so it reached `browserFetch` with no HTTP result in hand and answered `browser_engine_unavailable` on a machine with no engine — for a page plain HTTP had returned in full one call earlier. The mark is a preference (short body, __NEXT_DATA__, script ratio), never a claim that HTTP cannot serve the page. The lower tier is now passed as a thunk, invoked only when acquisition fails, so a healthy machine still pays no extra round-trip. A thrown or empty lower tier leaves the actionable error standing.
The suspected warmup/acquirer disagreement is not real: measured on a fresh packed install with a live network, warmup --browser reports ok and the next process's acquirer finds both driver and binary. The red was the domain-marked tier path hard-failing with an HTTP tier that could have served the page.
|
Important Review skippedAuto reviews are disabled on base/target branches other than the default branch. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Advanced Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
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.
Closes KnockOutEZ/wigolo-studio-run#521.
The suspected defect does not exist
#521's sharper question was a product one:
warmup --browserreports success and asubsequent fetch in a fresh process says the engine is not installed — which would make
the hint on the one error whose job is to be actionable into advice that does not work.
Measured on a fresh
npm pack+npm installsandbox with its ownHOMEandWIGOLO_DATA_DIRand a live network (no egress fence):warmup --browserandbrowserInstalledOnDisk()agree across processes. The hint issound. Inside the RC suite the egress fence blocks every non-loopback destination, so the
driver cannot be acquired there at all — an empty
browser-driver/directory in thesandbox data dir after a gate run is the evidence — and a warmup run there cannot have
reported
ok. The prior session's "interesting half" was a fence artifact.What actually reddened the arm
One layer up, and it is a real product defect on the fresh-install population the gate is
about.
/changelog's fixture body carries 34 characters of visible text, underVISIBLE_TEXT_THRESHOLD(200), so the seedingfetchtripped SPA-shell detection and setstats.preferPlaywrighton the fixture host. That fetch still succeeded: the escalationwas holding the HTTP result and handed it over as
fallback, so the machine degraded to itwith the actionable note. The forced re-read then took
browserOrHttpForBinary— thedomain-marked path, which starts at the browser tier holding nothing — and hard-failed
browser_engine_unavailablefor a page plain HTTP had served in full one call earlier. Ona fresh install with no engine, that is every later fetch of that host, forever.
The mark is a preference, not a requirement: it is set by an under-threshold body, a
__NEXT_DATA__blob or a high script ratio, none of which claim HTTP cannot serve the page.So that branch now carries a deferred lower tier —
fallbackFetch, a thunk invoked onlywhen acquisition fails. A thunk rather than an eager fetch because eagerly fetching would
put a full HTTP round-trip in front of every domain-marked fetch on a healthy machine,
which is the cost the mark exists to avoid. A thrown or empty lower tier leaves the
actionable error standing, and
guardChallengeShellapplies to the lazily-fetched body too,so an interstitial still becomes
blocked_by_challengerather than content.Fixing the product rather than taking the arm off the browser tier (scope option 3) was
deliberate and is recorded as D-521-1: lengthening the fixture body would have greened
the gate and left the defect shipped and unobserved.
Tests
Two arms in
tests/unit/fetch/router-browser-acquire.test.ts, both proven able to fail:before the fix with the exact production error envelope.
negative control, so the fix cannot swallow a real failure into a silent empty success.
Mutated the guard (synthesise an empty result in the
catch) and watched it red, thenreverted.
Verification
RUN_PX2_RC=1 npx vitest run tests/integration/px2-rc/rc-exit-gate.test.ts→ 12passed (12), 535.83s (was 1 failed | 11 passed).
npm test→ 971 files passed, 12083 passed, 36 skipped, 7 todo, 0 failed.npx tsc --noEmit→ clean.npm run typecheck:studio→ clean.npm run gate:studio→ clean; tests/ type-check debt holds at baseline 341.One blocker found, filed, not caused
The 12/12 above was obtained with
TMPDIR="$TMPDIR/sd521safe/", and that matters foranyone re-running it.
internal-docs/runner/cleanup.sh:112sweepsrm -rf "$TMP"/wigolo-*whenever any worker slot finishes, and all slots share one
$TMPDIR. It landed in themiddle of a gate run here and turned 1 failure into 4 — one cause, four faces: the packed
tarball vanished (
npm error enoent … wigolo-0.2.1.tgz), an extracted install lost files(
Cannot find module …/dist/security/key-store.js), a fresh install'saccount/state.jsonnever appeared, and the fixture's embedded-postgres data dir was gutted (
FATAL: could not open file "global/pg_filenode.map"). Every message names a product module, so it readsexactly like a regression in the code under test. Directory mtimes settled it: everything
older than the sweep instant was gone, everything created after it survived. The glob is
flat, so one extra
$TMPDIRlevel escapes it. Filed asKnockOutEZ/wigolo-studio-run#524with a two-arm acceptance probe; not fixed here becausecleanup.shis the conductor's own machinery running live under six lanes.CI note: a red confined to the brief's named classes (
clean-machine smoke/studio-setupxattrarms /net-fencedoctor probe / build-less tests-debt ratchet)does not reflect this change — the diff touches
src/fetch/router.tsand two test filesonly.