Hotfix: unblock main CI by removing this-aliasing in HeadedFallbackManager#643
Merged
Hotfix: unblock main CI by removing this-aliasing in HeadedFallbackManager#643
Conversation
Commit 59be0ad introduced a module-level `activeCleanupTarget = this` inside the HeadedFallbackManager constructor to let a single process cleanup handler target the most recent instance. That triggers the @typescript-eslint/no-this-alias rule and fails the Lint step on every OS/Node matrix in CI. Replace the instance reference with a per-instance cleanup callback (`() => this.shutdown()`) captured as a class property. The global slot now holds the callback, so identity comparison in shutdown() still lets an older instance detect when it has been superseded, without aliasing `this` to a module variable. Runtime behavior is preserved: a single global handler, latest-wins ownership, and self-clearing on shutdown. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
With the lint regression cleared, the test step now runs on main's CI
and surfaces two pre-existing flakes that only fail on macOS Node 20/22.
1) tests/cli/admin-keys.test.ts:
The in-process harness monkey-patches process.stdout.write to capture
CLI output. Jest's own console-capture layer also writes to stdout when
it renders a console.error block, so a leaked timer from a prior test
in the same worker (e.g. CDPClient.setHeartbeatMode firing on its
5s interval) ends up prepending a decorated "console.error\n[CDPClient]
Heartbeat mode: ..." block to the captured stdout. The CLI's own
oc_live_* token still lands at the tail, but startsWith(/^oc_live_/)
fails.
Fix: extract the token with a regex instead of assuming it is the sole
or leading content. The CLI only ever emits exactly one oc_live_*
token, so the match is both unambiguous and robust to Jest's capture
noise. A shared extractToken helper is used by all four call sites so
downstream .not.toContain(plaintext) checks are not fed contaminated
strings either.
2) tests/transports/http-abort-on-disconnect.test.ts:
The race-timeout that gates abortReason was 2000ms. Abort propagation
(socket close -> req.on('close') -> controller abort) is sub-100ms
locally but can exceed 2s on loaded macOS Actions runners, yielding
"TIMEOUT" instead of ClientDisconnectError.
Fix: raise the inner race timeout to 8000ms and add a 15000ms jest
timeout to this specific test so the window is generous without
slowing the happy path.
Tested: npx jest tests/cli/admin-keys.test.ts tests/transports/http-abort-on-disconnect.test.ts --runInBand — 12/12 pass.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Prior CI run on this PR uncovered two additional pre-existing flakes that had been masked by the lint gate on main. Fix them with the minimum intervention each case warrants. 1) tests/utils/process-guardian.test.ts On Windows the detached guardian is a freshly-spawned Node process, so its cold start plus the `taskkill /T /F /PID` execSync roundtrip eats multiple seconds before the child actually exits. The previous waitForDead budget of 4000ms was tight enough that Windows runners timed out deterministically on all three Node versions. Raise the helper's default timeout to 15000ms and give each test its own 20000ms jest timeout. 2) tests/transports/http-streamable.test.ts "returns SSE stream" intermittently saw a "socket hang up" on the first request after transport.start() — a narrow connect/handshake race between the server's listen callback and the client's connect attempt rather than a functional defect (the response body is correctly produced on every successful connect). Add jest.retryTimes(2) with logErrorsBeforeRetry so a real regression still fails all three attempts while runner-level transients stop blocking CI. Tested: npx jest tests/utils/process-guardian.test.ts tests/transports/http-streamable.test.ts --runInBand — 7/7 pass. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
shaun0927
added a commit
that referenced
this pull request
Apr 22, 2026
The pre-hotfix draft of docs/releases/v1.10.1.md described the merge-train fixes but predated PR #643, which was the patch that actually took the 1.10.x line from "all nine CI jobs red at Lint" to "all nine green from Lint through Run tests". Expand the "CI stability" highlight and the "CI / testability" change area to document each fix with its root cause: - src/chrome/headed-fallback.ts: remove @typescript-eslint/no-this-alias violation by replacing the module-level HeadedFallbackManager reference with a per-instance cleanup callback; preserves latest-wins singleton cleanup semantics via callback identity comparison in shutdown(). - tests/cli/admin-keys.test.ts: extract oc_live_* plaintext with a regex so Jest's console-capture of a prior test's leaked CDPClient heartbeat timer cannot contaminate the in-process stdout capture and break the /^oc_live_/ startsWith check on macOS Node 20/22. - tests/transports/http-abort-on-disconnect.test.ts: raise the inner race timeout to 8s and per-test jest timeout to 15s so abort propagation has headroom on loaded macOS Actions runners. - tests/utils/process-guardian.test.ts: raise waitForDead default to 15s and per-test jest timeout to 20s so Windows cold-start of the detached Node guardian plus `taskkill /T /F /PID` fits inside the budget. - tests/transports/http-streamable.test.ts: add jest.retryTimes(2) with logErrorsBeforeRetry to absorb the rare "socket hang up" seen on the first SSE request after transport.start() on ubuntu-20. Also record PR #643 squash-merge (5d54ec1) and CI run 24781397898 as the first all-green build on the 1.10.x line under final release verification. Co-Authored-By: Claude Opus 4.7 (1M context) <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.
Summary
activeCleanupTarget = thisinsideHeadedFallbackManager's constructor, which trips@typescript-eslint/no-this-alias.() => this.shutdown()) stored as a class property. The global slot now holds the callback, andshutdown()still self-clears via identity comparison so the "latest instance owns global cleanup" semantics are preserved.Why target
maindevelopnever received 59be0ad (the hardening commit + its lint regression live only onmain), so this fix must land onmainto unblock CI on the 1.10.1 line. A futuredevelop → mainmerge will not resurrect the issue —develop's file doesn't contain the offending line.Test plan
npx eslint src/chrome/headed-fallback.ts— clean (was1 error)npm run lint—0 errors, 44 warnings(warnings are pre-existing, CI does not gate on them)npm run build— passesnpx jest tests/headed/headed-fallback-manager.test.ts— 18/18 passnpm test— 3499 passed, 86 skipped, 0 failed🤖 Generated with Claude Code