Skip to content

feat(browser): start tabs load in order and settle before agents get an address (FD-4941) - #530

Merged
earakely-scale merged 6 commits into
mainfrom
edgararakelyan/fd-4941-m512-start-tabs-when-they-count-as
Nov 8, 2026
Merged

earakely-scale merged 6 commits into
mainfrom
edgararakelyan/fd-4941-m512-start-tabs-when-they-count-as

Conversation

@earakely-scale

@earakely-scale earakely-scale commented Nov 7, 2026 •

Copy link
Copy Markdown
Collaborator

What and why

M5.12 (FD-4941): no agent gets an address until a browser's start tabs have loaded. They open in start_urls order with the first active, each page sees the window's size from its first script, and every start tab's outcome is kept. Tasks with two or three start URLs (WebArena-Verified, M8.13) need the agent to land on the first start page, signed in, never on an error or boot page. Graders and run debugging need to know what each start tab actually loaded.

The outcome and six decisions are on FD-4941. Decision 4 moved refusing an unclaimed start-tab host at save to M5.16, which adds live = true / allow_hosts.

  • One path for start tabs, in a computer and alone (decision 5): Chromium starts on a blank tab with only about:blank on its command line. The first start URL loads in that tab and each further one in a tab opened blank after it, each held to the window's size (headless) before it goes to its URL. The first is then brought to the front. Before, a computer put every start URL on the command line, and alone the first went there and the rest opened over the pipe at their URLs, loading before the window size was fixed (the bug feat(computer): gate 3 checks a browser computer's input and page tools on an HTML Fixture Desk #475 fixed for agent tabs).
  • When a tab is done (decisions 2 and 6): the tabs load at once. Each settles when its document loads, its final status read: the last document its main frame committed from its own navigation on, so a page that sends itself elsewhere by script before it loads is judged by the page it lands on; when it fails (a network error, or a 4xx/5xx on one of the env's own sites, which settles it at once); or after a wait: every tab settles within the browser's ready timeout of its launch, its setup counted, an own-site tab on what's left of it and any other on 30 s at most, so a deploy and data/reset, which wait that timeout and a margin, always get its outcome. A new start_tabs_loaded ready check passes once all have settled, so a deploy and cdp wait for them. A navigation's answer is awaited apart, since CDP answers Page.navigate only once it commits, which a page that never answers never does.
  • How the browser learns the env's own sites (decision 1): a new browser/v1 sites/set {sites: [{name, domains}]}. A computer deploy routing sites calls it on each held browser before launching it, and the app server keeps the sites in .agentenv/sites.json for every launch after. A browser built before it is told nothing, with a warning, and its start tabs get the live wait.
  • Sites ready before launch (decision 2): a computer deploy with site_routing asks each routed website env's website/v1 ready (up to 60 s, as each already passed its own deploy's wait), failing with each one's problems. Only the envs whose websites it routes are asked, so an env of the run that serves none can't hold the deploy up.
  • Where outcomes are kept (decision 3): record's start_tabs, one StartTabsLaunch per launch (at, tabs); data/get's browser-state.json start_tabs, the latest launch's; and the deploy record's metadata["computer"]["start_tabs"], by browser name. Each StartTab has tab, url, final_url, status, error, load_ms, site (null for live) and outcome (loaded, failed, timed_out).
  • Failing (decision 6): a computer's deploy, and a reset_env reboot, keep the outcomes in their record, then fail naming each start tab on one of the env's sites that didn't load. data/reset (through a new hook the data plane awaits after a relaunch, waiting on the start tabs for the ready timeout and a 30 s setup margin) and a load that opens the start tabs again fail the same way; a server's restart only records it.

How it was tested

  • ruff check: clean.
  • Protocol unit tests: 1310 passed.
    • test_website_browser.py: start tabs load in order in the first tab then blank ones, each held then sent, the first active, a hanging live page not holding up the others; own-site strict (error status settles at once, refused, never loads, a stale load event before its commit) against live (waited 30 s, an error status recorded but not fatal); a browser that ends mid-load; a fake browser over a real pipe launching headless with three start tabs to ready.
    • test_browser_data.py: a load that reopens the start tabs fails naming an own-site tab that failed, not a live one; the after-relaunch check; sites/set and host matching (exact, *. one label more, case and trailing dot).
    • test_computer_app_data.py: a reset that relaunched the app awaits the relaunch check, whose error fails it.
  • Core unit tests: test_computer_env.py (held browsers handed the routed sites before launch, one without sites/set launched with a warning; start tabs read per browser, an older browser left out; the boot failing naming each own-site tab), test_computer_sites.py (the readiness wait). The full unit tier on my Mac: 13742 passed; the 6 that failed are in tests this change doesn't touch (cua_driver_test, test_disk_recipe, which fails on macOS only, and test_docker_image_github_clone), and CI's Linux shards run them.
  • VM e2e, agent-environments-admin, amd64 and arm64: the chromium, browser computer, website browser, gateway CDP and computer app suites: amd64 passed all 35, arm64 all 32 (the three echo-agent tests deselected, since their image needs qemu there). The first run caught a page that never answers holding up every start tab: CDP answers Page.navigate only once it commits, and the pipe's 30 s call timeout then failed the whole load, so the browser never became ready. Navigations are now awaited apart, as above. New:
    • in a desktop, three start pages served on the host open in order, the first active, and a live one that never answers is recorded timed_out after 30 s while the deploy goes on;
    • with a site routed into the computer through the site proxy, a start tab on it loads (site: shop), and the same site answering 503 fails the deploy naming it. The site's home page replaces itself by script before it loads (a slow image keeps it from loading first), so the tab is judged by the page it lands on, /landed, in a real Chromium. This is the first e2e of site routing into a computer;
    • alone, each of three start tabs' first script sees 1280×800.

🤖 Generated with Claude Code

RetriggerView in GreptileConfidence Score: 5/5

The reviewed changes appear safe to merge.

Summary

The latest changes count browser launch setup against the time allowed for start tabs to settle.

  • Browser start tabs open in order and settle before the browser is ready.
  • Browser records show what each start tab loaded.
  • Computer deploys check routed websites and name start tabs that fail.
  • Browser data calls reject failed start tabs after they reopen.

No new actionable issues were found. The earlier threads are addressed: replacement pages are followed, reset waits include a margin, failed boot outcomes are saved before refusal, and losing the browser’s pipe no longer discards outcomes when activating the first tab.

Diagram

%%{init: {'theme': 'neutral'}}%%
flowchart TD
  A[Browser launch starts] --> B[Attach browser control]
  B --> C[Calculate remaining ready time]
  C --> D[Open and prepare start tabs]
  D --> E[Wait for each tab within its budget]
  E --> F[Save all tab outcomes]
  F --> G[Allow readiness checks to pass]
Loading

Reviews (11) · Last reviewed commit: "refactor(browser): a launch's start tabs..." · Reviewed by Greptile

…an address

Every start tab now opens over the browser's pipe, in a computer and alone: the first in the blank tab Chromium starts
with, the rest in blank tabs after it, each held to the window's size before it goes to its URL, the first brought to
the front. They load at once; each settles once it loads, fails (an error status on one of the env's own sites
settling it at once) or has waited its time (the ready timeout on an own site, 30 s on a live one), and a
start_tabs_loaded ready check passes once all have. Outcomes are kept in browser/v1 record per launch, in data/get and
in a computer deploy's record.

A deploy routing sites asks each routed website's ready, hands each browser the sites through a new browser/v1
sites/set before launching it, and fails naming any start tab on an own site that didn't load; data/reset (through a
new relaunch hook) and a load that reopens the start tabs fail the same way.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@earakely-scale
earakely-scale requested a review from a team as a code owner November 7, 2026 17:06
earakely-scale and others added 2 commits November 7, 2026 09:21
…here it lands, and failed start tabs are named and kept

A start tab now follows its main frame to the last document committed from its own navigation on, so a page that
replaces itself by script before it loads settles on that page's load and status rather than timing out. After a
relaunch, data/reset and a profile load wait on the start tabs themselves, their ready timeout and a setup margin, so
a failed one is named rather than reported as the browser not being ready. A boot keeps its start tabs' outcomes in
its record before it fails on one. Also renames a test loop variable that shadowed an import.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Resolves the one conflict, test_computer_env.py's imports, keeping main's conformance import and this branch's browser
one.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
earakely-scale and others added 3 commits November 7, 2026 15:46
…ey're ready, and test a routed start page that redirects

A computer routing the run's websites waited on every deployed env's readiness, so an env serving none that couldn't
be reached failed the deploy after 60 s; it now asks only the envs whose websites it routes. The routed-site e2e's home
page now replaces itself by script before it loads, held from loading by a slow image, so the start tab is judged by
the page it lands on in a real Chromium.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… before the first comes to the front

Bringing the first start tab to the front over a pipe the browser closed raised, so the outcomes the tabs had settled
on, "the browser ended", were never kept for the launch.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…out, its setup counted

A start tab on one of the env's sites waited the whole ready timeout from its own navigation, after the launch's
setup, while a deploy and data/reset wait that timeout and a margin from the launch, so a slow setup could let them
give up first with no outcome. Every start tab now settles within the ready timeout of its launch, an own-site one on
what's left of it and a live one on 30 s at most, so one constant decides it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@earakely-scale
earakely-scale merged commit b7a8e54 into main Nov 8, 2026
36 checks passed
@earakely-scale
earakely-scale deleted the edgararakelyan/fd-4941-m512-start-tabs-when-they-count-as branch November 8, 2026 02:06
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant