Skip to content

Warm the browser binary before the containment probes - #14

Merged
randroid88 merged 1 commit into
mainfrom
fix/warm-chrome-before-containment-probes
Aug 3, 2026
Merged

Warm the browser binary before the containment probes#14
randroid88 merged 1 commit into
mainfrom
fix/warm-chrome-before-containment-probes

Conversation

@randroid88

Copy link
Copy Markdown
Contributor

node / windows-latest failed two of four CI runs on the real-Chrome containment probe, always with a timed-out DevTools endpoint on the first of its four launches, about eleven seconds in. It is unrelated to #13 and predates CI existing.

Diagnosis

probeHostedBrowserContainment gives the browser DEVTOOLS_ENDPOINT_TIMEOUT_MS (10s) to write DevToolsActivePort. That is a supervision bound for an already-warm appliance, not a launch-latency budget for a cold CI host, where a first Chrome start pays for binary paging and profile creation.

Only windows-latest is affected in practice because it ships Chrome, so the test actually runs there. The mocked subtests in the same file pass; only the real-browser one fails.

Fix

Run the browser once and wait for it to exit before the probe loop, so the probes do not spend their bound on first-launch cost.

  • The 10s bound is unchanged.
  • Every assertion still runs against a real browser; no coverage is dropped.
  • Warm-up failures are ignored on purpose — it is a cache warm-up, not an assertion.
  • The harness timeout rises 60s → 180s to cover the warm-up plus four real launches. That is the budget for the whole test, not the bound each probe is measured against.

hosted-browser-supervisor.ts is pinned by nine plans; the test file is not, so this changes no evidence bindings. Confirmed still 551/551 current.

Honest limits

This cannot be reproduced locally. Chrome is already warm on both my machines and the test completes in about two seconds. I verified only that it still passes on Linux and Windows — whether it removes the CI failure is what this run establishes. If it fails again, the answer is to measure the actual cold-start time rather than keep guessing.

Left open: if a cold Chrome start exceeds ten seconds on a GitHub Windows runner, it may also exceed it on a Raspberry Pi 5 booting from an SD card. That is a question about the supervision bound on target hardware, not about this test, and it is not answered here.

🤖 Generated with Claude Code

node / windows-latest failed two of four CI runs on the real-Chrome
containment probe, always with a timed-out DevTools endpoint on the first of
its four launches, about eleven seconds in.

probeHostedBrowserContainment gives the browser DEVTOOLS_ENDPOINT_TIMEOUT_MS
(10s) to write DevToolsActivePort. That is a supervision bound for an
already-warm appliance, not a launch-latency budget for a cold CI host, where
a first Chrome start pays for binary paging and profile creation.

Runs the browser once and waits for it to exit before the probe loop, so the
probes do not spend their bound on first-launch cost. The bound itself is
unchanged, every assertion still runs against a real browser, and the probes
remain the thing being measured. Warm-up failures are ignored on purpose: it
is a cache warm-up, not an assertion.

The harness timeout rises from 60s to 180s to cover the warm-up plus four real
launches. That is the budget for the whole test, not the bound each probe is
measured against.

This cannot be reproduced locally, where Chrome is already warm and the test
completes in about two seconds. Verified only that it still passes on both
platforms; whether it removes the CI failure is what the next run establishes.

Left open: if a cold Chrome start exceeds ten seconds on a GitHub Windows
runner, it may also exceed it on a Raspberry Pi 5 booting from an SD card.
That is a question about the supervision bound on target hardware, not about
this test, and it is not answered here.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Aug 3, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

The hosted browser supervisor test now warms Chrome before probes, bounds warm-up cleanup, ignores warm-up failures, and allows 180 seconds for the real-browser test.

Changes

Hosted browser test

Layer / File(s) Summary
Bounded browser warm-up
scripts/hosted-browser-supervisor.test.ts
The test imports spawn and adds warmBrowserBinary. The helper launches headless Chrome, handles exit or launch errors, and kills stalled processes after 30 seconds.
Real-browser test timing and setup
scripts/hosted-browser-supervisor.test.ts
The test timeout increases from 60 to 180 seconds. The test warms Chrome before running probes.

Estimated code review effort: 2 (Simple) | ~10 minutes

Poem

The rabbit warms Chrome with a hop,
A timer guards the little prop.
Probes begin when startup’s done,
With three more minutes for the run.
Safe exits keep the burrow neat.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly states the main change: warming the browser binary before containment probes.
Description check ✅ Passed The description explains the CI failure, diagnosis, fix, preserved timeout, and test coverage.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/warm-chrome-before-containment-probes
✨ Simplify code
  • Create PR with simplified code
  • Commit simplified code in branch fix/warm-chrome-before-containment-probes

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@scripts/hosted-browser-supervisor.test.ts`:
- Around line 99-108: Update the browser spawn arguments in warmBrowserBinary to
include --remote-debugging-port=0 and an isolated temporary --user-data-dir,
matching the DevTools startup configuration expected by the probes while keeping
the existing warm-up behavior unchanged.
- Around line 111-114: Update the timeout handling around the warm-up process
timer to wait for the child’s exit event before resolving. Add bounded,
platform-specific cleanup for the browser process tree on timed-out warm-ups,
rather than relying on detached process-group cleanup, while preserving the
existing timeout behavior for normal completion.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 6101e508-c2c7-445d-9030-7900a7c967b4

📥 Commits

Reviewing files that changed from the base of the PR and between 2c7dded and ba7c846.

📒 Files selected for processing (1)
  • scripts/hosted-browser-supervisor.test.ts
📜 Review details
⏰ Context from checks skipped due to timeout. (5)
  • GitHub Check: e2e / windows-latest
  • GitHub Check: native / windows-latest
  • GitHub Check: node / windows-latest
  • GitHub Check: e2e / ubuntu-latest
  • GitHub Check: node / ubuntu-latest
🔇 Additional comments (2)
scripts/hosted-browser-supervisor.test.ts (2)

2-2: LGTM!


876-881: LGTM!

Comment on lines +99 to +108
const child = spawn(
browserPath,
[
"--headless=new",
"--disable-gpu",
"--no-first-run",
"--no-default-browser-check",
"--dump-dom",
"about:blank",
],

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

rg -n -C 12 \
  'DEVTOOLS_ENDPOINT_TIMEOUT_MS|DevToolsActivePort|remote-debugging|user-data-dir|probeHostedBrowserContainment|spawn\(' \
  scripts

Repository: Randroids-Dojo/VCG-Console

Length of output: 33771


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '%s\n' '--- warm-up implementation and test call site ---'
sed -n '77,125p;865,925p' scripts/hosted-browser-supervisor.test.ts

printf '%s\n' '--- production argument builder and endpoint wait ---'
sed -n '414,438p;1641,1688p' scripts/hosted-browser-supervisor.ts

printf '%s\n' '--- focused argument comparison ---'
python3 - <<'PY'
from pathlib import Path
import re

test = Path("scripts/hosted-browser-supervisor.test.ts").read_text()
source = Path("scripts/hosted-browser-supervisor.ts").read_text()

warm = re.search(r'const child = spawn\(\s*browserPath,\s*\[(.*?)\],\s*\{', test, re.S)
probe = re.search(r'const child = spawn\(\s*browserPath,\s*\[\s*\.\.\.buildHostedBrowserArguments\(profilePath\),(.*?)\],\s*\{', source, re.S)
builder = re.search(r'return Object\.freeze\(\[(.*?)\]\);', source, re.S)

def strings(body):
    return re.findall(r'"([^"]+)"|`([^`]+)`', body)

print("warm-up literal arguments:", [a or b for a,b in strings(warm.group(1))])
print("probe extra literal arguments:", [a or b for a,b in strings(probe.group(1))])
print("builder arguments:", [a or b for a,b in strings(builder.group(1))])
PY

Repository: Randroids-Dojo/VCG-Console

Length of output: 6416


Exercise the DevTools startup path in the warm-up.

warmBrowserBinary omits --remote-debugging-port=0 and --user-data-dir, while the probes wait for DevToolsActivePort in fresh profiles. Use equivalent DevTools flags and an isolated temporary profile in the warm-up.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@scripts/hosted-browser-supervisor.test.ts` around lines 99 - 108, Update the
browser spawn arguments in warmBrowserBinary to include
--remote-debugging-port=0 and an isolated temporary --user-data-dir, matching
the DevTools startup configuration expected by the probes while keeping the
existing warm-up behavior unchanged.

Comment on lines +111 to +114
const timer = setTimeout(() => {
child.kill();
resolve();
}, 30_000);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🩺 Stability & Availability | 🟠 Major | 🏗️ Heavy lift

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

rg -n -C 12 \
  'warmBrowserBinary|child\.kill|taskkill|process\.kill|close|exit' \
  scripts/hosted-browser-supervisor.test.ts scripts/hosted-browser-supervisor.ts

Repository: Randroids-Dojo/VCG-Console

Length of output: 50382


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '%s\n' '--- imports and warm-up helper ---'
sed -n '1,125p' scripts/hosted-browser-supervisor.test.ts

printf '%s\n' '--- process-stop implementation and callers ---'
rg -n -C 18 \
  'function stopBrowserProcess|const stopBrowserProcess|stopBrowserProcess\(' \
  scripts/hosted-browser-supervisor.ts

printf '%s\n' '--- test sequencing and cleanup ---'
sed -n '870,930p' scripts/hosted-browser-supervisor.test.ts

Repository: Randroids-Dojo/VCG-Console

Length of output: 14607


🏁 Script executed:

#!/bin/bash
set -euo pipefail

sed -n '1690,1755p' scripts/hosted-browser-supervisor.ts
printf '%s\n' '--- all process-tree cleanup references ---'
rg -n 'taskkill|kill\(-|kill\(|detached|windowsHide|process\.platform|SIGKILL|SIGTERM' \
  scripts package.json .github 2>/dev/null || true

Repository: Randroids-Dojo/VCG-Console

Length of output: 8191


🏁 Script executed:

#!/bin/bash
set -euo pipefail

sed -n '1750,1815p' scripts/hosted-browser-supervisor.ts
printf '%s\n' '--- launch options around every production browser spawn ---'
sed -n '500,535p;850,880p;960,980p;1045,1065p' scripts/hosted-browser-supervisor.ts

Repository: Randroids-Dojo/VCG-Console

Length of output: 5110


🏁 Script executed:

#!/bin/bash
set -euo pipefail

node - <<'JS'
const { EventEmitter } = require('node:events');

const child = new EventEmitter();
child.kill = () => {
  setTimeout(() => child.emit('exit', null, 'SIGTERM'), 50);
  return true;
};

let exited = false;
child.once('exit', () => { exited = true; });

const settled = new Promise((resolve) => {
  const timer = setTimeout(() => {
    child.kill();
    resolve();
  }, 1);
  child.once('exit', () => {
    clearTimeout(timer);
    resolve();
  });
});

(async () => {
  await settled;
  console.log(JSON.stringify({ settledBeforeExit: !exited }));
  await new Promise((resolve) => setTimeout(resolve, 75));
  console.log(JSON.stringify({ exitedAfterward: exited }));
})();
JS

Repository: Randroids-Dojo/VCG-Console

Length of output: 216


Wait for the timed-out browser process to terminate before resolving.

The timeout path resolves before the exit event. The warm-up process is not detached, so it cannot use the production process-group cleanup on POSIX. Wait for exit and add bounded platform-specific browser-tree cleanup for timed-out warm-ups.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@scripts/hosted-browser-supervisor.test.ts` around lines 111 - 114, Update the
timeout handling around the warm-up process timer to wait for the child’s exit
event before resolving. Add bounded, platform-specific cleanup for the browser
process tree on timed-out warm-ups, rather than relying on detached
process-group cleanup, while preserving the existing timeout behavior for normal
completion.

@randroid88
randroid88 merged commit a3b9889 into main Aug 3, 2026
22 of 29 checks passed
@randroid88
randroid88 deleted the fix/warm-chrome-before-containment-probes branch August 3, 2026 02:53
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant