diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c857a69..ac77841 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -33,6 +33,7 @@ jobs: - run: npm ci --ignore-scripts - run: npm run typecheck - run: npm run build + - run: npm run verify:lightweight-proof - run: npm test - run: npm run check:package - run: npm run check:site diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 0ada392..08c1560 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -44,6 +44,15 @@ jobs: run: | node -e "const p=require('./package.json'); const tag=process.env.GITHUB_REF_NAME; if(tag !== 'v'+p.version) throw new Error('tag '+tag+' does not match package '+p.version)" git tag --points-at HEAD | grep -Fx "${GITHUB_REF_NAME}" + if [ "$(git cat-file -t "refs/tags/${GITHUB_REF_NAME}")" != "tag" ]; then + echo "::error::Release tags must be annotated tags" + exit 1 + fi + default_branch="${{ github.event.repository.default_branch }}" + if ! git merge-base --is-ancestor "$GITHUB_SHA" "origin/$default_branch"; then + echo "::error::Release commit $GITHUB_SHA is not reachable from protected $default_branch" + exit 1 + fi - run: npm ci --ignore-scripts - run: npx playwright install --with-deps chromium firefox webkit - run: xvfb-run -a npm run check @@ -191,4 +200,18 @@ jobs: throw new Error(`GitHub release state mismatch: ${JSON.stringify(release)}`); } ' "$release_json" "${{ steps.channel.outputs.prerelease }}" - gh release upload "$GITHUB_REF_NAME" "$RELEASE_ASSET_DIR"/* --clobber + existing_assets="$(gh release view "$GITHUB_REF_NAME" --json assets --jq '.assets[].name')" + for asset in "$RELEASE_ASSET_DIR"/*; do + asset_name="$(basename "$asset")" + if grep -Fxq "$asset_name" <<<"$existing_assets"; then + download_dir="$(mktemp -d)" + gh release download "$GITHUB_REF_NAME" --pattern "$asset_name" --dir "$download_dir" + if ! cmp --silent "$asset" "$download_dir/$asset_name"; then + echo "::error::Existing release asset $asset_name has a different immutable identity" + exit 1 + fi + echo "Verified existing immutable release asset $asset_name." + else + gh release upload "$GITHUB_REF_NAME" "$asset" + fi + done diff --git a/CHANGELOG.md b/CHANGELOG.md index b320c8c..0f5f8ee 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,32 @@ The format follows Keep a Changelog and the project uses Semantic Versioning. ## [Unreleased] +## [0.5.0-rc.1] - 2026-09-03 + +### Added + +- explicit runtime-owned Obscura and Lightpanda CDP providers with loopback-only + startup, optional executable digest pinning, capability preflight, and owned + process-tree cleanup +- bounded structured extraction for sanitized text, HTML, Markdown, links, + metadata, JSON-LD, and tables +- continuous browser process-tree sampling and a fail-closed lightweight + conformance benchmark that reports browser and coordinator memory separately + +### Changed + +- browser capabilities now describe a routed union across full Playwright and + Puppeteer engines, protocol and device adapters, and experimental lightweight + engines instead of implying one engine implements every browser feature +- public action and session schemas now track the runtime action and provider + contracts, including lightweight-provider opt-in and configuration limits + +### Verification + +- lightweight benchmark results include exact binary identity, machine data, + warm-up and measured runs, required DOM/JavaScript/form checks, continuous + peak RSS and CPU observations, and a non-zero exit when any target fails + ## [0.4.1] - 2026-08-15 ### Fixed @@ -206,7 +232,8 @@ The format follows Keep a Changelog and the project uses Semantic Versioning. - deterministic capability catalog, JSON schemas, examples, Docker profile, and release checks - Node.js 22, 24, and 26 verification matrix -[Unreleased]: https://github.com/AjnasNB/cockroach-browser/compare/v0.4.1...HEAD +[Unreleased]: https://github.com/AjnasNB/cockroach-browser/compare/v0.5.0-rc.1...HEAD +[0.5.0-rc.1]: https://github.com/AjnasNB/cockroach-browser/compare/v0.4.1...v0.5.0-rc.1 [0.4.1]: https://github.com/AjnasNB/cockroach-browser/compare/v0.4.0...v0.4.1 [0.4.0]: https://github.com/AjnasNB/cockroach-browser/compare/v0.4.0-rc.1...v0.4.0 [0.4.0-rc.1]: https://github.com/AjnasNB/cockroach-browser/compare/v0.3.0...v0.4.0-rc.1 diff --git a/CITATION.cff b/CITATION.cff index 7c6f7c2..c6006e4 100644 --- a/CITATION.cff +++ b/CITATION.cff @@ -6,8 +6,8 @@ authors: - family-names: "N B" given-names: "Ajnas" alias: "AjnasNB" -version: 0.4.1 -date-released: 2026-08-15 +version: 0.5.0-rc.1 +date-released: 2026-09-03 license: AGPL-3.0-or-later repository-code: "https://github.com/AjnasNB/cockroach-browser" url: "https://cockroachbrowser.com" diff --git a/Dockerfile b/Dockerfile index d89b557..5c2af49 100644 --- a/Dockerfile +++ b/Dockerfile @@ -7,6 +7,7 @@ RUN npm ci --ignore-scripts COPY tsconfig.json tsconfig.build.json server.json ./ COPY src ./src +COPY schemas ./schemas RUN npm run build FROM node:24-bookworm-slim AS runtime diff --git a/README.md b/README.md index 069992d..a05aee2 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,7 @@ Documentation - Features - AI agents - - 127-capability registry - + 130-capability registry - Complete Playwright and Puppeteer API inventory - Alternatives by product layer - Technical white paper @@ -41,12 +41,12 @@ These controls preserve the powerful operational workflows people expect from an ## Release status -Current release line: **0.4.1** +Current release line: **0.5.0-rc.1**. This prerelease is the tested integration line; keep production deployments pinned until its release checks and provenance are green. - License: AGPL-3.0-or-later - Runtime: maintained Node.js 22, 24, or 26 - Registry: `cockroach-browser` -- Capability registry: 127 entries, with 117 available and 10 adapter-backed +- Current-main capability registry: 130 entries, with 119 available and 11 adapter-backed - MCP identity: `io.github.AjnasNB/cockroach-browser` - Paper: [Cockroach Browser: A Local-First Browser Runtime for AI Agents](https://cockroachbrowser.com/paper/) - Published paper v1.1 DOI: [10.5281/zenodo.21850760](https://doi.org/10.5281/zenodo.21850760) @@ -54,12 +54,30 @@ Current release line: **0.4.1** Verify the npm version, provenance, Git commit, and matching GitHub release before production use. +## Choose the execution lane before launch + +Cockroach Browser exposes one machine-readable capability contract across two deliberately different execution lanes. The bounded runtime applies session policy, budgets, evidence, and receipts; the raw Playwright and Puppeteer operator exports are intentionally unrestricted and do not inherit that boundary. + +- **Full-fidelity:** headed or headless Chromium, Firefox, and WebKit for rendering, screenshots, PDFs, traces, HAR, video, downloads, uploads, frames, Shadow DOM, profiles, extensions, raw Playwright/Puppeteer objects, and protocol access. +- **Lightweight:** an explicit, separately installed engine for compatible DOM and JavaScript work. Obscura is an experimental runtime-owned CDP provider. Selecting `rendering: "none"` makes Cockroach Browser deny visual actions during capability preflight; it does not assert that the Obscura binary disabled or omitted its renderer. Lightpanda currently exposes only its manifest, validation, and machine preflight; managed Lightpanda launch fails closed until an engine- or OS-level boundary covers every relevant egress path. + +Clients can inspect `GET /v1/engines`, call `BrowserClient.engines()`, or use the MCP tools `browser_engines` and `browser_engine_preflight` before creating or acting on a session. The manifest reports each requested capability as `supported`, `experimental`, or `unsupported`; experimental work requires explicit opt-in, and unsupported work is rejected rather than silently switching engines. In particular, `runtime.owned_launch` is supported for the three full engines, experimental for Obscura, and unsupported for Lightpanda. + +The measured memory result is intentionally narrow. On the September 3, 2026 pinned Windows fixture, the constrained Obscura 0.2.1 non-visual workload ran one warmup plus 20 measured launches per target, with ten steady-state samples at 25 ms intervals. The reviewed 58,097,152-byte binary has SHA-256 `5b609fb46bc00da79e450fb0fbd34bd442e565b1394f4af95433e0b341078221`. Required connection, JavaScript, DOM, form, screenshot-preflight-denial, and teardown checks passed in every measured launch. + +| Target | Verdict | Maximum complete owned-browser-tree RSS | +| --- | --- | --- | +| 30 MiB | **PASS** | 29,622,272 bytes | +| 25 MiB | **FAIL** | 29,679,616 bytes | + +The passing maximum is exactly 28.25 MiB. The Node coordinator was measured separately. This is not a whole-app, coordinator, arbitrary-page, rendered-page, or full-browser memory guarantee; Chromium, Firefox, and WebKit use far more memory. See the [canonical measured proof record](./docs/benchmarks/obscura-non-visual-2026-09-03.md), [headless compatibility guide](./docs/headless-compatibility.md), and [resource-governance methodology](./docs/resource-governance.md). + ## Install once for your user account Install the CLI globally when the same operator account should use Cockroach Browser across projects: ```bash -npm install --global cockroach-browser +npm install --global cockroach-browser@0.5.0-rc.1 cockroach-browser bootstrap cockroach-browser doctor ``` @@ -69,7 +87,7 @@ The global installation makes the CLI available across the current computer acco ## Install inside one project ```bash -npm install cockroach-browser +npm install cockroach-browser@0.5.0-rc.1 npx cockroach-browser bootstrap ``` @@ -212,9 +230,9 @@ The authenticated daemon exposes: | Method | Route | Purpose | | --- | --- | --- | -| GET | `/v1/health` | Runtime and evidence integrity health | -| GET | `/v1/openapi.json` | Machine-readable authenticated route index | -| GET | `/v1/metrics` | Prometheus-compatible operational counters | +| GET | `/v1/health` | Administrator-only runtime and evidence integrity health | +| GET | `/v1/openapi.json` | Machine-readable index of every implemented daemon method/path, with unique operation IDs and response declarations | +| GET | `/v1/metrics` | Administrator-only Prometheus-compatible operational counters | | GET | `/v1/activity` | Bounded, actor-filtered activity ledger | | GET | `/v1/activity/stream` | Server-sent lifecycle activity stream | | GET, POST, DELETE | `/v1/profiles/:name?` | Administrator-owned persistent profile lifecycle | @@ -222,6 +240,7 @@ The authenticated daemon exposes: | GET | `/v1/jobs/:id` | Inspect one visible job | | POST | `/v1/jobs/:id/cancel` | Cancel queued or running work | | GET | `/v1/capabilities` | Source-derived capability catalog | +| GET | `/v1/engines` | Per-engine supported, experimental, and unsupported capability manifest; optionally filter with `?engine=` | | GET, POST | `/v1/sessions` | List or create authorized sessions | | GET, DELETE | `/v1/sessions/:id` | Inspect or close one session | | GET | `/v1/sessions/:id/navigation-graph` | Session-local URL nodes and traversed edges | @@ -236,7 +255,7 @@ The authenticated daemon exposes: | POST | `/v1/sessions/:id/network/export` | Redacted network export evidence | | POST | `/v1/sessions/:id/challenge/resume` | Resume after human handling | | GET | `/v1/evidence` | Evidence records | -| GET | `/v1/evidence/verify` | Hash-chain verification | +| GET | `/v1/evidence/verify` | Administrator-only global hash-chain verification | | GET | `/v1/artifacts/:id` | Authenticated artifact download | ## Use the typed daemon client @@ -255,7 +274,11 @@ console.log(await browser.health()); console.log(await browser.capabilities()); ``` -`BrowserClient` supports health, capabilities, session creation and inspection, session close, navigation graphs, actions, bounded batches, jobs, snapshots, paired capture, bounded network observation and export, audits, persistent profile administration, activity polling, and human-handoff resume. The daemon still enforces its own route and session-authority settings. +`BrowserClient` supports health, capabilities, engine-manifest inspection, session creation and inspection, session close, navigation graphs, actions, bounded batches, jobs, snapshots, paired capture, bounded network observation and export, audits, persistent profile administration, activity polling, and human-handoff resume. The daemon still enforces its own route and session-authority settings. + +Actor-scoped tokens require a configured `TeamSessionStore`; the daemon refuses to start with actor tokens and no ownership store. The administrator token and every actor token must be unique or startup fails with `AUTH_TOKEN_COLLISION`. Actor-token session creation also remains disabled until the host supplies `actorSessionFactory`. That callback must derive an authoritative `SessionCreateInput` from individually reviewed request fields - never spread caller JSON into the result. The server overwrites the actor from the authenticated token and claims the new session in `TeamSessionStore` before exposing it; claim failure closes the session. Access mutations are persisted before they replace the in-memory ownership map, so a disk failure leaves the prior access state intact. + +Daemon admission is independently bounded by `BrowserServerOptions.maxSessions` (default `32`) and `maxSessionsPerActor` (default `8`). The ceilings count every runtime session whose state is not `closed`, plus pending session-creation reservations. Admission runs through a concurrency-safe serialized reservation step, so simultaneous `POST /v1/sessions` requests cannot oversubscribe either ceiling. A rejected request returns HTTP `429` with the stable code `SESSION_GLOBAL_LIMIT_EXCEEDED` or, for an actor-assigned session, `SESSION_ACTOR_LIMIT_EXCEEDED`. `BrowserServerOptions.maxRequestBytes` defaults to 1,048,576 bytes and accepts only integer values from 1,024 through 16,777,216 bytes. ## Connect through MCP @@ -266,7 +289,7 @@ Start the daemon, load its token into the client process through a secret store, "mcpServers": { "cockroach-browser": { "command": "npx", - "args": ["-y", "cockroach-browser@0.4.1", "mcp"], + "args": ["-y", "cockroach-browser@0.5.0-rc.1", "mcp"], "env": { "COCKROACH_BROWSER_URL": "http://127.0.0.1:43110", "COCKROACH_BROWSER_TOKEN": "" @@ -281,6 +304,8 @@ Do not commit a live daemon token to an MCP configuration. The MCP process reads The MCP surface is observation-first: - `browser_capabilities` +- `browser_engines` +- `browser_engine_preflight` - `browser_health` - `browser_sessions` - `browser_snapshot` @@ -289,7 +314,7 @@ The MCP surface is observation-first: - `browser_network` - `browser_propose_action` -`browser_propose_action` returns a canonical proposal and input digest. It does not execute the action. Dispatch consequential work through Maqam. +`browser_engines` returns the same exact engine manifests as `GET /v1/engines`. `browser_engine_preflight` evaluates a proposed action set without launching a browser; experimental capabilities require `allowExperimental`, while unsupported capabilities always fail. `browser_propose_action` returns a canonical proposal and input digest. None of these tools creates a session or executes the action. Dispatch consequential work through Maqam. ## Run with Docker @@ -354,24 +379,24 @@ Profile import and export require `COCKROACH_BROWSER_PROFILE_PASSPHRASE`. Passph Daemon clients can use `--token`, `--token-file`, `COCKROACH_BROWSER_TOKEN`, or `COCKROACH_BROWSER_TOKEN_FILE`. They can override the URL with `--url` or `COCKROACH_BROWSER_URL`. -## 127 source-registered capabilities +## 130 source-registered capabilities The registry is generated from `src/capabilities.ts`, not from a marketing checklist. | Group | Available | Adapter | Planned | Total | | --- | ---: | ---: | ---: | ---: | -| Sessions | 24 | 0 | 0 | 24 | +| Sessions | 25 | 1 | 0 | 26 | | Interaction | 32 | 0 | 0 | 32 | -| Evidence | 18 | 0 | 0 | 18 | +| Evidence | 19 | 0 | 0 | 19 | | Audit | 8 | 0 | 0 | 8 | | Security | 9 | 3 | 0 | 12 | | Deployment | 22 | 3 | 0 | 25 | | Integration | 4 | 4 | 0 | 8 | -| **Total** | **117** | **10** | **0** | **127** | +| **Total** | **119** | **11** | **0** | **130** | ### Sessions -Authorized browser sessions; headless or headed Chromium, Firefox, and WebKit; explicit CDP attachment; raw CDP and WebDriver BiDi; complete Playwright and Puppeteer Core re-exports; native mobile WebDriver/Appium transport; cross-platform Chrome, Edge, Brave, and Chromium discovery; bundled, system, custom-executable, and CDP providers; reviewed unpacked Chromium extensions; runtime-owned persistent profiles; named isolated profiles; encrypted storage state and checkpoints; clipboard; explicit proxies; bounded runtime emulation; and unrestricted upstream emulation through the raw operator APIs. +Authorized browser sessions; headless or headed Chromium, Firefox, and WebKit; explicit CDP attachment; raw CDP and WebDriver BiDi; complete Playwright and Puppeteer Core re-exports; native mobile WebDriver/Appium transport; cross-platform Chrome, Edge, Brave, and Chromium discovery; bundled, system, custom-executable, CDP, and explicit lightweight providers; machine-readable engine negotiation and action preflight; reviewed unpacked Chromium extensions; runtime-owned persistent profiles; named isolated profiles; encrypted storage state and checkpoints; clipboard; explicit proxies; bounded runtime emulation; and unrestricted upstream emulation through the raw operator APIs. ### Interaction @@ -387,7 +412,7 @@ Accessibility; page performance observations; broken assets; console errors and ### Security -Challenge detection; human challenge handoff; origin allowlists; private-network blocking; effect-level policy; finite resource budgets; exact-origin static network interception. +Challenge detection; human challenge handoff; HTTP(S) origin allowlists; private-network blocking; effect-level policy; finite resource budgets; and exact-origin static interception for routed HTTP(S) requests. Adapter-backed security surfaces: @@ -415,8 +440,8 @@ Available: signatures, bounded retries, dead letters, and hash-linked delivery receipts - Persistent team session ownership with revocable viewer and operator grants, without raw profile sharing -- OpenAI-compatible model gateway with bounded structured tool calls -- Finite-step browser agent loop over semantic snapshots, exact actions, and receipts +- OpenAI-compatible model gateway with independent request/response byte ceilings, deadlines, secret-provider support, and bounded structured tool calls +- Finite-step browser agent loop with strict shared action-schema validation, `maxContextChars`, `maxToolOutputChars`, complete-turn compaction, optional cited context, mandatory post-action observation before completion, semantic snapshots, exact actions, and receipts The complete searchable matrix, including capability IDs, implementation status, and exact API surfaces, is in [docs/capabilities.md](./docs/capabilities.md). @@ -472,7 +497,8 @@ await webhooks.upsertEndpoint({ const runtime = new BrowserRuntime({ root: ".cockroach-browser/runtime", - eventPublisher: webhooks + eventPublisher: webhooks, + eventPublisherTimeoutMs: 1_000 }); await runtime.initialize(); @@ -484,6 +510,8 @@ const result = await webhooks.drain({ console.log(result, await webhooks.health()); ``` +`eventPublisherTimeoutMs` limits how long lifecycle delivery may delay browser work. It defaults to 1,000 ms and accepts integer values from 1 through 120,000 ms. Publisher rejection or timeout is treated as an operational integration failure and does not replace the browser action or cleanup result. + Endpoint configuration stores only an opaque `ref:` value. The host resolver returns the signing key during `drain()`, and the key is never written to the outbox or receipt ledger. Endpoints must be credential-free public HTTPS URLs @@ -518,9 +546,9 @@ handling, quotas, and recovery procedures. ## Action surface -The typed runtime implements 65 action kinds: +The typed runtime implements 66 action kinds: -`navigate`, `back`, `forward`, `reload`, `click`, `doubleClick`, `fill`, `type`, `press`, `hover`, `focus`, `check`, `uncheck`, `select`, `scroll`, `drag`, `mouse.move`, `mouse.down`, `mouse.up`, `mouse.click`, `keyboard.down`, `keyboard.up`, `keyboard.insertText`, `upload`, `download`, `evaluate`, `query.inspect`, `emulation.set`, `emulation.clear`, `cache.clear`, `console.clear`, `network.clear`, `wait`, `challenge.resolve`, `history.inspect`, `capture.paired`, `annotate.show`, `annotate.clear`, `clipboard.read`, `clipboard.write`, `network.inspect`, `network.export`, `network.route.add`, `network.route.remove`, `network.routes.list`, `state.save`, `state.load`, `state.list`, `state.delete`, `screenshot`, `pdf`, `snapshot`, `extract`, `cookies.read`, `cookies.write`, `storage.read`, `storage.write`, `tab.open`, `tab.close`, `tab.switch`, `tab.lock`, `tab.unlock`, `tab.lock.status`, `trace.start`, and `trace.stop`. +`navigate`, `back`, `forward`, `reload`, `click`, `doubleClick`, `fill`, `type`, `press`, `hover`, `focus`, `check`, `uncheck`, `select`, `scroll`, `drag`, `mouse.move`, `mouse.down`, `mouse.up`, `mouse.click`, `keyboard.down`, `keyboard.up`, `keyboard.insertText`, `upload`, `download`, `evaluate`, `query.inspect`, `emulation.set`, `emulation.clear`, `cache.clear`, `console.clear`, `network.clear`, `wait`, `challenge.resolve`, `history.inspect`, `capture.paired`, `annotate.show`, `annotate.clear`, `clipboard.read`, `clipboard.write`, `network.inspect`, `network.export`, `network.route.add`, `network.route.remove`, `network.routes.list`, `state.save`, `state.load`, `state.list`, `state.delete`, `screenshot`, `pdf`, `snapshot`, `extract`, `extract.structured`, `cookies.read`, `cookies.write`, `storage.read`, `storage.write`, `tab.open`, `tab.close`, `tab.switch`, `tab.lock`, `tab.unlock`, `tab.lock.status`, `trace.start`, `trace.stop`. Availability in the type system does not grant authority. The session policy, effect policy, approval provider, server surface, origin checks, and resource budget decide whether an action can run. @@ -559,6 +587,8 @@ await runtime.act(session.id, { Network routes are off until `allowNetworkInterception` is enabled. A route may match one already admitted origin, a bounded pathname glob, an explicit method set, and optional resource types. It may only abort the request or return a static response. It cannot redirect requests, inject credentials, discover cookies, or widen the origin policy. Static response bodies are constrained by `maxRouteFulfillBytes`, and cumulative fulfilled bytes are constrained by `maxInterceptedBytes`. +Bounded contexts route intercepted HTTP(S) navigation and subresource requests through the session origin policy. Runtime-owned full engines also validate WebSocket handshakes, and service workers are blocked in balanced and lean contexts so they cannot bypass HTTP(S) routing through that surface. This is not a general network sandbox: it does not contain WebRTC/STUN/TURN/UDP, WebTransport/QUIC, attached CDP, lightweight-engine WebSockets, or the deliberately unrestricted raw Playwright/Puppeteer lane. Use an OS, container, firewall, or equivalent egress boundary for hostile content or complete protocol isolation. + ```js await runtime.act(session.id, { kind: "network.route.add", @@ -608,16 +638,26 @@ The driver exposes four operations: `observe`, `preview`, `apply`, and `submit`. ### Qarinah ```js -import { createQarinahContextRecorder } from "cockroach-browser/qarinah"; +import { + createQarinahAgentContextProvider, + createQarinahContextRecorder +} from "cockroach-browser/qarinah"; const recorder = createQarinahContextRecorder({ async appendBrowserOutcome(event) { await hostProvidedQarinahSink.appendBrowserOutcome(event); } }); + +const contextProvider = createQarinahAgentContextProvider( + hostProvidedQarinahMemorySource, + { limit: 24 } +); ``` -The host supplies the persistence callback supported by its installed Qarinah release. Qarinah receives cited, metadata-only outcomes with canonical input and output digests, the browser receipt hash, and evidence IDs as top-level context links. The recorder recursively removes authorization data, cookies, credentials, passwords, passphrases, secrets, tokens, storage values, form values, and API keys. It does not persist hidden reasoning or profile data. For consequential mutations, a host may link the sanitized outcome to a complete causal receipt chain when every stage exists; the recorder does not invent or require that chain. +The host supplies the persistence callback supported by its installed Qarinah release. The recorder emits a `cockroach.browser-memory.v2` event with type, session ID, optional actor, a SHA-256 `purposeDigest` instead of raw purpose, timestamp, optional canonical input/output digests, evidence IDs, optional browser receipt hash, and allowlisted bounded metadata. Its envelope contains no `sourceUrl`, raw page content, browser profile, hidden reasoning, or raw session purpose. Authorization data, cookies, credentials, passwords, passphrases, secrets, tokens, storage values, form values, and API keys are removed recursively. `contextRecorderTimeoutMs` bounds the recorder wait, defaults to 1,000 ms, and accepts integer values from 1 through 120,000 ms; a recorder rejection or timeout is reported operationally without replacing browser work. + +The optional context provider forwards the current session ID, agent task as query, a host-enforced character ceiling, a configured result limit from 1 to 128, and cancellation to the host-owned Qarinah source. Returned summaries require bounded citation IDs and may link receipt hashes and evidence IDs. The agent preserves citation anchors when it truncates a pack and serializes the historical content in a user-role message behind a trusted system boundary; Qarinah content never becomes a system instruction. Model actions are validated against the same public action schema used by runtime dispatch. Mixed action-and-finish output executes nothing, and every non-snapshot action is followed by a fresh bounded snapshot before a later finish can complete the run. For consequential mutations, a host may link the sanitized outcome to a complete causal receipt chain when every stage exists; neither adapter invents or requires that chain. ### Cockroach Crawler @@ -704,6 +744,8 @@ when it remains, and writes the result into a hash-linked action receipt. Headless mode is not a sandbox. Treat browser rendering, JavaScript, remote CDP, proxies, uploads, downloads, and imported profiles as privileged capabilities. +Application-level origin routing and periodic process telemetry are not kernel isolation. Put untrusted pages behind deployment-owned network controls and cgroup, container, Windows Job Object, or equivalent process limits. + Read the complete [security policy](./SECURITY.md) before exposing the runtime to another process. ## Documentation @@ -723,6 +765,8 @@ Read the complete [security policy](./SECURITY.md) before exposing the runtime t - [Cockroach Crawler handoff](./docs/crawler.md) - [ProductLoop OS](./docs/productloop.md) - [Deployment](./docs/deployment.md) +- [Headless compatibility and engine lanes](./docs/headless-compatibility.md) +- [Browser resource governance](./docs/resource-governance.md) - [Capability matrix](./docs/capabilities.md) - [Alternatives and product-layer comparison](https://cockroachbrowser.com/alternatives/) - [Security](./docs/security.md) diff --git a/docker-compose.yml b/docker-compose.yml index de308d1..c71c41a 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -2,7 +2,7 @@ services: browser: build: context: . - image: cockroach-browser:0.4.1 + image: cockroach-browser:0.5.0-rc.1 ports: - "127.0.0.1:43110:43111" restart: unless-stopped diff --git a/docs/Cockroach-Browser-Technical-White-Paper-v1.2-build.json b/docs/Cockroach-Browser-Technical-White-Paper-v1.2-build.json new file mode 100644 index 0000000..de12ba6 --- /dev/null +++ b/docs/Cockroach-Browser-Technical-White-Paper-v1.2-build.json @@ -0,0 +1,63 @@ +{ + "schemaVersion": "cockroach.white-paper-build.v1", + "paperVersion": "1.2", + "implementationVersion": "0.5.0-rc.1", + "publicationDate": "2026-09-03", + "gitBaseCommit": "deb6c25b4c0bc1927a631e3b062464b4f4cc2775", + "workingTreeDirty": true, + "inputs": [ + { + "path": "docs/whitepaper.md", + "bytes": 65102, + "sha256": "2460ad9cf2185c964b45b5a7607e25c35fad4ff5277d86a6256b16f2e60b423b" + }, + { + "path": "scripts/build-whitepaper-pdf.py", + "bytes": 18938, + "sha256": "db9eb6e2b26266e5a6da2f7fb382e726d8c43748f2f1fa9085278403b72a60ec" + }, + { + "path": "site/assets/logo.png", + "bytes": 948069, + "sha256": "5fad338e75ac88d018fcbde96b736ba4c540d988b2316d1aeb0c16dbfced5e89" + }, + { + "path": "C:/Windows/Fonts/arial.ttf", + "bytes": 1045720, + "sha256": "b3658eadae55e682b5f69eb64c439c1ecc8f196c0bb8d4756d145d13bc86476a" + }, + { + "path": "C:/Windows/Fonts/arialbd.ttf", + "bytes": 989780, + "sha256": "e8f4e3baf6cc35fed6fcce3a540e8b39e8f6cda1d22a28f2ec8f526fef7a43f5" + }, + { + "path": "C:/Windows/Fonts/ariali.ttf", + "bytes": 721584, + "sha256": "86b32db9a06f9694e2a3760c42e5117bcdc5cc1255bb5186ca8ce0305e22f288" + }, + { + "path": "C:/Windows/Fonts/consola.ttf", + "bytes": 453088, + "sha256": "cf00b507b3286870cc5064ebd0633c303f70b491a4af25eec2d32df413db0179" + } + ], + "outputs": [ + { + "path": "output/pdf/Cockroach-Browser-Technical-White-Paper-v1.2.pdf", + "bytes": 1406360, + "sha256": "6723c2e6a9c5ba639f637eb98281781585a5470075ac713a4d1ef0be15b097e3" + }, + { + "path": "docs/Cockroach-Browser-Technical-White-Paper-v1.2.pdf", + "bytes": 1406360, + "sha256": "6723c2e6a9c5ba639f637eb98281781585a5470075ac713a4d1ef0be15b097e3" + }, + { + "path": "site/paper/Cockroach-Browser-Technical-White-Paper-v1.2.pdf", + "bytes": 1406360, + "sha256": "6723c2e6a9c5ba639f637eb98281781585a5470075ac713a4d1ef0be15b097e3" + } + ], + "pdfSha256": "6723c2e6a9c5ba639f637eb98281781585a5470075ac713a4d1ef0be15b097e3" +} diff --git a/docs/Cockroach-Browser-Technical-White-Paper-v1.2.pdf b/docs/Cockroach-Browser-Technical-White-Paper-v1.2.pdf new file mode 100644 index 0000000..561b7eb Binary files /dev/null and b/docs/Cockroach-Browser-Technical-White-Paper-v1.2.pdf differ diff --git a/docs/Cockroach-Browser-Technical-White-Paper-v1.2.sha256 b/docs/Cockroach-Browser-Technical-White-Paper-v1.2.sha256 new file mode 100644 index 0000000..a6d6469 --- /dev/null +++ b/docs/Cockroach-Browser-Technical-White-Paper-v1.2.sha256 @@ -0,0 +1 @@ +6723c2e6a9c5ba639f637eb98281781585a5470075ac713a4d1ef0be15b097e3 Cockroach-Browser-Technical-White-Paper-v1.2.pdf diff --git a/docs/README.md b/docs/README.md index 98dc06e..33fed1a 100644 --- a/docs/README.md +++ b/docs/README.md @@ -13,7 +13,7 @@ The public documentation lives at https://cockroachbrowser.com/docs/. - [Actions and semantic refs](./actions.md) - [Operator runtime](./operator-runtime.md) - [Capture and evidence](./capture.md) -- [Network boundary](./network.md) +- [Network controls](./network.md) - [Files and downloads](./files.md) - [Audits and comparisons](./audits.md) - [Jobs and retries](./jobs.md) @@ -26,6 +26,9 @@ The public documentation lives at https://cockroachbrowser.com/docs/. - [Security](./security.md) - [Deployment](./deployment.md) - [Capability matrix](./capabilities.md) +- [Headless compatibility and engine lanes](./headless-compatibility.md) +- [Browser resource governance](./resource-governance.md) +- [Obscura non-visual measured proof](./benchmarks/obscura-non-visual-2026-09-03.md) - [Alternatives and product-layer comparison](https://cockroachbrowser.com/alternatives/) - [Complete Playwright and Puppeteer API inventory](https://cockroachbrowser.com/api-surface/) - [Technical white paper](./whitepaper.md) @@ -34,7 +37,7 @@ The public documentation lives at https://cockroachbrowser.com/docs/. - Cockroach Browser owns browser execution, tabs, semantic snapshots, browser evidence, audits, and authenticated worker transport. - Cockroach Crawler owns bounded public-web breadth, mapping, and extraction. -- Qarinah stores compact cited read outcomes but cannot dispatch browser actions. +- The optional Qarinah adapter emits versioned, metadata-only outcomes with evidence IDs and receipt hashes to a host-supplied sink; it cannot dispatch browser actions. - For browser operations routed through its adapter, Maqam owns policy, exact approval, replay protection, dispatch, and governance receipts. - ProductLoop OS composes package contracts without silently combining their ledgers or authority. diff --git a/docs/actions.md b/docs/actions.md index c21509e..dc2cf47 100644 --- a/docs/actions.md +++ b/docs/actions.md @@ -33,7 +33,9 @@ console.log(result.receipt.receiptHash); ## Supported interactions -Navigation, reload, back, forward, tab control, click, double-click, fill, type, press, select, check, uncheck, hover, focus, bounded scroll, low-level in-viewport mouse input, bounded keyboard input, drag, wait, dialog handling, session-history inspection, upload, download, extract, screenshot, PDF, tracing, and policy-gated JavaScript are available in the runtime.Each action is classified by effect and risk before dispatch. High-risk actions belong behind Maqam approval. +Navigation, reload, back, forward, tab control, click, double-click, fill, type, press, select, check, uncheck, hover, focus, bounded scroll, low-level in-viewport mouse input, bounded keyboard input, drag, wait, dialog handling, session-history inspection, upload, download, extract, screenshot, PDF, tracing, and policy-gated JavaScript are available in the runtime. + +Each action is classified by effect and risk before dispatch. High-risk actions belong behind Maqam approval. ## Target exact XPath and same-origin frames @@ -63,7 +65,7 @@ Expression evaluation is disabled unless the session policy allows JavaScript an ## Inspect a target without inventing a selector -query.inspect returns bounded text, cleaned HTML, attributes, geometry, form state, visibility, enabled state, and match counts for one semantic ref, CSS selector, or XPath. It is read-only, policy-evaluated, and receipt-linked. +query.inspect returns bounded text, raw element inner HTML, attributes, geometry, form state, visibility, enabled state, and match counts for one semantic ref, CSS selector, or XPath. It is read-only, policy-evaluated, and receipt-linked. Use extract.structured when sanitized HTML is required. ## Run an ordered bounded batch @@ -81,6 +83,6 @@ cockroach-browser batch \ emulation.set can apply bounded viewport, media, offline, geolocation, permissions, and non-secret headers after allowEmulation and exact approval. emulation.clear returns to the session baseline. These actions do not provide fingerprint evasion or access-control bypass. -## Release status +## Source status -This manual targets Cockroach Browser 0.4.1. Check [the capability matrix](https://cockroachbrowser.com/docs/capabilities/) before relying on a surface. Available means implemented in this release. Adapter means another authority or package is required. Planned means the surface is not part of this release. +This manual is generated from current `main` for the next Cockroach Browser release. Package examples still identify published line 0.5.0-rc.1 where shown; verify npm provenance and the matching tag before production use. Available means implemented in the current source tree, not necessarily published in 0.5.0-rc.1. Adapter means another authority or package is required. Planned means the surface is not implemented here. diff --git a/docs/audits.md b/docs/audits.md index d2815de..c09b2f0 100644 --- a/docs/audits.md +++ b/docs/audits.md @@ -30,6 +30,6 @@ Collect navigation timing, paint entries, transfer sizes, resource summaries, mi Compare a current screenshot with an explicit baseline, store the diff, and emit a mismatch percentage. Pin viewport, color scheme, browser version, data fixtures, and fonts for stable regression checks. -## Release status +## Source status -This manual targets Cockroach Browser 0.4.1. Check [the capability matrix](https://cockroachbrowser.com/docs/capabilities/) before relying on a surface. Available means implemented in this release. Adapter means another authority or package is required. Planned means the surface is not part of this release. +This manual is generated from current `main` for the next Cockroach Browser release. Package examples still identify published line 0.5.0-rc.1 where shown; verify npm provenance and the matching tag before production use. Available means implemented in the current source tree, not necessarily published in 0.5.0-rc.1. Adapter means another authority or package is required. Planned means the surface is not implemented here. diff --git a/docs/automation-platform.md b/docs/automation-platform.md index b4bc56f..106b40b 100644 --- a/docs/automation-platform.md +++ b/docs/automation-platform.md @@ -72,7 +72,7 @@ await safari.close(); ## Run the optional model gateway and finite-step agent -The OpenAI-compatible gateway resolves its API key in the trusted host, enforces request deadlines and response byte ceilings, and parses structured tool calls. The agent observes semantic snapshots, dispatches typed actions through BrowserRuntime, retains receipts, and stops at a configured step limit. A host can omit this layer and use any external planner. +The OpenAI-compatible gateway resolves its API key in the trusted host, applies its deadline to API-key-provider waiting and the HTTP exchange, enforces independent maxRequestBytes and maxResponseBytes ceilings, and parses structured tool calls. The agent validates every action payload against the published schema before runtime dispatch, refuses mixed action-and-finish output, observes a fresh bounded snapshot after every non-snapshot action, retains receipts, and accepts completion only on a later reviewed turn. maxContextChars bounds each serialized model turn, maxToolOutputChars bounds each tool result, and compaction removes only complete older assistant/tool rounds while retaining digest, receipt, evidence, and citation anchors. Optional cited history is serialized as an untrusted user-role observation behind the trusted system boundary, never as an instruction or authority. A host can omit this layer and use any external planner. ``` import { BrowserAgent } from "cockroach-browser/agent"; @@ -81,9 +81,17 @@ import { OpenAICompatibleModelGateway } from "cockroach-browser/model-gateway"; const gateway = new OpenAICompatibleModelGateway({ endpoint: process.env.MODEL_ENDPOINT, model: process.env.MODEL_NAME, - apiKeyProvider: () => process.env.MODEL_API_KEY + apiKeyProvider: () => process.env.MODEL_API_KEY, + maxRequestBytes: 4 * 1024 * 1024, + maxResponseBytes: 8 * 1024 * 1024 +}); +const agent = new BrowserAgent({ + runtime, + gateway, + maxSteps: 30, + maxContextChars: 128_000, + maxToolOutputChars: 32_000 }); -const agent = new BrowserAgent({ runtime, gateway, maxSteps: 30 }); const result = await agent.run({ sessionId: session.id, task: "Inspect the release page and report the visible version" @@ -125,6 +133,6 @@ TypeScript is the native embedded and daemon SDK. Dependency-light Python, Java, ``` -## Release status +## Source status -This manual targets Cockroach Browser 0.4.1. Check [the capability matrix](https://cockroachbrowser.com/docs/capabilities/) before relying on a surface. Available means implemented in this release. Adapter means another authority or package is required. Planned means the surface is not part of this release. +This manual is generated from current `main` for the next Cockroach Browser release. Package examples still identify published line 0.5.0-rc.1 where shown; verify npm provenance and the matching tag before production use. Available means implemented in the current source tree, not necessarily published in 0.5.0-rc.1. Adapter means another authority or package is required. Planned means the surface is not implemented here. diff --git a/docs/benchmarks/artifacts/obscura-0.2.1-constrained-non-visual-25mib-2026-09-03-rc1.json b/docs/benchmarks/artifacts/obscura-0.2.1-constrained-non-visual-25mib-2026-09-03-rc1.json new file mode 100644 index 0000000..45380e6 --- /dev/null +++ b/docs/benchmarks/artifacts/obscura-0.2.1-constrained-non-visual-25mib-2026-09-03-rc1.json @@ -0,0 +1,3921 @@ +{ + "schemaVersion": "cockroach.lightweight-browser-benchmark.v2", + "generatedAt": "2026-09-02T20:39:06.900Z", + "implementation": "obscura", + "resourceProfile": "constrained", + "renderingPolicy": "visual-actions-denied", + "executable": { + "name": "obscura.exe", + "bytes": 58097152, + "sha256": "5b609fb46bc00da79e450fb0fbd34bd442e565b1394f4af95433e0b341078221", + "version": "obscura 0.2.1" + }, + "sourceIdentity": { + "gitCommit": "deb6c25b4c0bc1927a631e3b062464b4f4cc2775", + "workingTreeDirty": true, + "sourceScope": "Runtime, schemas, benchmark/package/build configuration, and tests; generated documentation and benchmark artifacts are excluded.", + "identityNormalization": "Relative paths use forward slashes and text line endings are normalized to LF before hashing.", + "sourceFileCount": 100, + "sourceTreeSha256": "fb0c4597e39f319dd9b6f3bab02777c395e9d8d84906981bf939a39b470e7279", + "runtimeBuildFileCount": 200, + "runtimeBuildSha256": "6738efa4000ba482db83c9dc95ba2f21caed31de96f30dcd342e5dc722d86025", + "benchmarkScriptSha256": "08a5294f2d446765f712b93c9bfaaca010b1d043ded638d1f4f57b5038c97e86" + }, + "machine": { + "platform": "win32", + "architecture": "x64", + "release": "10.0.26200", + "logicalCpuCount": 16, + "cpuModel": "AMD Ryzen 7 4800H with Radeon Graphics ", + "totalMemoryBytes": 16557887488, + "freeMemoryBytesAtReport": 3987808256, + "node": "v24.15.0" + }, + "methodology": { + "warmup": 1, + "iterations": 20, + "settleMs": 500, + "startupTimeoutMs": 15000, + "actionTimeoutMs": 10000, + "teardownTimeoutMs": 5000, + "resourceSamples": 10, + "resourceSampleIntervalMs": 25, + "targetMiB": 25, + "targetDefinition": "Every measured owned-browser process-tree RSS sample is at or below the target and every required CDP/JavaScript/DOM/form/screenshot-preflight/teardown check passes.", + "memoryDefinition": "Aggregate RSS for the complete owned browser process tree; the Node coordinator is disclosed separately and is not hidden in browser RSS.", + "renderingControl": "Cockroach Browser declares a no-visual-action policy and rejects capture at preflight. This does not assert that the selected engine binary exposes or received a renderer-disable launch switch.", + "workload": "One runtime-owned loopback CDP server, one non-visual data-URL document, JavaScript evaluation, DOM query, input, and click.", + "requiredChecks": [ + "connect", + "javascript", + "dom", + "forms", + "screenshot", + "teardown" + ], + "optionalChecks": [], + "energy": { + "available": false, + "reason": "No calibrated platform energy counter is available; CPU time is not watts." + } + }, + "verdict": { + "passed": false, + "everyRunWithinTarget": false, + "requiredConformancePassed": true, + "targetBytes": 26214400, + "maxObservedBrowserTreeRssBytes": 29679616 + }, + "summary": { + "peakBrowserTreeRssBytes": { + "median": 29323264, + "p95": 29634560, + "min": 28831744, + "max": 29679616 + }, + "browserCpuTimeMs": { + "median": 94, + "p95": 156, + "min": 31, + "max": 156 + }, + "processCount": { + "median": 1, + "p95": 1, + "min": 1, + "max": 1 + }, + "launchMs": { + "median": 787.0129000000015, + "p95": 809.0815000000002, + "min": 752.3525000000373, + "max": 828.5519000000004 + }, + "connectMs": { + "median": 9.100799999985611, + "p95": 11.477599999998347, + "min": 6.7515000000130385, + "max": 11.787299999996321 + }, + "workloadMs": { + "median": 77.9427999999898, + "p95": 88.92900000000009, + "min": 72.45730000000913, + "max": 93.07159999999567 + }, + "shutdownMs": { + "median": 685.1016999999993, + "p95": 698.9339000000036, + "min": 669.2262000000046, + "max": 728.0233000000007 + }, + "coordinatorPeakRssBytes": { + "median": 113401856, + "p95": 128679936, + "min": 102383616, + "max": 128679936 + } + }, + "samples": [ + { + "timings": { + "launchMs": 828.5519000000004, + "connectMs": 9.685999999999694, + "workloadMs": 88.92900000000009, + "shutdownMs": 687.1514999999999 + }, + "browserTree": { + "peakRssBytes": 29380608, + "cpuTimeMs": 78, + "processCount": 1, + "sampleCount": 24, + "observations": [ + { + "sampledAt": "2026-09-02T20:34:35.891Z", + "rssBytes": 16130048, + "cpuTimeMs": 31, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:34:36.463Z", + "rssBytes": 29380608, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:34:37.022Z", + "rssBytes": 29380608, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:34:37.544Z", + "rssBytes": 29380608, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:34:38.064Z", + "rssBytes": 29380608, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:34:38.583Z", + "rssBytes": 29380608, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:34:39.102Z", + "rssBytes": 29380608, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:34:39.624Z", + "rssBytes": 29380608, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:34:40.151Z", + "rssBytes": 29380608, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:34:40.666Z", + "rssBytes": 29380608, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:34:41.187Z", + "rssBytes": 29380608, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:34:41.701Z", + "rssBytes": 29380608, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:34:42.219Z", + "rssBytes": 29380608, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:34:42.732Z", + "rssBytes": 29380608, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:34:43.288Z", + "rssBytes": 29380608, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:34:43.809Z", + "rssBytes": 29380608, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:34:44.338Z", + "rssBytes": 29380608, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:34:44.867Z", + "rssBytes": 29380608, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:34:45.392Z", + "rssBytes": 29380608, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:34:45.914Z", + "rssBytes": 29380608, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:34:46.426Z", + "rssBytes": 29380608, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:34:46.949Z", + "rssBytes": 29380608, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:34:47.464Z", + "rssBytes": 29380608, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:34:47.987Z", + "rssBytes": 29380608, + "cpuTimeMs": 78, + "processCount": 1 + } + ] + }, + "coordinator": { + "peakRssBytes": 114233344, + "cpuTimeMs": 765 + }, + "conformance": { + "connect": { + "passed": true + }, + "javascript": { + "passed": true + }, + "dom": { + "passed": true + }, + "forms": { + "passed": true, + "actionability": "dom-non-visual" + }, + "screenshot": { + "passed": true, + "supported": false, + "state": "unsupported", + "enforcement": "preflight" + }, + "teardown": { + "passed": true, + "rootProcessExited": true, + "transportDisposed": true + } + } + }, + { + "timings": { + "launchMs": 794.870799999997, + "connectMs": 8.71979999999894, + "workloadMs": 84.07210000000123, + "shutdownMs": 689.582699999999 + }, + "browserTree": { + "peakRssBytes": 29323264, + "cpuTimeMs": 47, + "processCount": 1, + "sampleCount": 24, + "observations": [ + { + "sampledAt": "2026-09-02T20:34:49.375Z", + "rssBytes": 16150528, + "cpuTimeMs": 16, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:34:49.933Z", + "rssBytes": 29323264, + "cpuTimeMs": 47, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:34:50.488Z", + "rssBytes": 29323264, + "cpuTimeMs": 47, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:34:51.024Z", + "rssBytes": 29323264, + "cpuTimeMs": 47, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:34:51.550Z", + "rssBytes": 29323264, + "cpuTimeMs": 47, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:34:52.073Z", + "rssBytes": 29323264, + "cpuTimeMs": 47, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:34:52.592Z", + "rssBytes": 29323264, + "cpuTimeMs": 47, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:34:53.127Z", + "rssBytes": 29323264, + "cpuTimeMs": 47, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:34:53.661Z", + "rssBytes": 29323264, + "cpuTimeMs": 47, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:34:54.181Z", + "rssBytes": 29323264, + "cpuTimeMs": 47, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:34:54.704Z", + "rssBytes": 29323264, + "cpuTimeMs": 47, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:34:55.222Z", + "rssBytes": 29323264, + "cpuTimeMs": 47, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:34:55.741Z", + "rssBytes": 29323264, + "cpuTimeMs": 47, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:34:56.260Z", + "rssBytes": 29323264, + "cpuTimeMs": 47, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:34:56.778Z", + "rssBytes": 29323264, + "cpuTimeMs": 47, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:34:57.301Z", + "rssBytes": 29323264, + "cpuTimeMs": 47, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:34:57.828Z", + "rssBytes": 29323264, + "cpuTimeMs": 47, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:34:58.356Z", + "rssBytes": 29323264, + "cpuTimeMs": 47, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:34:58.884Z", + "rssBytes": 29323264, + "cpuTimeMs": 47, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:34:59.431Z", + "rssBytes": 29323264, + "cpuTimeMs": 47, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:34:59.960Z", + "rssBytes": 29323264, + "cpuTimeMs": 47, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:35:00.488Z", + "rssBytes": 29323264, + "cpuTimeMs": 47, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:35:01.044Z", + "rssBytes": 29323264, + "cpuTimeMs": 47, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:35:01.564Z", + "rssBytes": 29323264, + "cpuTimeMs": 47, + "processCount": 1 + } + ] + }, + "coordinator": { + "peakRssBytes": 112324608, + "cpuTimeMs": 578 + }, + "conformance": { + "connect": { + "passed": true + }, + "javascript": { + "passed": true + }, + "dom": { + "passed": true + }, + "forms": { + "passed": true, + "actionability": "dom-non-visual" + }, + "screenshot": { + "passed": true, + "supported": false, + "state": "unsupported", + "enforcement": "preflight" + }, + "teardown": { + "passed": true, + "rootProcessExited": true, + "transportDisposed": true + } + } + }, + { + "timings": { + "launchMs": 809.0815000000002, + "connectMs": 10.18119999999908, + "workloadMs": 81.0222999999969, + "shutdownMs": 695.5594000000056 + }, + "browserTree": { + "peakRssBytes": 29052928, + "cpuTimeMs": 94, + "processCount": 1, + "sampleCount": 24, + "observations": [ + { + "sampledAt": "2026-09-02T20:35:02.950Z", + "rssBytes": 16203776, + "cpuTimeMs": 31, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:35:03.523Z", + "rssBytes": 29052928, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:35:04.078Z", + "rssBytes": 29052928, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:35:04.633Z", + "rssBytes": 29052928, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:35:05.197Z", + "rssBytes": 29052928, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:35:05.766Z", + "rssBytes": 29052928, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:35:06.326Z", + "rssBytes": 29052928, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:35:06.893Z", + "rssBytes": 29052928, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:35:07.456Z", + "rssBytes": 29052928, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:35:08.038Z", + "rssBytes": 29052928, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:35:08.625Z", + "rssBytes": 29052928, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:35:09.152Z", + "rssBytes": 29052928, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:35:09.682Z", + "rssBytes": 29052928, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:35:10.202Z", + "rssBytes": 29052928, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:35:10.723Z", + "rssBytes": 29052928, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:35:11.248Z", + "rssBytes": 29052928, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:35:11.776Z", + "rssBytes": 29052928, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:35:12.295Z", + "rssBytes": 29052928, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:35:12.822Z", + "rssBytes": 29052928, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:35:13.337Z", + "rssBytes": 29052928, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:35:13.866Z", + "rssBytes": 29052928, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:35:14.414Z", + "rssBytes": 29052928, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:35:14.944Z", + "rssBytes": 29052928, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:35:15.468Z", + "rssBytes": 29052928, + "cpuTimeMs": 94, + "processCount": 1 + } + ] + }, + "coordinator": { + "peakRssBytes": 106496000, + "cpuTimeMs": 703 + }, + "conformance": { + "connect": { + "passed": true + }, + "javascript": { + "passed": true + }, + "dom": { + "passed": true + }, + "forms": { + "passed": true, + "actionability": "dom-non-visual" + }, + "screenshot": { + "passed": true, + "supported": false, + "state": "unsupported", + "enforcement": "preflight" + }, + "teardown": { + "passed": true, + "rootProcessExited": true, + "transportDisposed": true + } + } + }, + { + "timings": { + "launchMs": 800.8223000000071, + "connectMs": 11.787299999996321, + "workloadMs": 78.87339999999676, + "shutdownMs": 685.1016999999993 + }, + "browserTree": { + "peakRssBytes": 29020160, + "cpuTimeMs": 78, + "processCount": 1, + "sampleCount": 24, + "observations": [ + { + "sampledAt": "2026-09-02T20:35:16.858Z", + "rssBytes": 16195584, + "cpuTimeMs": 16, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:35:17.418Z", + "rssBytes": 29020160, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:35:17.973Z", + "rssBytes": 29020160, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:35:18.492Z", + "rssBytes": 29020160, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:35:19.015Z", + "rssBytes": 29020160, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:35:19.544Z", + "rssBytes": 29020160, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:35:20.063Z", + "rssBytes": 29020160, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:35:20.585Z", + "rssBytes": 29020160, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:35:21.109Z", + "rssBytes": 29020160, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:35:21.636Z", + "rssBytes": 29020160, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:35:22.168Z", + "rssBytes": 29020160, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:35:22.685Z", + "rssBytes": 29020160, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:35:23.206Z", + "rssBytes": 29020160, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:35:23.728Z", + "rssBytes": 29020160, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:35:24.248Z", + "rssBytes": 29020160, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:35:24.772Z", + "rssBytes": 29020160, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:35:25.297Z", + "rssBytes": 29020160, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:35:25.823Z", + "rssBytes": 29020160, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:35:26.344Z", + "rssBytes": 29020160, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:35:26.882Z", + "rssBytes": 29020160, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:35:27.411Z", + "rssBytes": 29020160, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:35:27.937Z", + "rssBytes": 29020160, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:35:28.454Z", + "rssBytes": 29020160, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:35:28.977Z", + "rssBytes": 29020160, + "cpuTimeMs": 78, + "processCount": 1 + } + ] + }, + "coordinator": { + "peakRssBytes": 102383616, + "cpuTimeMs": 594 + }, + "conformance": { + "connect": { + "passed": true + }, + "javascript": { + "passed": true + }, + "dom": { + "passed": true + }, + "forms": { + "passed": true, + "actionability": "dom-non-visual" + }, + "screenshot": { + "passed": true, + "supported": false, + "state": "unsupported", + "enforcement": "preflight" + }, + "teardown": { + "passed": true, + "rootProcessExited": true, + "transportDisposed": true + } + } + }, + { + "timings": { + "launchMs": 800.002999999997, + "connectMs": 9.858800000001793, + "workloadMs": 74.75309999998717, + "shutdownMs": 728.0233000000007 + }, + "browserTree": { + "peakRssBytes": 29335552, + "cpuTimeMs": 141, + "processCount": 1, + "sampleCount": 24, + "observations": [ + { + "sampledAt": "2026-09-02T20:35:30.344Z", + "rssBytes": 16187392, + "cpuTimeMs": 16, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:35:30.902Z", + "rssBytes": 29335552, + "cpuTimeMs": 141, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:35:31.460Z", + "rssBytes": 29335552, + "cpuTimeMs": 141, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:35:31.985Z", + "rssBytes": 29335552, + "cpuTimeMs": 141, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:35:32.507Z", + "rssBytes": 29335552, + "cpuTimeMs": 141, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:35:33.034Z", + "rssBytes": 29335552, + "cpuTimeMs": 141, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:35:33.554Z", + "rssBytes": 29335552, + "cpuTimeMs": 141, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:35:34.075Z", + "rssBytes": 29335552, + "cpuTimeMs": 141, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:35:34.597Z", + "rssBytes": 29335552, + "cpuTimeMs": 141, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:35:35.138Z", + "rssBytes": 29335552, + "cpuTimeMs": 141, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:35:35.698Z", + "rssBytes": 29335552, + "cpuTimeMs": 141, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:35:36.225Z", + "rssBytes": 29335552, + "cpuTimeMs": 141, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:35:36.757Z", + "rssBytes": 29335552, + "cpuTimeMs": 141, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:35:37.276Z", + "rssBytes": 29335552, + "cpuTimeMs": 141, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:35:37.815Z", + "rssBytes": 29335552, + "cpuTimeMs": 141, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:35:38.370Z", + "rssBytes": 29335552, + "cpuTimeMs": 141, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:35:38.907Z", + "rssBytes": 29335552, + "cpuTimeMs": 141, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:35:39.438Z", + "rssBytes": 29335552, + "cpuTimeMs": 141, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:35:39.983Z", + "rssBytes": 29335552, + "cpuTimeMs": 141, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:35:40.508Z", + "rssBytes": 29335552, + "cpuTimeMs": 141, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:35:41.049Z", + "rssBytes": 29335552, + "cpuTimeMs": 141, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:35:41.576Z", + "rssBytes": 29335552, + "cpuTimeMs": 141, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:35:42.107Z", + "rssBytes": 29335552, + "cpuTimeMs": 141, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:35:42.625Z", + "rssBytes": 29335552, + "cpuTimeMs": 141, + "processCount": 1 + } + ] + }, + "coordinator": { + "peakRssBytes": 102449152, + "cpuTimeMs": 641 + }, + "conformance": { + "connect": { + "passed": true + }, + "javascript": { + "passed": true + }, + "dom": { + "passed": true + }, + "forms": { + "passed": true, + "actionability": "dom-non-visual" + }, + "screenshot": { + "passed": true, + "supported": false, + "state": "unsupported", + "enforcement": "preflight" + }, + "teardown": { + "passed": true, + "rootProcessExited": true, + "transportDisposed": true + } + } + }, + { + "timings": { + "launchMs": 807.7658000000083, + "connectMs": 9.999499999990803, + "workloadMs": 74.8521999999939, + "shutdownMs": 686.9419000000053 + }, + "browserTree": { + "peakRssBytes": 29634560, + "cpuTimeMs": 156, + "processCount": 1, + "sampleCount": 24, + "observations": [ + { + "sampledAt": "2026-09-02T20:35:44.046Z", + "rssBytes": 16175104, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:35:44.616Z", + "rssBytes": 29634560, + "cpuTimeMs": 156, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:35:45.186Z", + "rssBytes": 29634560, + "cpuTimeMs": 156, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:35:45.708Z", + "rssBytes": 29634560, + "cpuTimeMs": 156, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:35:46.236Z", + "rssBytes": 29634560, + "cpuTimeMs": 156, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:35:46.762Z", + "rssBytes": 29634560, + "cpuTimeMs": 156, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:35:47.284Z", + "rssBytes": 29634560, + "cpuTimeMs": 156, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:35:47.810Z", + "rssBytes": 29634560, + "cpuTimeMs": 156, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:35:48.324Z", + "rssBytes": 29634560, + "cpuTimeMs": 156, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:35:48.849Z", + "rssBytes": 29634560, + "cpuTimeMs": 156, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:35:49.372Z", + "rssBytes": 29634560, + "cpuTimeMs": 156, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:35:49.893Z", + "rssBytes": 29634560, + "cpuTimeMs": 156, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:35:50.411Z", + "rssBytes": 29634560, + "cpuTimeMs": 156, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:35:50.933Z", + "rssBytes": 29634560, + "cpuTimeMs": 156, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:35:51.455Z", + "rssBytes": 29634560, + "cpuTimeMs": 156, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:35:51.975Z", + "rssBytes": 29634560, + "cpuTimeMs": 156, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:35:52.496Z", + "rssBytes": 29634560, + "cpuTimeMs": 156, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:35:53.019Z", + "rssBytes": 29634560, + "cpuTimeMs": 156, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:35:53.546Z", + "rssBytes": 29634560, + "cpuTimeMs": 156, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:35:54.068Z", + "rssBytes": 29634560, + "cpuTimeMs": 156, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:35:54.596Z", + "rssBytes": 29634560, + "cpuTimeMs": 156, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:35:55.119Z", + "rssBytes": 29634560, + "cpuTimeMs": 156, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:35:55.642Z", + "rssBytes": 29634560, + "cpuTimeMs": 156, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:35:56.156Z", + "rssBytes": 29634560, + "cpuTimeMs": 156, + "processCount": 1 + } + ] + }, + "coordinator": { + "peakRssBytes": 109404160, + "cpuTimeMs": 546 + }, + "conformance": { + "connect": { + "passed": true + }, + "javascript": { + "passed": true + }, + "dom": { + "passed": true + }, + "forms": { + "passed": true, + "actionability": "dom-non-visual" + }, + "screenshot": { + "passed": true, + "supported": false, + "state": "unsupported", + "enforcement": "preflight" + }, + "teardown": { + "passed": true, + "rootProcessExited": true, + "transportDisposed": true + } + } + }, + { + "timings": { + "launchMs": 806.6667000000016, + "connectMs": 11.054300000003423, + "workloadMs": 73.71199999999953, + "shutdownMs": 698.9339000000036 + }, + "browserTree": { + "peakRssBytes": 28917760, + "cpuTimeMs": 31, + "processCount": 1, + "sampleCount": 24, + "observations": [ + { + "sampledAt": "2026-09-02T20:35:57.536Z", + "rssBytes": 16162816, + "cpuTimeMs": 16, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:35:58.100Z", + "rssBytes": 28917760, + "cpuTimeMs": 31, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:35:58.651Z", + "rssBytes": 28917760, + "cpuTimeMs": 31, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:35:59.168Z", + "rssBytes": 28917760, + "cpuTimeMs": 31, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:35:59.689Z", + "rssBytes": 28917760, + "cpuTimeMs": 31, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:36:00.212Z", + "rssBytes": 28917760, + "cpuTimeMs": 31, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:36:00.785Z", + "rssBytes": 28917760, + "cpuTimeMs": 31, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:36:01.308Z", + "rssBytes": 28917760, + "cpuTimeMs": 31, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:36:01.830Z", + "rssBytes": 28917760, + "cpuTimeMs": 31, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:36:02.350Z", + "rssBytes": 28917760, + "cpuTimeMs": 31, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:36:02.877Z", + "rssBytes": 28917760, + "cpuTimeMs": 31, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:36:03.408Z", + "rssBytes": 28917760, + "cpuTimeMs": 31, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:36:03.945Z", + "rssBytes": 28917760, + "cpuTimeMs": 31, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:36:04.471Z", + "rssBytes": 28917760, + "cpuTimeMs": 31, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:36:05.007Z", + "rssBytes": 28917760, + "cpuTimeMs": 31, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:36:05.538Z", + "rssBytes": 28917760, + "cpuTimeMs": 31, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:36:06.076Z", + "rssBytes": 28917760, + "cpuTimeMs": 31, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:36:06.611Z", + "rssBytes": 28917760, + "cpuTimeMs": 31, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:36:07.148Z", + "rssBytes": 28917760, + "cpuTimeMs": 31, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:36:07.692Z", + "rssBytes": 28917760, + "cpuTimeMs": 31, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:36:08.222Z", + "rssBytes": 28917760, + "cpuTimeMs": 31, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:36:08.739Z", + "rssBytes": 28917760, + "cpuTimeMs": 31, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:36:09.256Z", + "rssBytes": 28917760, + "cpuTimeMs": 31, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:36:09.781Z", + "rssBytes": 28917760, + "cpuTimeMs": 31, + "processCount": 1 + } + ] + }, + "coordinator": { + "peakRssBytes": 106123264, + "cpuTimeMs": 657 + }, + "conformance": { + "connect": { + "passed": true + }, + "javascript": { + "passed": true + }, + "dom": { + "passed": true + }, + "forms": { + "passed": true, + "actionability": "dom-non-visual" + }, + "screenshot": { + "passed": true, + "supported": false, + "state": "unsupported", + "enforcement": "preflight" + }, + "teardown": { + "passed": true, + "rootProcessExited": true, + "transportDisposed": true + } + } + }, + { + "timings": { + "launchMs": 808.5942000000068, + "connectMs": 10.643600000010338, + "workloadMs": 79.13899999999558, + "shutdownMs": 689.6215999999986 + }, + "browserTree": { + "peakRssBytes": 29679616, + "cpuTimeMs": 78, + "processCount": 1, + "sampleCount": 24, + "observations": [ + { + "sampledAt": "2026-09-02T20:36:11.178Z", + "rssBytes": 16351232, + "cpuTimeMs": 16, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:36:11.740Z", + "rssBytes": 29679616, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:36:12.305Z", + "rssBytes": 29679616, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:36:12.829Z", + "rssBytes": 29679616, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:36:13.348Z", + "rssBytes": 29679616, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:36:13.873Z", + "rssBytes": 29679616, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:36:14.392Z", + "rssBytes": 29679616, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:36:14.916Z", + "rssBytes": 29679616, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:36:15.438Z", + "rssBytes": 29679616, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:36:15.966Z", + "rssBytes": 29679616, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:36:16.485Z", + "rssBytes": 29679616, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:36:17.043Z", + "rssBytes": 29679616, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:36:17.574Z", + "rssBytes": 29679616, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:36:18.101Z", + "rssBytes": 29679616, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:36:18.626Z", + "rssBytes": 29679616, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:36:19.181Z", + "rssBytes": 29679616, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:36:19.739Z", + "rssBytes": 29679616, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:36:20.274Z", + "rssBytes": 29679616, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:36:20.796Z", + "rssBytes": 29679616, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:36:21.319Z", + "rssBytes": 29679616, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:36:21.879Z", + "rssBytes": 29679616, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:36:22.422Z", + "rssBytes": 29679616, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:36:22.942Z", + "rssBytes": 29679616, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:36:23.464Z", + "rssBytes": 29679616, + "cpuTimeMs": 78, + "processCount": 1 + } + ] + }, + "coordinator": { + "peakRssBytes": 106692608, + "cpuTimeMs": 640 + }, + "conformance": { + "connect": { + "passed": true + }, + "javascript": { + "passed": true + }, + "dom": { + "passed": true + }, + "forms": { + "passed": true, + "actionability": "dom-non-visual" + }, + "screenshot": { + "passed": true, + "supported": false, + "state": "unsupported", + "enforcement": "preflight" + }, + "teardown": { + "passed": true, + "rootProcessExited": true, + "transportDisposed": true + } + } + }, + { + "timings": { + "launchMs": 782.8353999999963, + "connectMs": 11.477599999998347, + "workloadMs": 76.70500000000175, + "shutdownMs": 693.0057000000088 + }, + "browserTree": { + "peakRssBytes": 29507584, + "cpuTimeMs": 109, + "processCount": 1, + "sampleCount": 24, + "observations": [ + { + "sampledAt": "2026-09-02T20:36:24.831Z", + "rssBytes": 16191488, + "cpuTimeMs": 47, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:36:25.393Z", + "rssBytes": 29507584, + "cpuTimeMs": 109, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:36:25.946Z", + "rssBytes": 29507584, + "cpuTimeMs": 109, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:36:26.465Z", + "rssBytes": 29507584, + "cpuTimeMs": 109, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:36:26.986Z", + "rssBytes": 29507584, + "cpuTimeMs": 109, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:36:27.511Z", + "rssBytes": 29507584, + "cpuTimeMs": 109, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:36:28.032Z", + "rssBytes": 29507584, + "cpuTimeMs": 109, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:36:28.555Z", + "rssBytes": 29507584, + "cpuTimeMs": 109, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:36:29.079Z", + "rssBytes": 29507584, + "cpuTimeMs": 109, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:36:29.596Z", + "rssBytes": 29507584, + "cpuTimeMs": 109, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:36:30.120Z", + "rssBytes": 29507584, + "cpuTimeMs": 109, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:36:30.641Z", + "rssBytes": 29507584, + "cpuTimeMs": 109, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:36:31.166Z", + "rssBytes": 29507584, + "cpuTimeMs": 109, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:36:31.684Z", + "rssBytes": 29507584, + "cpuTimeMs": 109, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:36:32.210Z", + "rssBytes": 29507584, + "cpuTimeMs": 109, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:36:32.730Z", + "rssBytes": 29507584, + "cpuTimeMs": 109, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:36:33.275Z", + "rssBytes": 29507584, + "cpuTimeMs": 109, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:36:33.860Z", + "rssBytes": 29507584, + "cpuTimeMs": 109, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:36:34.386Z", + "rssBytes": 29507584, + "cpuTimeMs": 109, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:36:34.910Z", + "rssBytes": 29507584, + "cpuTimeMs": 109, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:36:35.441Z", + "rssBytes": 29507584, + "cpuTimeMs": 109, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:36:35.974Z", + "rssBytes": 29507584, + "cpuTimeMs": 109, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:36:36.498Z", + "rssBytes": 29507584, + "cpuTimeMs": 109, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:36:37.018Z", + "rssBytes": 29507584, + "cpuTimeMs": 109, + "processCount": 1 + } + ] + }, + "coordinator": { + "peakRssBytes": 110948352, + "cpuTimeMs": 578 + }, + "conformance": { + "connect": { + "passed": true + }, + "javascript": { + "passed": true + }, + "dom": { + "passed": true + }, + "forms": { + "passed": true, + "actionability": "dom-non-visual" + }, + "screenshot": { + "passed": true, + "supported": false, + "state": "unsupported", + "enforcement": "preflight" + }, + "teardown": { + "passed": true, + "rootProcessExited": true, + "transportDisposed": true + } + } + }, + { + "timings": { + "launchMs": 786.0217999999877, + "connectMs": 9.100799999985611, + "workloadMs": 77.5228000000061, + "shutdownMs": 672.8160999999964 + }, + "browserTree": { + "peakRssBytes": 28942336, + "cpuTimeMs": 94, + "processCount": 1, + "sampleCount": 24, + "observations": [ + { + "sampledAt": "2026-09-02T20:36:38.397Z", + "rssBytes": 16154624, + "cpuTimeMs": 31, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:36:38.954Z", + "rssBytes": 28942336, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:36:39.496Z", + "rssBytes": 28942336, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:36:40.014Z", + "rssBytes": 28942336, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:36:40.535Z", + "rssBytes": 28942336, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:36:41.061Z", + "rssBytes": 28942336, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:36:41.607Z", + "rssBytes": 28942336, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:36:42.146Z", + "rssBytes": 28942336, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:36:42.677Z", + "rssBytes": 28942336, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:36:43.239Z", + "rssBytes": 28942336, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:36:43.788Z", + "rssBytes": 28942336, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:36:44.328Z", + "rssBytes": 28942336, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:36:44.908Z", + "rssBytes": 28942336, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:36:45.462Z", + "rssBytes": 28942336, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:36:46.027Z", + "rssBytes": 28942336, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:36:46.564Z", + "rssBytes": 28942336, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:36:47.105Z", + "rssBytes": 28942336, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:36:47.647Z", + "rssBytes": 28942336, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:36:48.196Z", + "rssBytes": 28942336, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:36:48.723Z", + "rssBytes": 28942336, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:36:49.250Z", + "rssBytes": 28942336, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:36:49.769Z", + "rssBytes": 28942336, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:36:50.283Z", + "rssBytes": 28942336, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:36:50.802Z", + "rssBytes": 28942336, + "cpuTimeMs": 94, + "processCount": 1 + } + ] + }, + "coordinator": { + "peakRssBytes": 116285440, + "cpuTimeMs": 516 + }, + "conformance": { + "connect": { + "passed": true + }, + "javascript": { + "passed": true + }, + "dom": { + "passed": true + }, + "forms": { + "passed": true, + "actionability": "dom-non-visual" + }, + "screenshot": { + "passed": true, + "supported": false, + "state": "unsupported", + "enforcement": "preflight" + }, + "teardown": { + "passed": true, + "rootProcessExited": true, + "transportDisposed": true + } + } + }, + { + "timings": { + "launchMs": 774.1764000000258, + "connectMs": 7.776699999987613, + "workloadMs": 78.79800000000978, + "shutdownMs": 681.9098999999987 + }, + "browserTree": { + "peakRssBytes": 29384704, + "cpuTimeMs": 78, + "processCount": 1, + "sampleCount": 24, + "observations": [ + { + "sampledAt": "2026-09-02T20:36:52.144Z", + "rssBytes": 16154624, + "cpuTimeMs": 16, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:36:52.713Z", + "rssBytes": 29384704, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:36:53.268Z", + "rssBytes": 29384704, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:36:53.785Z", + "rssBytes": 29384704, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:36:54.304Z", + "rssBytes": 29384704, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:36:54.830Z", + "rssBytes": 29384704, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:36:55.349Z", + "rssBytes": 29384704, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:36:55.869Z", + "rssBytes": 29384704, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:36:56.385Z", + "rssBytes": 29384704, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:36:56.902Z", + "rssBytes": 29384704, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:36:57.422Z", + "rssBytes": 29384704, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:36:57.944Z", + "rssBytes": 29384704, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:36:58.499Z", + "rssBytes": 29384704, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:36:59.029Z", + "rssBytes": 29384704, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:36:59.549Z", + "rssBytes": 29384704, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:37:00.078Z", + "rssBytes": 29384704, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:37:00.606Z", + "rssBytes": 29384704, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:37:01.153Z", + "rssBytes": 29384704, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:37:01.669Z", + "rssBytes": 29384704, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:37:02.186Z", + "rssBytes": 29384704, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:37:02.711Z", + "rssBytes": 29384704, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:37:03.227Z", + "rssBytes": 29384704, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:37:03.747Z", + "rssBytes": 29384704, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:37:04.267Z", + "rssBytes": 29384704, + "cpuTimeMs": 78, + "processCount": 1 + } + ] + }, + "coordinator": { + "peakRssBytes": 113401856, + "cpuTimeMs": 563 + }, + "conformance": { + "connect": { + "passed": true + }, + "javascript": { + "passed": true + }, + "dom": { + "passed": true + }, + "forms": { + "passed": true, + "actionability": "dom-non-visual" + }, + "screenshot": { + "passed": true, + "supported": false, + "state": "unsupported", + "enforcement": "preflight" + }, + "teardown": { + "passed": true, + "rootProcessExited": true, + "transportDisposed": true + } + } + }, + { + "timings": { + "launchMs": 769.2356999999902, + "connectMs": 8.238200000021607, + "workloadMs": 82.50559999997495, + "shutdownMs": 679.5457999999926 + }, + "browserTree": { + "peakRssBytes": 29614080, + "cpuTimeMs": 78, + "processCount": 1, + "sampleCount": 24, + "observations": [ + { + "sampledAt": "2026-09-02T20:37:05.627Z", + "rssBytes": 16273408, + "cpuTimeMs": 16, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:37:06.199Z", + "rssBytes": 29614080, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:37:06.754Z", + "rssBytes": 29614080, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:37:07.275Z", + "rssBytes": 29614080, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:37:07.801Z", + "rssBytes": 29614080, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:37:08.329Z", + "rssBytes": 29614080, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:37:08.854Z", + "rssBytes": 29614080, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:37:09.395Z", + "rssBytes": 29614080, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:37:09.942Z", + "rssBytes": 29614080, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:37:10.467Z", + "rssBytes": 29614080, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:37:11.001Z", + "rssBytes": 29614080, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:37:11.534Z", + "rssBytes": 29614080, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:37:12.060Z", + "rssBytes": 29614080, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:37:12.595Z", + "rssBytes": 29614080, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:37:13.128Z", + "rssBytes": 29614080, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:37:13.663Z", + "rssBytes": 29614080, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:37:14.194Z", + "rssBytes": 29614080, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:37:14.781Z", + "rssBytes": 29614080, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:37:15.343Z", + "rssBytes": 29614080, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:37:15.868Z", + "rssBytes": 29614080, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:37:16.397Z", + "rssBytes": 29614080, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:37:16.921Z", + "rssBytes": 29614080, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:37:17.442Z", + "rssBytes": 29614080, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:37:17.970Z", + "rssBytes": 29614080, + "cpuTimeMs": 78, + "processCount": 1 + } + ] + }, + "coordinator": { + "peakRssBytes": 125640704, + "cpuTimeMs": 484 + }, + "conformance": { + "connect": { + "passed": true + }, + "javascript": { + "passed": true + }, + "dom": { + "passed": true + }, + "forms": { + "passed": true, + "actionability": "dom-non-visual" + }, + "screenshot": { + "passed": true, + "supported": false, + "state": "unsupported", + "enforcement": "preflight" + }, + "teardown": { + "passed": true, + "rootProcessExited": true, + "transportDisposed": true + } + } + }, + { + "timings": { + "launchMs": 787.0129000000015, + "connectMs": 9.087599999998929, + "workloadMs": 77.9427999999898, + "shutdownMs": 697.1994999999879 + }, + "browserTree": { + "peakRssBytes": 29548544, + "cpuTimeMs": 156, + "processCount": 1, + "sampleCount": 24, + "observations": [ + { + "sampledAt": "2026-09-02T20:37:19.320Z", + "rssBytes": 16228352, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:37:19.893Z", + "rssBytes": 29548544, + "cpuTimeMs": 156, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:37:20.444Z", + "rssBytes": 29548544, + "cpuTimeMs": 156, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:37:20.964Z", + "rssBytes": 29548544, + "cpuTimeMs": 156, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:37:21.488Z", + "rssBytes": 29548544, + "cpuTimeMs": 156, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:37:22.011Z", + "rssBytes": 29548544, + "cpuTimeMs": 156, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:37:22.533Z", + "rssBytes": 29548544, + "cpuTimeMs": 156, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:37:23.057Z", + "rssBytes": 29548544, + "cpuTimeMs": 156, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:37:23.567Z", + "rssBytes": 29548544, + "cpuTimeMs": 156, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:37:24.088Z", + "rssBytes": 29548544, + "cpuTimeMs": 156, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:37:24.602Z", + "rssBytes": 29548544, + "cpuTimeMs": 156, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:37:25.122Z", + "rssBytes": 29548544, + "cpuTimeMs": 156, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:37:25.636Z", + "rssBytes": 29548544, + "cpuTimeMs": 156, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:37:26.157Z", + "rssBytes": 29548544, + "cpuTimeMs": 156, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:37:26.674Z", + "rssBytes": 29548544, + "cpuTimeMs": 156, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:37:27.202Z", + "rssBytes": 29548544, + "cpuTimeMs": 156, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:37:27.721Z", + "rssBytes": 29548544, + "cpuTimeMs": 156, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:37:28.240Z", + "rssBytes": 29548544, + "cpuTimeMs": 156, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:37:28.766Z", + "rssBytes": 29548544, + "cpuTimeMs": 156, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:37:29.284Z", + "rssBytes": 29548544, + "cpuTimeMs": 156, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:37:29.810Z", + "rssBytes": 29548544, + "cpuTimeMs": 156, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:37:30.331Z", + "rssBytes": 29548544, + "cpuTimeMs": 156, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:37:30.844Z", + "rssBytes": 29548544, + "cpuTimeMs": 156, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:37:31.364Z", + "rssBytes": 29548544, + "cpuTimeMs": 156, + "processCount": 1 + } + ] + }, + "coordinator": { + "peakRssBytes": 123944960, + "cpuTimeMs": 719 + }, + "conformance": { + "connect": { + "passed": true + }, + "javascript": { + "passed": true + }, + "dom": { + "passed": true + }, + "forms": { + "passed": true, + "actionability": "dom-non-visual" + }, + "screenshot": { + "passed": true, + "supported": false, + "state": "unsupported", + "enforcement": "preflight" + }, + "teardown": { + "passed": true, + "rootProcessExited": true, + "transportDisposed": true + } + } + }, + { + "timings": { + "launchMs": 787.8471999999892, + "connectMs": 10.07620000001043, + "workloadMs": 76.6423000000068, + "shutdownMs": 685.5359999999928 + }, + "browserTree": { + "peakRssBytes": 29417472, + "cpuTimeMs": 109, + "processCount": 1, + "sampleCount": 24, + "observations": [ + { + "sampledAt": "2026-09-02T20:37:32.739Z", + "rssBytes": 16236544, + "cpuTimeMs": 31, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:37:33.294Z", + "rssBytes": 29417472, + "cpuTimeMs": 109, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:37:33.853Z", + "rssBytes": 29417472, + "cpuTimeMs": 109, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:37:34.380Z", + "rssBytes": 29417472, + "cpuTimeMs": 109, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:37:34.904Z", + "rssBytes": 29417472, + "cpuTimeMs": 109, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:37:35.434Z", + "rssBytes": 29417472, + "cpuTimeMs": 109, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:37:35.960Z", + "rssBytes": 29417472, + "cpuTimeMs": 109, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:37:36.474Z", + "rssBytes": 29417472, + "cpuTimeMs": 109, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:37:36.996Z", + "rssBytes": 29417472, + "cpuTimeMs": 109, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:37:37.518Z", + "rssBytes": 29417472, + "cpuTimeMs": 109, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:37:38.047Z", + "rssBytes": 29417472, + "cpuTimeMs": 109, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:37:38.568Z", + "rssBytes": 29417472, + "cpuTimeMs": 109, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:37:39.090Z", + "rssBytes": 29417472, + "cpuTimeMs": 109, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:37:39.610Z", + "rssBytes": 29417472, + "cpuTimeMs": 109, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:37:40.138Z", + "rssBytes": 29417472, + "cpuTimeMs": 109, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:37:40.661Z", + "rssBytes": 29417472, + "cpuTimeMs": 109, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:37:41.179Z", + "rssBytes": 29417472, + "cpuTimeMs": 109, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:37:41.712Z", + "rssBytes": 29417472, + "cpuTimeMs": 109, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:37:42.240Z", + "rssBytes": 29417472, + "cpuTimeMs": 109, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:37:42.765Z", + "rssBytes": 29417472, + "cpuTimeMs": 109, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:37:43.324Z", + "rssBytes": 29417472, + "cpuTimeMs": 109, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:37:43.843Z", + "rssBytes": 29417472, + "cpuTimeMs": 109, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:37:44.356Z", + "rssBytes": 29417472, + "cpuTimeMs": 109, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:37:44.868Z", + "rssBytes": 29417472, + "cpuTimeMs": 109, + "processCount": 1 + } + ] + }, + "coordinator": { + "peakRssBytes": 108761088, + "cpuTimeMs": 484 + }, + "conformance": { + "connect": { + "passed": true + }, + "javascript": { + "passed": true + }, + "dom": { + "passed": true + }, + "forms": { + "passed": true, + "actionability": "dom-non-visual" + }, + "screenshot": { + "passed": true, + "supported": false, + "state": "unsupported", + "enforcement": "preflight" + }, + "teardown": { + "passed": true, + "rootProcessExited": true, + "transportDisposed": true + } + } + }, + { + "timings": { + "launchMs": 777.1791000000085, + "connectMs": 8.440999999991618, + "workloadMs": 78.12110000001849, + "shutdownMs": 671.7950000000128 + }, + "browserTree": { + "peakRssBytes": 28950528, + "cpuTimeMs": 94, + "processCount": 1, + "sampleCount": 24, + "observations": [ + { + "sampledAt": "2026-09-02T20:37:46.222Z", + "rssBytes": 16277504, + "cpuTimeMs": 31, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:37:46.795Z", + "rssBytes": 28950528, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:37:47.351Z", + "rssBytes": 28950528, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:37:47.868Z", + "rssBytes": 28950528, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:37:48.379Z", + "rssBytes": 28950528, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:37:48.905Z", + "rssBytes": 28950528, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:37:49.426Z", + "rssBytes": 28950528, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:37:49.968Z", + "rssBytes": 28950528, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:37:50.530Z", + "rssBytes": 28950528, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:37:51.065Z", + "rssBytes": 28950528, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:37:51.593Z", + "rssBytes": 28950528, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:37:52.106Z", + "rssBytes": 28950528, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:37:52.657Z", + "rssBytes": 28950528, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:37:53.183Z", + "rssBytes": 28950528, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:37:53.717Z", + "rssBytes": 28950528, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:37:54.245Z", + "rssBytes": 28950528, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:37:54.758Z", + "rssBytes": 28950528, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:37:55.272Z", + "rssBytes": 28950528, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:37:55.786Z", + "rssBytes": 28950528, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:37:56.299Z", + "rssBytes": 28950528, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:37:56.819Z", + "rssBytes": 28950528, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:37:57.335Z", + "rssBytes": 28950528, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:37:57.845Z", + "rssBytes": 28950528, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:37:58.361Z", + "rssBytes": 28950528, + "cpuTimeMs": 94, + "processCount": 1 + } + ] + }, + "coordinator": { + "peakRssBytes": 126849024, + "cpuTimeMs": 626 + }, + "conformance": { + "connect": { + "passed": true + }, + "javascript": { + "passed": true + }, + "dom": { + "passed": true + }, + "forms": { + "passed": true, + "actionability": "dom-non-visual" + }, + "screenshot": { + "passed": true, + "supported": false, + "state": "unsupported", + "enforcement": "preflight" + }, + "teardown": { + "passed": true, + "rootProcessExited": true, + "transportDisposed": true + } + } + }, + { + "timings": { + "launchMs": 779.6937999999791, + "connectMs": 9.24790000001667, + "workloadMs": 77.78229999999166, + "shutdownMs": 669.2262000000046 + }, + "browserTree": { + "peakRssBytes": 29446144, + "cpuTimeMs": 94, + "processCount": 1, + "sampleCount": 24, + "observations": [ + { + "sampledAt": "2026-09-02T20:37:59.685Z", + "rssBytes": 16171008, + "cpuTimeMs": 16, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:38:00.250Z", + "rssBytes": 29446144, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:38:00.830Z", + "rssBytes": 29446144, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:38:01.349Z", + "rssBytes": 29446144, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:38:01.873Z", + "rssBytes": 29446144, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:38:02.383Z", + "rssBytes": 29446144, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:38:02.902Z", + "rssBytes": 29446144, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:38:03.410Z", + "rssBytes": 29446144, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:38:03.924Z", + "rssBytes": 29446144, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:38:04.427Z", + "rssBytes": 29446144, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:38:04.944Z", + "rssBytes": 29446144, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:38:05.457Z", + "rssBytes": 29446144, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:38:05.978Z", + "rssBytes": 29446144, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:38:06.493Z", + "rssBytes": 29446144, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:38:07.010Z", + "rssBytes": 29446144, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:38:07.524Z", + "rssBytes": 29446144, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:38:08.037Z", + "rssBytes": 29446144, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:38:08.564Z", + "rssBytes": 29446144, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:38:09.084Z", + "rssBytes": 29446144, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:38:09.603Z", + "rssBytes": 29446144, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:38:10.134Z", + "rssBytes": 29446144, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:38:10.643Z", + "rssBytes": 29446144, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:38:11.157Z", + "rssBytes": 29446144, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:38:11.676Z", + "rssBytes": 29446144, + "cpuTimeMs": 94, + "processCount": 1 + } + ] + }, + "coordinator": { + "peakRssBytes": 126849024, + "cpuTimeMs": 609 + }, + "conformance": { + "connect": { + "passed": true + }, + "javascript": { + "passed": true + }, + "dom": { + "passed": true + }, + "forms": { + "passed": true, + "actionability": "dom-non-visual" + }, + "screenshot": { + "passed": true, + "supported": false, + "state": "unsupported", + "enforcement": "preflight" + }, + "teardown": { + "passed": true, + "rootProcessExited": true, + "transportDisposed": true + } + } + }, + { + "timings": { + "launchMs": 788.579299999983, + "connectMs": 7.575200000021141, + "workloadMs": 72.67779999997583, + "shutdownMs": 674.4684000000125 + }, + "browserTree": { + "peakRssBytes": 28917760, + "cpuTimeMs": 109, + "processCount": 1, + "sampleCount": 24, + "observations": [ + { + "sampledAt": "2026-09-02T20:38:13.001Z", + "rssBytes": 16318464, + "cpuTimeMs": 31, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:38:13.560Z", + "rssBytes": 28917760, + "cpuTimeMs": 109, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:38:14.122Z", + "rssBytes": 28917760, + "cpuTimeMs": 109, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:38:14.645Z", + "rssBytes": 28917760, + "cpuTimeMs": 109, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:38:15.190Z", + "rssBytes": 28917760, + "cpuTimeMs": 109, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:38:15.725Z", + "rssBytes": 28917760, + "cpuTimeMs": 109, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:38:16.256Z", + "rssBytes": 28917760, + "cpuTimeMs": 109, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:38:16.767Z", + "rssBytes": 28917760, + "cpuTimeMs": 109, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:38:17.303Z", + "rssBytes": 28917760, + "cpuTimeMs": 109, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:38:17.829Z", + "rssBytes": 28917760, + "cpuTimeMs": 109, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:38:18.371Z", + "rssBytes": 28917760, + "cpuTimeMs": 109, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:38:18.900Z", + "rssBytes": 28917760, + "cpuTimeMs": 109, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:38:19.418Z", + "rssBytes": 28917760, + "cpuTimeMs": 109, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:38:19.951Z", + "rssBytes": 28917760, + "cpuTimeMs": 109, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:38:20.478Z", + "rssBytes": 28917760, + "cpuTimeMs": 109, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:38:21.005Z", + "rssBytes": 28917760, + "cpuTimeMs": 109, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:38:21.521Z", + "rssBytes": 28917760, + "cpuTimeMs": 109, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:38:22.046Z", + "rssBytes": 28917760, + "cpuTimeMs": 109, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:38:22.600Z", + "rssBytes": 28917760, + "cpuTimeMs": 109, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:38:23.152Z", + "rssBytes": 28917760, + "cpuTimeMs": 109, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:38:23.670Z", + "rssBytes": 28917760, + "cpuTimeMs": 109, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:38:24.183Z", + "rssBytes": 28917760, + "cpuTimeMs": 109, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:38:24.709Z", + "rssBytes": 28917760, + "cpuTimeMs": 109, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:38:25.242Z", + "rssBytes": 28917760, + "cpuTimeMs": 109, + "processCount": 1 + } + ] + }, + "coordinator": { + "peakRssBytes": 127926272, + "cpuTimeMs": 437 + }, + "conformance": { + "connect": { + "passed": true + }, + "javascript": { + "passed": true + }, + "dom": { + "passed": true + }, + "forms": { + "passed": true, + "actionability": "dom-non-visual" + }, + "screenshot": { + "passed": true, + "supported": false, + "state": "unsupported", + "enforcement": "preflight" + }, + "teardown": { + "passed": true, + "rootProcessExited": true, + "transportDisposed": true + } + } + }, + { + "timings": { + "launchMs": 755.5834000000032, + "connectMs": 6.7515000000130385, + "workloadMs": 79.20370000001276, + "shutdownMs": 679.0819999999949 + }, + "browserTree": { + "peakRssBytes": 29302784, + "cpuTimeMs": 78, + "processCount": 1, + "sampleCount": 24, + "observations": [ + { + "sampledAt": "2026-09-02T20:38:26.590Z", + "rssBytes": 16195584, + "cpuTimeMs": 16, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:38:27.159Z", + "rssBytes": 29302784, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:38:27.703Z", + "rssBytes": 29302784, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:38:28.221Z", + "rssBytes": 29302784, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:38:28.735Z", + "rssBytes": 29302784, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:38:29.298Z", + "rssBytes": 29302784, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:38:29.820Z", + "rssBytes": 29302784, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:38:30.338Z", + "rssBytes": 29302784, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:38:30.854Z", + "rssBytes": 29302784, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:38:31.409Z", + "rssBytes": 29302784, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:38:31.945Z", + "rssBytes": 29302784, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:38:32.463Z", + "rssBytes": 29302784, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:38:33.015Z", + "rssBytes": 29302784, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:38:33.541Z", + "rssBytes": 29302784, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:38:34.063Z", + "rssBytes": 29302784, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:38:34.581Z", + "rssBytes": 29302784, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:38:35.100Z", + "rssBytes": 29302784, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:38:35.616Z", + "rssBytes": 29302784, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:38:36.132Z", + "rssBytes": 29302784, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:38:36.651Z", + "rssBytes": 29302784, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:38:37.177Z", + "rssBytes": 29302784, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:38:37.694Z", + "rssBytes": 29302784, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:38:38.211Z", + "rssBytes": 29302784, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:38:38.729Z", + "rssBytes": 29302784, + "cpuTimeMs": 78, + "processCount": 1 + } + ] + }, + "coordinator": { + "peakRssBytes": 127279104, + "cpuTimeMs": 391 + }, + "conformance": { + "connect": { + "passed": true + }, + "javascript": { + "passed": true + }, + "dom": { + "passed": true + }, + "forms": { + "passed": true, + "actionability": "dom-non-visual" + }, + "screenshot": { + "passed": true, + "supported": false, + "state": "unsupported", + "enforcement": "preflight" + }, + "teardown": { + "passed": true, + "rootProcessExited": true, + "transportDisposed": true + } + } + }, + { + "timings": { + "launchMs": 777.7975999999908, + "connectMs": 8.076600000000326, + "workloadMs": 93.07159999999567, + "shutdownMs": 675.7256000000052 + }, + "browserTree": { + "peakRssBytes": 29167616, + "cpuTimeMs": 78, + "processCount": 1, + "sampleCount": 24, + "observations": [ + { + "sampledAt": "2026-09-02T20:38:40.085Z", + "rssBytes": 16089088, + "cpuTimeMs": 16, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:38:40.651Z", + "rssBytes": 29167616, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:38:41.205Z", + "rssBytes": 29167616, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:38:41.721Z", + "rssBytes": 29167616, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:38:42.245Z", + "rssBytes": 29167616, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:38:42.762Z", + "rssBytes": 29167616, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:38:43.302Z", + "rssBytes": 29167616, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:38:43.815Z", + "rssBytes": 29167616, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:38:44.335Z", + "rssBytes": 29167616, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:38:44.857Z", + "rssBytes": 29167616, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:38:45.370Z", + "rssBytes": 29167616, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:38:45.898Z", + "rssBytes": 29167616, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:38:46.418Z", + "rssBytes": 29167616, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:38:46.934Z", + "rssBytes": 29167616, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:38:47.448Z", + "rssBytes": 29167616, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:38:47.973Z", + "rssBytes": 29167616, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:38:48.490Z", + "rssBytes": 29167616, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:38:49.008Z", + "rssBytes": 29167616, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:38:49.527Z", + "rssBytes": 29167616, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:38:50.051Z", + "rssBytes": 29167616, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:38:50.564Z", + "rssBytes": 29167616, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:38:51.085Z", + "rssBytes": 29167616, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:38:51.601Z", + "rssBytes": 29167616, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:38:52.119Z", + "rssBytes": 29167616, + "cpuTimeMs": 78, + "processCount": 1 + } + ] + }, + "coordinator": { + "peakRssBytes": 128679936, + "cpuTimeMs": 515 + }, + "conformance": { + "connect": { + "passed": true + }, + "javascript": { + "passed": true + }, + "dom": { + "passed": true + }, + "forms": { + "passed": true, + "actionability": "dom-non-visual" + }, + "screenshot": { + "passed": true, + "supported": false, + "state": "unsupported", + "enforcement": "preflight" + }, + "teardown": { + "passed": true, + "rootProcessExited": true, + "transportDisposed": true + } + } + }, + { + "timings": { + "launchMs": 752.3525000000373, + "connectMs": 8.821799999976065, + "workloadMs": 72.45730000000913, + "shutdownMs": 682.5961999999708 + }, + "browserTree": { + "peakRssBytes": 28831744, + "cpuTimeMs": 94, + "processCount": 1, + "sampleCount": 24, + "observations": [ + { + "sampledAt": "2026-09-02T20:38:53.437Z", + "rssBytes": 16211968, + "cpuTimeMs": 31, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:38:53.997Z", + "rssBytes": 28831744, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:38:54.547Z", + "rssBytes": 28831744, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:38:55.068Z", + "rssBytes": 28831744, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:38:55.585Z", + "rssBytes": 28831744, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:38:56.129Z", + "rssBytes": 28831744, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:38:56.680Z", + "rssBytes": 28831744, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:38:57.211Z", + "rssBytes": 28831744, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:38:57.762Z", + "rssBytes": 28831744, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:38:58.300Z", + "rssBytes": 28831744, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:38:58.841Z", + "rssBytes": 28831744, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:38:59.361Z", + "rssBytes": 28831744, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:38:59.890Z", + "rssBytes": 28831744, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:39:00.433Z", + "rssBytes": 28831744, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:39:00.999Z", + "rssBytes": 28831744, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:39:01.527Z", + "rssBytes": 28831744, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:39:02.042Z", + "rssBytes": 28831744, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:39:02.567Z", + "rssBytes": 28831744, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:39:03.086Z", + "rssBytes": 28831744, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:39:03.600Z", + "rssBytes": 28831744, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:39:04.124Z", + "rssBytes": 28831744, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:39:04.644Z", + "rssBytes": 28831744, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:39:05.167Z", + "rssBytes": 28831744, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:39:05.694Z", + "rssBytes": 28831744, + "cpuTimeMs": 94, + "processCount": 1 + } + ] + }, + "coordinator": { + "peakRssBytes": 128679936, + "cpuTimeMs": 438 + }, + "conformance": { + "connect": { + "passed": true + }, + "javascript": { + "passed": true + }, + "dom": { + "passed": true + }, + "forms": { + "passed": true, + "actionability": "dom-non-visual" + }, + "screenshot": { + "passed": true, + "supported": false, + "state": "unsupported", + "enforcement": "preflight" + }, + "teardown": { + "passed": true, + "rootProcessExited": true, + "transportDisposed": true + } + } + } + ] +} diff --git a/docs/benchmarks/artifacts/obscura-0.2.1-constrained-non-visual-25mib-2026-09-03.json b/docs/benchmarks/artifacts/obscura-0.2.1-constrained-non-visual-25mib-2026-09-03.json new file mode 100644 index 0000000..6098903 --- /dev/null +++ b/docs/benchmarks/artifacts/obscura-0.2.1-constrained-non-visual-25mib-2026-09-03.json @@ -0,0 +1,3921 @@ +{ + "schemaVersion": "cockroach.lightweight-browser-benchmark.v2", + "generatedAt": "2026-09-02T19:58:11.913Z", + "implementation": "obscura", + "resourceProfile": "constrained", + "renderingPolicy": "visual-actions-denied", + "executable": { + "name": "obscura.exe", + "bytes": 58097152, + "sha256": "5b609fb46bc00da79e450fb0fbd34bd442e565b1394f4af95433e0b341078221", + "version": "obscura 0.2.1" + }, + "sourceIdentity": { + "gitCommit": "deb6c25b4c0bc1927a631e3b062464b4f4cc2775", + "workingTreeDirty": true, + "sourceScope": "Runtime, schemas, benchmark/package/build configuration, and tests; generated documentation and benchmark artifacts are excluded.", + "identityNormalization": "Relative paths use forward slashes and text line endings are normalized to LF before hashing.", + "sourceFileCount": 100, + "sourceTreeSha256": "7cd941b6dc2f12139627ff4e2b5ea15acd9f22368d203c87dd5170765829abf7", + "runtimeBuildFileCount": 200, + "runtimeBuildSha256": "730a61e4c1fc8c20fcb6906d8d84af2b0e2f544b35a8da9a2a1eee6a37ed22fd", + "benchmarkScriptSha256": "08a5294f2d446765f712b93c9bfaaca010b1d043ded638d1f4f57b5038c97e86" + }, + "machine": { + "platform": "win32", + "architecture": "x64", + "release": "10.0.26200", + "logicalCpuCount": 16, + "cpuModel": "AMD Ryzen 7 4800H with Radeon Graphics ", + "totalMemoryBytes": 16557887488, + "freeMemoryBytesAtReport": 6021763072, + "node": "v24.15.0" + }, + "methodology": { + "warmup": 1, + "iterations": 20, + "settleMs": 500, + "startupTimeoutMs": 15000, + "actionTimeoutMs": 10000, + "teardownTimeoutMs": 5000, + "resourceSamples": 10, + "resourceSampleIntervalMs": 25, + "targetMiB": 25, + "targetDefinition": "Every measured owned-browser process-tree RSS sample is at or below the target and every required CDP/JavaScript/DOM/form/screenshot-preflight/teardown check passes.", + "memoryDefinition": "Aggregate RSS for the complete owned browser process tree; the Node coordinator is disclosed separately and is not hidden in browser RSS.", + "renderingControl": "Cockroach Browser declares a no-visual-action policy and rejects capture at preflight. This does not assert that the selected engine binary exposes or received a renderer-disable launch switch.", + "workload": "One runtime-owned loopback CDP server, one non-visual data-URL document, JavaScript evaluation, DOM query, input, and click.", + "requiredChecks": [ + "connect", + "javascript", + "dom", + "forms", + "screenshot", + "teardown" + ], + "optionalChecks": [], + "energy": { + "available": false, + "reason": "No calibrated platform energy counter is available; CPU time is not watts." + } + }, + "verdict": { + "passed": false, + "everyRunWithinTarget": false, + "requiredConformancePassed": true, + "targetBytes": 26214400, + "maxObservedBrowserTreeRssBytes": 29745152 + }, + "summary": { + "peakBrowserTreeRssBytes": { + "median": 29446144, + "p95": 29605888, + "min": 28831744, + "max": 29745152 + }, + "browserCpuTimeMs": { + "median": 94, + "p95": 156, + "min": 47, + "max": 172 + }, + "processCount": { + "median": 1, + "p95": 1, + "min": 1, + "max": 1 + }, + "launchMs": { + "median": 797.4639000000025, + "p95": 836.4932999999874, + "min": 759.6841999999888, + "max": 836.5996999999916 + }, + "connectMs": { + "median": 9.488600000011502, + "p95": 11.16610000000037, + "min": 6.967899999988731, + "max": 11.325400000001537 + }, + "workloadMs": { + "median": 78.94189999997616, + "p95": 102.49629999999888, + "min": 74.2153999999864, + "max": 110.34090000001015 + }, + "shutdownMs": { + "median": 692.5248000000138, + "p95": 709.5445000000036, + "min": 680.5192000000097, + "max": 710.9101999999839 + }, + "coordinatorPeakRssBytes": { + "median": 111546368, + "p95": 122474496, + "min": 105431040, + "max": 126640128 + } + }, + "samples": [ + { + "timings": { + "launchMs": 795.1435000000001, + "connectMs": 11.16610000000037, + "workloadMs": 98.76620000000003, + "shutdownMs": 699.4146000000001 + }, + "browserTree": { + "peakRssBytes": 29380608, + "cpuTimeMs": 94, + "processCount": 1, + "sampleCount": 24, + "observations": [ + { + "sampledAt": "2026-09-02T19:53:40.156Z", + "rssBytes": 16257024, + "cpuTimeMs": 31, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:53:40.735Z", + "rssBytes": 29380608, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:53:41.295Z", + "rssBytes": 29380608, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:53:41.825Z", + "rssBytes": 29380608, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:53:42.367Z", + "rssBytes": 29380608, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:53:42.975Z", + "rssBytes": 29380608, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:53:43.519Z", + "rssBytes": 29380608, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:53:44.060Z", + "rssBytes": 29380608, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:53:44.585Z", + "rssBytes": 29380608, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:53:45.125Z", + "rssBytes": 29380608, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:53:45.660Z", + "rssBytes": 29380608, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:53:46.174Z", + "rssBytes": 29380608, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:53:46.710Z", + "rssBytes": 29380608, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:53:47.235Z", + "rssBytes": 29380608, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:53:47.757Z", + "rssBytes": 29380608, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:53:48.282Z", + "rssBytes": 29380608, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:53:48.801Z", + "rssBytes": 29380608, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:53:49.324Z", + "rssBytes": 29380608, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:53:49.842Z", + "rssBytes": 29380608, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:53:50.358Z", + "rssBytes": 29380608, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:53:50.871Z", + "rssBytes": 29380608, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:53:51.391Z", + "rssBytes": 29380608, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:53:51.911Z", + "rssBytes": 29380608, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:53:52.430Z", + "rssBytes": 29380608, + "cpuTimeMs": 94, + "processCount": 1 + } + ] + }, + "coordinator": { + "peakRssBytes": 113930240, + "cpuTimeMs": 641 + }, + "conformance": { + "connect": { + "passed": true + }, + "javascript": { + "passed": true + }, + "dom": { + "passed": true + }, + "forms": { + "passed": true, + "actionability": "dom-non-visual" + }, + "screenshot": { + "passed": true, + "supported": false, + "state": "unsupported", + "enforcement": "preflight" + }, + "teardown": { + "passed": true, + "rootProcessExited": true, + "transportDisposed": true + } + } + }, + { + "timings": { + "launchMs": 812.7645999999986, + "connectMs": 9.564900000001217, + "workloadMs": 83.44830000000002, + "shutdownMs": 693.2782999999981 + }, + "browserTree": { + "peakRssBytes": 29515776, + "cpuTimeMs": 109, + "processCount": 1, + "sampleCount": 24, + "observations": [ + { + "sampledAt": "2026-09-02T19:53:53.850Z", + "rssBytes": 16297984, + "cpuTimeMs": 31, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:53:54.415Z", + "rssBytes": 29515776, + "cpuTimeMs": 109, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:53:54.965Z", + "rssBytes": 29515776, + "cpuTimeMs": 109, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:53:55.500Z", + "rssBytes": 29515776, + "cpuTimeMs": 109, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:53:56.019Z", + "rssBytes": 29515776, + "cpuTimeMs": 109, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:53:56.548Z", + "rssBytes": 29515776, + "cpuTimeMs": 109, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:53:57.133Z", + "rssBytes": 29515776, + "cpuTimeMs": 109, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:53:57.655Z", + "rssBytes": 29515776, + "cpuTimeMs": 109, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:53:58.174Z", + "rssBytes": 29515776, + "cpuTimeMs": 109, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:53:58.696Z", + "rssBytes": 29515776, + "cpuTimeMs": 109, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:53:59.220Z", + "rssBytes": 29515776, + "cpuTimeMs": 109, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:53:59.742Z", + "rssBytes": 29515776, + "cpuTimeMs": 109, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:54:00.280Z", + "rssBytes": 29515776, + "cpuTimeMs": 109, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:54:00.825Z", + "rssBytes": 29515776, + "cpuTimeMs": 109, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:54:01.343Z", + "rssBytes": 29515776, + "cpuTimeMs": 109, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:54:01.858Z", + "rssBytes": 29515776, + "cpuTimeMs": 109, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:54:02.382Z", + "rssBytes": 29515776, + "cpuTimeMs": 109, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:54:02.906Z", + "rssBytes": 29515776, + "cpuTimeMs": 109, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:54:03.429Z", + "rssBytes": 29515776, + "cpuTimeMs": 109, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:54:03.954Z", + "rssBytes": 29515776, + "cpuTimeMs": 109, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:54:04.485Z", + "rssBytes": 29515776, + "cpuTimeMs": 109, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:54:04.999Z", + "rssBytes": 29515776, + "cpuTimeMs": 109, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:54:05.520Z", + "rssBytes": 29515776, + "cpuTimeMs": 109, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:54:06.071Z", + "rssBytes": 29515776, + "cpuTimeMs": 109, + "processCount": 1 + } + ] + }, + "coordinator": { + "peakRssBytes": 111546368, + "cpuTimeMs": 641 + }, + "conformance": { + "connect": { + "passed": true + }, + "javascript": { + "passed": true + }, + "dom": { + "passed": true + }, + "forms": { + "passed": true, + "actionability": "dom-non-visual" + }, + "screenshot": { + "passed": true, + "supported": false, + "state": "unsupported", + "enforcement": "preflight" + }, + "teardown": { + "passed": true, + "rootProcessExited": true, + "transportDisposed": true + } + } + }, + { + "timings": { + "launchMs": 797.4639000000025, + "connectMs": 11.325400000001537, + "workloadMs": 85.12059999999474, + "shutdownMs": 687.9965000000011 + }, + "browserTree": { + "peakRssBytes": 29454336, + "cpuTimeMs": 78, + "processCount": 1, + "sampleCount": 24, + "observations": [ + { + "sampledAt": "2026-09-02T19:54:07.450Z", + "rssBytes": 16240640, + "cpuTimeMs": 16, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:54:08.051Z", + "rssBytes": 29454336, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:54:08.605Z", + "rssBytes": 29454336, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:54:09.139Z", + "rssBytes": 29454336, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:54:09.681Z", + "rssBytes": 29454336, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:54:10.207Z", + "rssBytes": 29454336, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:54:10.737Z", + "rssBytes": 29454336, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:54:11.251Z", + "rssBytes": 29454336, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:54:11.798Z", + "rssBytes": 29454336, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:54:12.320Z", + "rssBytes": 29454336, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:54:12.852Z", + "rssBytes": 29454336, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:54:13.376Z", + "rssBytes": 29454336, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:54:13.922Z", + "rssBytes": 29454336, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:54:14.476Z", + "rssBytes": 29454336, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:54:15.042Z", + "rssBytes": 29454336, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:54:15.605Z", + "rssBytes": 29454336, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:54:16.168Z", + "rssBytes": 29454336, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:54:16.725Z", + "rssBytes": 29454336, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:54:17.295Z", + "rssBytes": 29454336, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:54:17.865Z", + "rssBytes": 29454336, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:54:18.423Z", + "rssBytes": 29454336, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:54:18.955Z", + "rssBytes": 29454336, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:54:19.482Z", + "rssBytes": 29454336, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:54:20.017Z", + "rssBytes": 29454336, + "cpuTimeMs": 78, + "processCount": 1 + } + ] + }, + "coordinator": { + "peakRssBytes": 113385472, + "cpuTimeMs": 593 + }, + "conformance": { + "connect": { + "passed": true + }, + "javascript": { + "passed": true + }, + "dom": { + "passed": true + }, + "forms": { + "passed": true, + "actionability": "dom-non-visual" + }, + "screenshot": { + "passed": true, + "supported": false, + "state": "unsupported", + "enforcement": "preflight" + }, + "teardown": { + "passed": true, + "rootProcessExited": true, + "transportDisposed": true + } + } + }, + { + "timings": { + "launchMs": 808.584600000002, + "connectMs": 9.447299999999814, + "workloadMs": 84.02409999999509, + "shutdownMs": 692.6659000000072 + }, + "browserTree": { + "peakRssBytes": 29257728, + "cpuTimeMs": 172, + "processCount": 1, + "sampleCount": 24, + "observations": [ + { + "sampledAt": "2026-09-02T19:54:21.416Z", + "rssBytes": 16166912, + "cpuTimeMs": 63, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:54:21.974Z", + "rssBytes": 29257728, + "cpuTimeMs": 172, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:54:22.524Z", + "rssBytes": 29257728, + "cpuTimeMs": 172, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:54:23.048Z", + "rssBytes": 29257728, + "cpuTimeMs": 172, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:54:23.566Z", + "rssBytes": 29257728, + "cpuTimeMs": 172, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:54:24.093Z", + "rssBytes": 29257728, + "cpuTimeMs": 172, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:54:24.611Z", + "rssBytes": 29257728, + "cpuTimeMs": 172, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:54:25.138Z", + "rssBytes": 29257728, + "cpuTimeMs": 172, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:54:25.656Z", + "rssBytes": 29257728, + "cpuTimeMs": 172, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:54:26.180Z", + "rssBytes": 29257728, + "cpuTimeMs": 172, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:54:26.719Z", + "rssBytes": 29257728, + "cpuTimeMs": 172, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:54:27.241Z", + "rssBytes": 29257728, + "cpuTimeMs": 172, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:54:27.777Z", + "rssBytes": 29257728, + "cpuTimeMs": 172, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:54:28.310Z", + "rssBytes": 29257728, + "cpuTimeMs": 172, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:54:28.868Z", + "rssBytes": 29257728, + "cpuTimeMs": 172, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:54:29.436Z", + "rssBytes": 29257728, + "cpuTimeMs": 172, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:54:29.993Z", + "rssBytes": 29257728, + "cpuTimeMs": 172, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:54:30.522Z", + "rssBytes": 29257728, + "cpuTimeMs": 172, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:54:31.068Z", + "rssBytes": 28905472, + "cpuTimeMs": 172, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:54:31.586Z", + "rssBytes": 28905472, + "cpuTimeMs": 172, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:54:32.117Z", + "rssBytes": 28905472, + "cpuTimeMs": 172, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:54:32.637Z", + "rssBytes": 28905472, + "cpuTimeMs": 172, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:54:33.165Z", + "rssBytes": 28905472, + "cpuTimeMs": 172, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:54:33.688Z", + "rssBytes": 28905472, + "cpuTimeMs": 172, + "processCount": 1 + } + ] + }, + "coordinator": { + "peakRssBytes": 108195840, + "cpuTimeMs": 563 + }, + "conformance": { + "connect": { + "passed": true + }, + "javascript": { + "passed": true + }, + "dom": { + "passed": true + }, + "forms": { + "passed": true, + "actionability": "dom-non-visual" + }, + "screenshot": { + "passed": true, + "supported": false, + "state": "unsupported", + "enforcement": "preflight" + }, + "teardown": { + "passed": true, + "rootProcessExited": true, + "transportDisposed": true + } + } + }, + { + "timings": { + "launchMs": 806.2921999999962, + "connectMs": 11.110899999999674, + "workloadMs": 78.05449999999837, + "shutdownMs": 703.5687999999936 + }, + "browserTree": { + "peakRssBytes": 29491200, + "cpuTimeMs": 63, + "processCount": 1, + "sampleCount": 24, + "observations": [ + { + "sampledAt": "2026-09-02T19:54:35.075Z", + "rssBytes": 16138240, + "cpuTimeMs": 0, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:54:35.641Z", + "rssBytes": 29491200, + "cpuTimeMs": 63, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:54:36.192Z", + "rssBytes": 29491200, + "cpuTimeMs": 63, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:54:36.711Z", + "rssBytes": 29491200, + "cpuTimeMs": 63, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:54:37.235Z", + "rssBytes": 29491200, + "cpuTimeMs": 63, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:54:37.762Z", + "rssBytes": 29491200, + "cpuTimeMs": 63, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:54:38.292Z", + "rssBytes": 29491200, + "cpuTimeMs": 63, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:54:38.820Z", + "rssBytes": 29491200, + "cpuTimeMs": 63, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:54:39.347Z", + "rssBytes": 29491200, + "cpuTimeMs": 63, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:54:39.873Z", + "rssBytes": 29491200, + "cpuTimeMs": 63, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:54:40.392Z", + "rssBytes": 29491200, + "cpuTimeMs": 63, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:54:40.942Z", + "rssBytes": 29491200, + "cpuTimeMs": 63, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:54:41.465Z", + "rssBytes": 29491200, + "cpuTimeMs": 63, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:54:41.992Z", + "rssBytes": 29491200, + "cpuTimeMs": 63, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:54:42.517Z", + "rssBytes": 29491200, + "cpuTimeMs": 63, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:54:43.070Z", + "rssBytes": 29491200, + "cpuTimeMs": 63, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:54:43.589Z", + "rssBytes": 29491200, + "cpuTimeMs": 63, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:54:44.119Z", + "rssBytes": 29491200, + "cpuTimeMs": 63, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:54:44.645Z", + "rssBytes": 29491200, + "cpuTimeMs": 63, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:54:45.165Z", + "rssBytes": 29491200, + "cpuTimeMs": 63, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:54:45.683Z", + "rssBytes": 29491200, + "cpuTimeMs": 63, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:54:46.206Z", + "rssBytes": 29491200, + "cpuTimeMs": 63, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:54:46.736Z", + "rssBytes": 29491200, + "cpuTimeMs": 63, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:54:47.258Z", + "rssBytes": 29491200, + "cpuTimeMs": 63, + "processCount": 1 + } + ] + }, + "coordinator": { + "peakRssBytes": 106442752, + "cpuTimeMs": 484 + }, + "conformance": { + "connect": { + "passed": true + }, + "javascript": { + "passed": true + }, + "dom": { + "passed": true + }, + "forms": { + "passed": true, + "actionability": "dom-non-visual" + }, + "screenshot": { + "passed": true, + "supported": false, + "state": "unsupported", + "enforcement": "preflight" + }, + "teardown": { + "passed": true, + "rootProcessExited": true, + "transportDisposed": true + } + } + }, + { + "timings": { + "launchMs": 836.4932999999874, + "connectMs": 8.794000000008964, + "workloadMs": 82.54959999999846, + "shutdownMs": 684.8218000000052 + }, + "browserTree": { + "peakRssBytes": 29421568, + "cpuTimeMs": 94, + "processCount": 1, + "sampleCount": 24, + "observations": [ + { + "sampledAt": "2026-09-02T19:54:48.683Z", + "rssBytes": 16334848, + "cpuTimeMs": 31, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:54:49.277Z", + "rssBytes": 29421568, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:54:49.830Z", + "rssBytes": 29421568, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:54:50.363Z", + "rssBytes": 29421568, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:54:50.902Z", + "rssBytes": 29421568, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:54:51.439Z", + "rssBytes": 29421568, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:54:51.979Z", + "rssBytes": 29421568, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:54:52.514Z", + "rssBytes": 29421568, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:54:53.040Z", + "rssBytes": 29421568, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:54:53.561Z", + "rssBytes": 29421568, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:54:54.081Z", + "rssBytes": 29421568, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:54:54.607Z", + "rssBytes": 29421568, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:54:55.127Z", + "rssBytes": 29421568, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:54:55.657Z", + "rssBytes": 29421568, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:54:56.183Z", + "rssBytes": 29421568, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:54:56.713Z", + "rssBytes": 29421568, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:54:57.234Z", + "rssBytes": 29421568, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:54:57.747Z", + "rssBytes": 29421568, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:54:58.274Z", + "rssBytes": 29421568, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:54:58.793Z", + "rssBytes": 29421568, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:54:59.321Z", + "rssBytes": 29421568, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:54:59.835Z", + "rssBytes": 29421568, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:55:00.392Z", + "rssBytes": 29421568, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:55:00.925Z", + "rssBytes": 29421568, + "cpuTimeMs": 94, + "processCount": 1 + } + ] + }, + "coordinator": { + "peakRssBytes": 106733568, + "cpuTimeMs": 546 + }, + "conformance": { + "connect": { + "passed": true + }, + "javascript": { + "passed": true + }, + "dom": { + "passed": true + }, + "forms": { + "passed": true, + "actionability": "dom-non-visual" + }, + "screenshot": { + "passed": true, + "supported": false, + "state": "unsupported", + "enforcement": "preflight" + }, + "teardown": { + "passed": true, + "rootProcessExited": true, + "transportDisposed": true + } + } + }, + { + "timings": { + "launchMs": 794.9214999999967, + "connectMs": 9.353000000002794, + "workloadMs": 78.63260000001173, + "shutdownMs": 709.5445000000036 + }, + "browserTree": { + "peakRssBytes": 29024256, + "cpuTimeMs": 78, + "processCount": 1, + "sampleCount": 24, + "observations": [ + { + "sampledAt": "2026-09-02T19:55:02.305Z", + "rssBytes": 16273408, + "cpuTimeMs": 16, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:55:02.886Z", + "rssBytes": 29024256, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:55:03.445Z", + "rssBytes": 29024256, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:55:03.969Z", + "rssBytes": 29024256, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:55:04.489Z", + "rssBytes": 29024256, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:55:05.022Z", + "rssBytes": 29024256, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:55:05.547Z", + "rssBytes": 29024256, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:55:06.070Z", + "rssBytes": 29024256, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:55:06.591Z", + "rssBytes": 29024256, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:55:07.119Z", + "rssBytes": 29024256, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:55:07.640Z", + "rssBytes": 29024256, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:55:08.161Z", + "rssBytes": 29024256, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:55:08.681Z", + "rssBytes": 29024256, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:55:09.208Z", + "rssBytes": 29024256, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:55:09.729Z", + "rssBytes": 29024256, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:55:10.246Z", + "rssBytes": 29024256, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:55:10.761Z", + "rssBytes": 29024256, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:55:11.277Z", + "rssBytes": 29024256, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:55:11.804Z", + "rssBytes": 29024256, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:55:12.326Z", + "rssBytes": 29024256, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:55:12.840Z", + "rssBytes": 29024256, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:55:13.358Z", + "rssBytes": 29024256, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:55:13.880Z", + "rssBytes": 29024256, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:55:14.422Z", + "rssBytes": 29024256, + "cpuTimeMs": 78, + "processCount": 1 + } + ] + }, + "coordinator": { + "peakRssBytes": 105431040, + "cpuTimeMs": 610 + }, + "conformance": { + "connect": { + "passed": true + }, + "javascript": { + "passed": true + }, + "dom": { + "passed": true + }, + "forms": { + "passed": true, + "actionability": "dom-non-visual" + }, + "screenshot": { + "passed": true, + "supported": false, + "state": "unsupported", + "enforcement": "preflight" + }, + "teardown": { + "passed": true, + "rootProcessExited": true, + "transportDisposed": true + } + } + }, + { + "timings": { + "launchMs": 804.8341999999975, + "connectMs": 8.650899999993271, + "workloadMs": 81.47999999999593, + "shutdownMs": 687.3295000000071 + }, + "browserTree": { + "peakRssBytes": 29339648, + "cpuTimeMs": 109, + "processCount": 1, + "sampleCount": 24, + "observations": [ + { + "sampledAt": "2026-09-02T19:55:15.840Z", + "rssBytes": 16171008, + "cpuTimeMs": 47, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:55:16.408Z", + "rssBytes": 29339648, + "cpuTimeMs": 109, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:55:16.974Z", + "rssBytes": 29339648, + "cpuTimeMs": 109, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:55:17.504Z", + "rssBytes": 29339648, + "cpuTimeMs": 109, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:55:18.025Z", + "rssBytes": 29339648, + "cpuTimeMs": 109, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:55:18.546Z", + "rssBytes": 29339648, + "cpuTimeMs": 109, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:55:19.080Z", + "rssBytes": 29339648, + "cpuTimeMs": 109, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:55:19.593Z", + "rssBytes": 29339648, + "cpuTimeMs": 109, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:55:20.121Z", + "rssBytes": 29339648, + "cpuTimeMs": 109, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:55:20.644Z", + "rssBytes": 29339648, + "cpuTimeMs": 109, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:55:21.163Z", + "rssBytes": 29339648, + "cpuTimeMs": 109, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:55:21.682Z", + "rssBytes": 29339648, + "cpuTimeMs": 109, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:55:22.200Z", + "rssBytes": 29339648, + "cpuTimeMs": 109, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:55:22.731Z", + "rssBytes": 29339648, + "cpuTimeMs": 109, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:55:23.248Z", + "rssBytes": 29339648, + "cpuTimeMs": 109, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:55:23.774Z", + "rssBytes": 29339648, + "cpuTimeMs": 109, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:55:24.293Z", + "rssBytes": 29339648, + "cpuTimeMs": 109, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:55:24.812Z", + "rssBytes": 29339648, + "cpuTimeMs": 109, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:55:25.332Z", + "rssBytes": 29339648, + "cpuTimeMs": 109, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:55:25.857Z", + "rssBytes": 29339648, + "cpuTimeMs": 109, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:55:26.384Z", + "rssBytes": 29339648, + "cpuTimeMs": 109, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:55:26.902Z", + "rssBytes": 29339648, + "cpuTimeMs": 109, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:55:27.423Z", + "rssBytes": 29339648, + "cpuTimeMs": 109, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:55:27.945Z", + "rssBytes": 29339648, + "cpuTimeMs": 109, + "processCount": 1 + } + ] + }, + "coordinator": { + "peakRssBytes": 114102272, + "cpuTimeMs": 500 + }, + "conformance": { + "connect": { + "passed": true + }, + "javascript": { + "passed": true + }, + "dom": { + "passed": true + }, + "forms": { + "passed": true, + "actionability": "dom-non-visual" + }, + "screenshot": { + "passed": true, + "supported": false, + "state": "unsupported", + "enforcement": "preflight" + }, + "teardown": { + "passed": true, + "rootProcessExited": true, + "transportDisposed": true + } + } + }, + { + "timings": { + "launchMs": 797.5705000000016, + "connectMs": 10.851800000004005, + "workloadMs": 76.16049999999814, + "shutdownMs": 692.5248000000138 + }, + "browserTree": { + "peakRssBytes": 29491200, + "cpuTimeMs": 63, + "processCount": 1, + "sampleCount": 24, + "observations": [ + { + "sampledAt": "2026-09-02T19:55:29.320Z", + "rssBytes": 16240640, + "cpuTimeMs": 16, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:55:29.881Z", + "rssBytes": 29491200, + "cpuTimeMs": 63, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:55:30.437Z", + "rssBytes": 29491200, + "cpuTimeMs": 63, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:55:30.956Z", + "rssBytes": 29491200, + "cpuTimeMs": 63, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:55:31.475Z", + "rssBytes": 29491200, + "cpuTimeMs": 63, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:55:31.999Z", + "rssBytes": 29491200, + "cpuTimeMs": 63, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:55:32.522Z", + "rssBytes": 29491200, + "cpuTimeMs": 63, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:55:33.048Z", + "rssBytes": 29491200, + "cpuTimeMs": 63, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:55:33.562Z", + "rssBytes": 29491200, + "cpuTimeMs": 63, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:55:34.092Z", + "rssBytes": 29491200, + "cpuTimeMs": 63, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:55:34.617Z", + "rssBytes": 29491200, + "cpuTimeMs": 63, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:55:35.158Z", + "rssBytes": 29491200, + "cpuTimeMs": 63, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:55:35.714Z", + "rssBytes": 29491200, + "cpuTimeMs": 63, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:55:36.255Z", + "rssBytes": 29491200, + "cpuTimeMs": 63, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:55:36.781Z", + "rssBytes": 29491200, + "cpuTimeMs": 63, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:55:37.307Z", + "rssBytes": 29491200, + "cpuTimeMs": 63, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:55:37.833Z", + "rssBytes": 29491200, + "cpuTimeMs": 63, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:55:38.348Z", + "rssBytes": 29491200, + "cpuTimeMs": 63, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:55:38.868Z", + "rssBytes": 29491200, + "cpuTimeMs": 63, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:55:39.393Z", + "rssBytes": 29491200, + "cpuTimeMs": 63, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:55:39.917Z", + "rssBytes": 29491200, + "cpuTimeMs": 63, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:55:40.436Z", + "rssBytes": 29491200, + "cpuTimeMs": 63, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:55:40.966Z", + "rssBytes": 29491200, + "cpuTimeMs": 63, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:55:41.492Z", + "rssBytes": 29491200, + "cpuTimeMs": 63, + "processCount": 1 + } + ] + }, + "coordinator": { + "peakRssBytes": 109584384, + "cpuTimeMs": 610 + }, + "conformance": { + "connect": { + "passed": true + }, + "javascript": { + "passed": true + }, + "dom": { + "passed": true + }, + "forms": { + "passed": true, + "actionability": "dom-non-visual" + }, + "screenshot": { + "passed": true, + "supported": false, + "state": "unsupported", + "enforcement": "preflight" + }, + "teardown": { + "passed": true, + "rootProcessExited": true, + "transportDisposed": true + } + } + }, + { + "timings": { + "launchMs": 836.5996999999916, + "connectMs": 9.488600000011502, + "workloadMs": 81.1923999999999, + "shutdownMs": 710.9101999999839 + }, + "browserTree": { + "peakRssBytes": 29446144, + "cpuTimeMs": 47, + "processCount": 1, + "sampleCount": 24, + "observations": [ + { + "sampledAt": "2026-09-02T19:55:42.920Z", + "rssBytes": 16125952, + "cpuTimeMs": 16, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:55:43.477Z", + "rssBytes": 29446144, + "cpuTimeMs": 47, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:55:44.038Z", + "rssBytes": 29446144, + "cpuTimeMs": 47, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:55:44.555Z", + "rssBytes": 29446144, + "cpuTimeMs": 47, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:55:45.080Z", + "rssBytes": 29446144, + "cpuTimeMs": 47, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:55:45.598Z", + "rssBytes": 29446144, + "cpuTimeMs": 47, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:55:46.119Z", + "rssBytes": 29446144, + "cpuTimeMs": 47, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:55:46.637Z", + "rssBytes": 29446144, + "cpuTimeMs": 47, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:55:47.163Z", + "rssBytes": 29446144, + "cpuTimeMs": 47, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:55:47.680Z", + "rssBytes": 29446144, + "cpuTimeMs": 47, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:55:48.210Z", + "rssBytes": 29446144, + "cpuTimeMs": 47, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:55:48.727Z", + "rssBytes": 29446144, + "cpuTimeMs": 47, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:55:49.246Z", + "rssBytes": 29446144, + "cpuTimeMs": 47, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:55:49.770Z", + "rssBytes": 29446144, + "cpuTimeMs": 47, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:55:50.294Z", + "rssBytes": 29446144, + "cpuTimeMs": 47, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:55:50.817Z", + "rssBytes": 29446144, + "cpuTimeMs": 47, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:55:51.344Z", + "rssBytes": 29446144, + "cpuTimeMs": 47, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:55:51.861Z", + "rssBytes": 29446144, + "cpuTimeMs": 47, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:55:52.380Z", + "rssBytes": 29446144, + "cpuTimeMs": 47, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:55:52.906Z", + "rssBytes": 29446144, + "cpuTimeMs": 47, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:55:53.426Z", + "rssBytes": 29446144, + "cpuTimeMs": 47, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:55:53.947Z", + "rssBytes": 29446144, + "cpuTimeMs": 47, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:55:54.490Z", + "rssBytes": 29446144, + "cpuTimeMs": 47, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:55:55.029Z", + "rssBytes": 29446144, + "cpuTimeMs": 47, + "processCount": 1 + } + ] + }, + "coordinator": { + "peakRssBytes": 112349184, + "cpuTimeMs": 624 + }, + "conformance": { + "connect": { + "passed": true + }, + "javascript": { + "passed": true + }, + "dom": { + "passed": true + }, + "forms": { + "passed": true, + "actionability": "dom-non-visual" + }, + "screenshot": { + "passed": true, + "supported": false, + "state": "unsupported", + "enforcement": "preflight" + }, + "teardown": { + "passed": true, + "rootProcessExited": true, + "transportDisposed": true + } + } + }, + { + "timings": { + "launchMs": 821.5632000000041, + "connectMs": 9.748699999996461, + "workloadMs": 77.8414000000048, + "shutdownMs": 685.8233999999939 + }, + "browserTree": { + "peakRssBytes": 29450240, + "cpuTimeMs": 94, + "processCount": 1, + "sampleCount": 24, + "observations": [ + { + "sampledAt": "2026-09-02T19:55:56.446Z", + "rssBytes": 16191488, + "cpuTimeMs": 16, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:55:57.035Z", + "rssBytes": 29450240, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:55:57.592Z", + "rssBytes": 29450240, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:55:58.137Z", + "rssBytes": 29450240, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:55:58.658Z", + "rssBytes": 29450240, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:55:59.220Z", + "rssBytes": 29450240, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:55:59.749Z", + "rssBytes": 29450240, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:56:00.277Z", + "rssBytes": 29450240, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:56:00.847Z", + "rssBytes": 29450240, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:56:01.364Z", + "rssBytes": 29450240, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:56:01.883Z", + "rssBytes": 29450240, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:56:02.398Z", + "rssBytes": 29450240, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:56:02.930Z", + "rssBytes": 29450240, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:56:03.449Z", + "rssBytes": 29450240, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:56:03.970Z", + "rssBytes": 29450240, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:56:04.490Z", + "rssBytes": 29450240, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:56:05.017Z", + "rssBytes": 29450240, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:56:05.536Z", + "rssBytes": 29450240, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:56:06.055Z", + "rssBytes": 29450240, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:56:06.581Z", + "rssBytes": 29450240, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:56:07.111Z", + "rssBytes": 29450240, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:56:07.633Z", + "rssBytes": 29450240, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:56:08.153Z", + "rssBytes": 29450240, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:56:08.680Z", + "rssBytes": 29450240, + "cpuTimeMs": 94, + "processCount": 1 + } + ] + }, + "coordinator": { + "peakRssBytes": 116649984, + "cpuTimeMs": 516 + }, + "conformance": { + "connect": { + "passed": true + }, + "javascript": { + "passed": true + }, + "dom": { + "passed": true + }, + "forms": { + "passed": true, + "actionability": "dom-non-visual" + }, + "screenshot": { + "passed": true, + "supported": false, + "state": "unsupported", + "enforcement": "preflight" + }, + "teardown": { + "passed": true, + "rootProcessExited": true, + "transportDisposed": true + } + } + }, + { + "timings": { + "launchMs": 793.1128000000026, + "connectMs": 10.06369999999879, + "workloadMs": 74.2153999999864, + "shutdownMs": 695.5141000000003 + }, + "browserTree": { + "peakRssBytes": 29745152, + "cpuTimeMs": 78, + "processCount": 1, + "sampleCount": 24, + "observations": [ + { + "sampledAt": "2026-09-02T19:56:10.050Z", + "rssBytes": 16310272, + "cpuTimeMs": 16, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:56:10.615Z", + "rssBytes": 29745152, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:56:11.160Z", + "rssBytes": 29745152, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:56:11.675Z", + "rssBytes": 29745152, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:56:12.194Z", + "rssBytes": 29745152, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:56:12.712Z", + "rssBytes": 29745152, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:56:13.233Z", + "rssBytes": 29745152, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:56:13.746Z", + "rssBytes": 29745152, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:56:14.269Z", + "rssBytes": 29745152, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:56:14.792Z", + "rssBytes": 29745152, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:56:15.319Z", + "rssBytes": 29745152, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:56:15.841Z", + "rssBytes": 29745152, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:56:16.361Z", + "rssBytes": 29745152, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:56:16.874Z", + "rssBytes": 29745152, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:56:17.394Z", + "rssBytes": 29745152, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:56:17.914Z", + "rssBytes": 29745152, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:56:18.434Z", + "rssBytes": 29745152, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:56:18.959Z", + "rssBytes": 29745152, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:56:19.483Z", + "rssBytes": 29745152, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:56:20.029Z", + "rssBytes": 29745152, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:56:20.552Z", + "rssBytes": 29745152, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:56:21.082Z", + "rssBytes": 29745152, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:56:21.609Z", + "rssBytes": 29745152, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:56:22.147Z", + "rssBytes": 29745152, + "cpuTimeMs": 78, + "processCount": 1 + } + ] + }, + "coordinator": { + "peakRssBytes": 107122688, + "cpuTimeMs": 610 + }, + "conformance": { + "connect": { + "passed": true + }, + "javascript": { + "passed": true + }, + "dom": { + "passed": true + }, + "forms": { + "passed": true, + "actionability": "dom-non-visual" + }, + "screenshot": { + "passed": true, + "supported": false, + "state": "unsupported", + "enforcement": "preflight" + }, + "teardown": { + "passed": true, + "rootProcessExited": true, + "transportDisposed": true + } + } + }, + { + "timings": { + "launchMs": 798.2816000000166, + "connectMs": 10.632700000016484, + "workloadMs": 110.34090000001015, + "shutdownMs": 681.5675999999803 + }, + "browserTree": { + "peakRssBytes": 29200384, + "cpuTimeMs": 156, + "processCount": 1, + "sampleCount": 24, + "observations": [ + { + "sampledAt": "2026-09-02T19:56:23.538Z", + "rssBytes": 16220160, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:56:24.108Z", + "rssBytes": 29200384, + "cpuTimeMs": 156, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:56:24.669Z", + "rssBytes": 29200384, + "cpuTimeMs": 156, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:56:25.189Z", + "rssBytes": 29200384, + "cpuTimeMs": 156, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:56:25.708Z", + "rssBytes": 29200384, + "cpuTimeMs": 156, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:56:26.228Z", + "rssBytes": 29200384, + "cpuTimeMs": 156, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:56:26.751Z", + "rssBytes": 29200384, + "cpuTimeMs": 156, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:56:27.279Z", + "rssBytes": 29200384, + "cpuTimeMs": 156, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:56:27.806Z", + "rssBytes": 29200384, + "cpuTimeMs": 156, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:56:28.331Z", + "rssBytes": 29200384, + "cpuTimeMs": 156, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:56:28.854Z", + "rssBytes": 29200384, + "cpuTimeMs": 156, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:56:29.368Z", + "rssBytes": 29200384, + "cpuTimeMs": 156, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:56:29.888Z", + "rssBytes": 29200384, + "cpuTimeMs": 156, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:56:30.413Z", + "rssBytes": 29200384, + "cpuTimeMs": 156, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:56:30.939Z", + "rssBytes": 29200384, + "cpuTimeMs": 156, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:56:31.468Z", + "rssBytes": 29200384, + "cpuTimeMs": 156, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:56:31.999Z", + "rssBytes": 29200384, + "cpuTimeMs": 156, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:56:32.519Z", + "rssBytes": 29200384, + "cpuTimeMs": 156, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:56:33.054Z", + "rssBytes": 29200384, + "cpuTimeMs": 156, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:56:33.623Z", + "rssBytes": 29200384, + "cpuTimeMs": 156, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:56:34.154Z", + "rssBytes": 29200384, + "cpuTimeMs": 156, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:56:34.683Z", + "rssBytes": 29200384, + "cpuTimeMs": 156, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:56:35.212Z", + "rssBytes": 29200384, + "cpuTimeMs": 156, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:56:35.735Z", + "rssBytes": 29200384, + "cpuTimeMs": 156, + "processCount": 1 + } + ] + }, + "coordinator": { + "peakRssBytes": 112963584, + "cpuTimeMs": 640 + }, + "conformance": { + "connect": { + "passed": true + }, + "javascript": { + "passed": true + }, + "dom": { + "passed": true + }, + "forms": { + "passed": true, + "actionability": "dom-non-visual" + }, + "screenshot": { + "passed": true, + "supported": false, + "state": "unsupported", + "enforcement": "preflight" + }, + "teardown": { + "passed": true, + "rootProcessExited": true, + "transportDisposed": true + } + } + }, + { + "timings": { + "launchMs": 759.6841999999888, + "connectMs": 6.967899999988731, + "workloadMs": 77.78390000000945, + "shutdownMs": 695.1073000000033 + }, + "browserTree": { + "peakRssBytes": 29372416, + "cpuTimeMs": 109, + "processCount": 1, + "sampleCount": 24, + "observations": [ + { + "sampledAt": "2026-09-02T19:56:37.090Z", + "rssBytes": 16179200, + "cpuTimeMs": 31, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:56:37.657Z", + "rssBytes": 29372416, + "cpuTimeMs": 109, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:56:38.222Z", + "rssBytes": 29372416, + "cpuTimeMs": 109, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:56:38.739Z", + "rssBytes": 29372416, + "cpuTimeMs": 109, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:56:39.270Z", + "rssBytes": 29372416, + "cpuTimeMs": 109, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:56:39.801Z", + "rssBytes": 29372416, + "cpuTimeMs": 109, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:56:40.321Z", + "rssBytes": 29372416, + "cpuTimeMs": 109, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:56:40.839Z", + "rssBytes": 29372416, + "cpuTimeMs": 109, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:56:41.356Z", + "rssBytes": 29372416, + "cpuTimeMs": 109, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:56:41.896Z", + "rssBytes": 29372416, + "cpuTimeMs": 109, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:56:42.462Z", + "rssBytes": 29372416, + "cpuTimeMs": 109, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:56:43.103Z", + "rssBytes": 29372416, + "cpuTimeMs": 109, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:56:43.623Z", + "rssBytes": 29372416, + "cpuTimeMs": 109, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:56:44.148Z", + "rssBytes": 29372416, + "cpuTimeMs": 109, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:56:44.669Z", + "rssBytes": 29372416, + "cpuTimeMs": 109, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:56:45.184Z", + "rssBytes": 29372416, + "cpuTimeMs": 109, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:56:45.712Z", + "rssBytes": 29372416, + "cpuTimeMs": 109, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:56:46.240Z", + "rssBytes": 29372416, + "cpuTimeMs": 109, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:56:46.765Z", + "rssBytes": 29372416, + "cpuTimeMs": 109, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:56:47.280Z", + "rssBytes": 29372416, + "cpuTimeMs": 109, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:56:47.818Z", + "rssBytes": 29372416, + "cpuTimeMs": 109, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:56:48.342Z", + "rssBytes": 29372416, + "cpuTimeMs": 109, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:56:48.867Z", + "rssBytes": 29372416, + "cpuTimeMs": 109, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:56:49.384Z", + "rssBytes": 29372416, + "cpuTimeMs": 109, + "processCount": 1 + } + ] + }, + "coordinator": { + "peakRssBytes": 122474496, + "cpuTimeMs": 516 + }, + "conformance": { + "connect": { + "passed": true + }, + "javascript": { + "passed": true + }, + "dom": { + "passed": true + }, + "forms": { + "passed": true, + "actionability": "dom-non-visual" + }, + "screenshot": { + "passed": true, + "supported": false, + "state": "unsupported", + "enforcement": "preflight" + }, + "teardown": { + "passed": true, + "rootProcessExited": true, + "transportDisposed": true + } + } + }, + { + "timings": { + "launchMs": 765.7957999999926, + "connectMs": 7.583500000007916, + "workloadMs": 80.26999999998952, + "shutdownMs": 707.7410000000091 + }, + "browserTree": { + "peakRssBytes": 29511680, + "cpuTimeMs": 94, + "processCount": 1, + "sampleCount": 24, + "observations": [ + { + "sampledAt": "2026-09-02T19:56:50.742Z", + "rssBytes": 16232448, + "cpuTimeMs": 16, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:56:51.309Z", + "rssBytes": 29511680, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:56:51.866Z", + "rssBytes": 29511680, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:56:52.386Z", + "rssBytes": 29511680, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:56:52.918Z", + "rssBytes": 29511680, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:56:53.443Z", + "rssBytes": 29511680, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:56:53.969Z", + "rssBytes": 29511680, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:56:54.486Z", + "rssBytes": 29511680, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:56:55.018Z", + "rssBytes": 29511680, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:56:55.545Z", + "rssBytes": 29511680, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:56:56.063Z", + "rssBytes": 29511680, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:56:56.579Z", + "rssBytes": 29511680, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:56:57.103Z", + "rssBytes": 29511680, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:56:57.618Z", + "rssBytes": 29511680, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:56:58.149Z", + "rssBytes": 29511680, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:56:58.669Z", + "rssBytes": 29511680, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:56:59.186Z", + "rssBytes": 29511680, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:56:59.708Z", + "rssBytes": 29511680, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:57:00.237Z", + "rssBytes": 29511680, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:57:00.827Z", + "rssBytes": 29511680, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:57:01.366Z", + "rssBytes": 29511680, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:57:01.913Z", + "rssBytes": 29511680, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:57:02.442Z", + "rssBytes": 29511680, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:57:02.977Z", + "rssBytes": 29511680, + "cpuTimeMs": 94, + "processCount": 1 + } + ] + }, + "coordinator": { + "peakRssBytes": 113524736, + "cpuTimeMs": 546 + }, + "conformance": { + "connect": { + "passed": true + }, + "javascript": { + "passed": true + }, + "dom": { + "passed": true + }, + "forms": { + "passed": true, + "actionability": "dom-non-visual" + }, + "screenshot": { + "passed": true, + "supported": false, + "state": "unsupported", + "enforcement": "preflight" + }, + "teardown": { + "passed": true, + "rootProcessExited": true, + "transportDisposed": true + } + } + }, + { + "timings": { + "launchMs": 811.1236000000208, + "connectMs": 10.238699999987148, + "workloadMs": 102.49629999999888, + "shutdownMs": 683.8581000000122 + }, + "browserTree": { + "peakRssBytes": 29331456, + "cpuTimeMs": 47, + "processCount": 1, + "sampleCount": 24, + "observations": [ + { + "sampledAt": "2026-09-02T19:57:04.388Z", + "rssBytes": 16293888, + "cpuTimeMs": 16, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:57:04.974Z", + "rssBytes": 29331456, + "cpuTimeMs": 47, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:57:05.561Z", + "rssBytes": 29331456, + "cpuTimeMs": 47, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:57:06.082Z", + "rssBytes": 29331456, + "cpuTimeMs": 47, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:57:06.600Z", + "rssBytes": 29331456, + "cpuTimeMs": 47, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:57:07.122Z", + "rssBytes": 29331456, + "cpuTimeMs": 47, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:57:07.644Z", + "rssBytes": 29331456, + "cpuTimeMs": 47, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:57:08.178Z", + "rssBytes": 29331456, + "cpuTimeMs": 47, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:57:08.703Z", + "rssBytes": 29331456, + "cpuTimeMs": 47, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:57:09.220Z", + "rssBytes": 29331456, + "cpuTimeMs": 47, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:57:09.743Z", + "rssBytes": 29331456, + "cpuTimeMs": 47, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:57:10.260Z", + "rssBytes": 29331456, + "cpuTimeMs": 47, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:57:10.785Z", + "rssBytes": 29331456, + "cpuTimeMs": 47, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:57:11.307Z", + "rssBytes": 29331456, + "cpuTimeMs": 47, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:57:11.831Z", + "rssBytes": 29331456, + "cpuTimeMs": 47, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:57:12.354Z", + "rssBytes": 29331456, + "cpuTimeMs": 47, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:57:12.872Z", + "rssBytes": 29331456, + "cpuTimeMs": 47, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:57:13.397Z", + "rssBytes": 29331456, + "cpuTimeMs": 47, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:57:13.922Z", + "rssBytes": 29331456, + "cpuTimeMs": 47, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:57:14.545Z", + "rssBytes": 29331456, + "cpuTimeMs": 47, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:57:15.074Z", + "rssBytes": 29331456, + "cpuTimeMs": 47, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:57:15.626Z", + "rssBytes": 29331456, + "cpuTimeMs": 47, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:57:16.155Z", + "rssBytes": 29331456, + "cpuTimeMs": 47, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:57:16.666Z", + "rssBytes": 29331456, + "cpuTimeMs": 47, + "processCount": 1 + } + ] + }, + "coordinator": { + "peakRssBytes": 107454464, + "cpuTimeMs": 579 + }, + "conformance": { + "connect": { + "passed": true + }, + "javascript": { + "passed": true + }, + "dom": { + "passed": true + }, + "forms": { + "passed": true, + "actionability": "dom-non-visual" + }, + "screenshot": { + "passed": true, + "supported": false, + "state": "unsupported", + "enforcement": "preflight" + }, + "teardown": { + "passed": true, + "rootProcessExited": true, + "transportDisposed": true + } + } + }, + { + "timings": { + "launchMs": 787.2588999999862, + "connectMs": 7.793600000004517, + "workloadMs": 76.45799999998417, + "shutdownMs": 688.0648999999976 + }, + "browserTree": { + "peakRssBytes": 29569024, + "cpuTimeMs": 94, + "processCount": 1, + "sampleCount": 24, + "observations": [ + { + "sampledAt": "2026-09-02T19:57:18.033Z", + "rssBytes": 16257024, + "cpuTimeMs": 31, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:57:18.600Z", + "rssBytes": 29569024, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:57:19.164Z", + "rssBytes": 29569024, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:57:19.685Z", + "rssBytes": 29569024, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:57:20.214Z", + "rssBytes": 29569024, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:57:20.734Z", + "rssBytes": 29569024, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:57:21.280Z", + "rssBytes": 29569024, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:57:21.803Z", + "rssBytes": 29569024, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:57:22.318Z", + "rssBytes": 29569024, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:57:22.841Z", + "rssBytes": 29569024, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:57:23.364Z", + "rssBytes": 29569024, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:57:23.885Z", + "rssBytes": 29569024, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:57:24.407Z", + "rssBytes": 29569024, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:57:24.931Z", + "rssBytes": 29569024, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:57:25.452Z", + "rssBytes": 29569024, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:57:25.991Z", + "rssBytes": 29569024, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:57:26.519Z", + "rssBytes": 29569024, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:57:27.056Z", + "rssBytes": 29569024, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:57:27.588Z", + "rssBytes": 29569024, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:57:28.113Z", + "rssBytes": 29569024, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:57:28.644Z", + "rssBytes": 29569024, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:57:29.174Z", + "rssBytes": 29569024, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:57:29.699Z", + "rssBytes": 29569024, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:57:30.229Z", + "rssBytes": 29569024, + "cpuTimeMs": 94, + "processCount": 1 + } + ] + }, + "coordinator": { + "peakRssBytes": 106627072, + "cpuTimeMs": 453 + }, + "conformance": { + "connect": { + "passed": true + }, + "javascript": { + "passed": true + }, + "dom": { + "passed": true + }, + "forms": { + "passed": true, + "actionability": "dom-non-visual" + }, + "screenshot": { + "passed": true, + "supported": false, + "state": "unsupported", + "enforcement": "preflight" + }, + "teardown": { + "passed": true, + "rootProcessExited": true, + "transportDisposed": true + } + } + }, + { + "timings": { + "launchMs": 787.948000000004, + "connectMs": 10.03839999999036, + "workloadMs": 75.69000000000233, + "shutdownMs": 680.5192000000097 + }, + "browserTree": { + "peakRssBytes": 29605888, + "cpuTimeMs": 78, + "processCount": 1, + "sampleCount": 24, + "observations": [ + { + "sampledAt": "2026-09-02T19:57:31.597Z", + "rssBytes": 16293888, + "cpuTimeMs": 16, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:57:32.156Z", + "rssBytes": 29605888, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:57:32.704Z", + "rssBytes": 29605888, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:57:33.224Z", + "rssBytes": 29605888, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:57:33.741Z", + "rssBytes": 29605888, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:57:34.260Z", + "rssBytes": 29605888, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:57:34.782Z", + "rssBytes": 29605888, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:57:35.309Z", + "rssBytes": 29605888, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:57:35.832Z", + "rssBytes": 29605888, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:57:36.354Z", + "rssBytes": 29605888, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:57:36.878Z", + "rssBytes": 29605888, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:57:37.392Z", + "rssBytes": 29605888, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:57:37.914Z", + "rssBytes": 29605888, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:57:38.434Z", + "rssBytes": 29605888, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:57:38.960Z", + "rssBytes": 29605888, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:57:39.482Z", + "rssBytes": 29605888, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:57:40.007Z", + "rssBytes": 29605888, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:57:40.538Z", + "rssBytes": 29605888, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:57:41.063Z", + "rssBytes": 29605888, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:57:41.578Z", + "rssBytes": 29605888, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:57:42.096Z", + "rssBytes": 29605888, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:57:42.620Z", + "rssBytes": 29605888, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:57:43.167Z", + "rssBytes": 29605888, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:57:43.683Z", + "rssBytes": 29605888, + "cpuTimeMs": 78, + "processCount": 1 + } + ] + }, + "coordinator": { + "peakRssBytes": 105873408, + "cpuTimeMs": 578 + }, + "conformance": { + "connect": { + "passed": true + }, + "javascript": { + "passed": true + }, + "dom": { + "passed": true + }, + "forms": { + "passed": true, + "actionability": "dom-non-visual" + }, + "screenshot": { + "passed": true, + "supported": false, + "state": "unsupported", + "enforcement": "preflight" + }, + "teardown": { + "passed": true, + "rootProcessExited": true, + "transportDisposed": true + } + } + }, + { + "timings": { + "launchMs": 761.3894999999902, + "connectMs": 8.019400000019232, + "workloadMs": 78.94189999997616, + "shutdownMs": 680.6283999999869 + }, + "browserTree": { + "peakRssBytes": 29556736, + "cpuTimeMs": 141, + "processCount": 1, + "sampleCount": 24, + "observations": [ + { + "sampledAt": "2026-09-02T19:57:45.033Z", + "rssBytes": 16187392, + "cpuTimeMs": 47, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:57:45.587Z", + "rssBytes": 29556736, + "cpuTimeMs": 141, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:57:46.138Z", + "rssBytes": 29556736, + "cpuTimeMs": 141, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:57:46.664Z", + "rssBytes": 29556736, + "cpuTimeMs": 141, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:57:47.175Z", + "rssBytes": 29556736, + "cpuTimeMs": 141, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:57:47.707Z", + "rssBytes": 29556736, + "cpuTimeMs": 141, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:57:48.231Z", + "rssBytes": 29556736, + "cpuTimeMs": 141, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:57:48.763Z", + "rssBytes": 29556736, + "cpuTimeMs": 141, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:57:49.279Z", + "rssBytes": 29556736, + "cpuTimeMs": 141, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:57:49.806Z", + "rssBytes": 29556736, + "cpuTimeMs": 141, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:57:50.324Z", + "rssBytes": 29556736, + "cpuTimeMs": 141, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:57:50.848Z", + "rssBytes": 29556736, + "cpuTimeMs": 141, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:57:51.372Z", + "rssBytes": 29556736, + "cpuTimeMs": 141, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:57:51.898Z", + "rssBytes": 29556736, + "cpuTimeMs": 141, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:57:52.417Z", + "rssBytes": 29556736, + "cpuTimeMs": 141, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:57:52.946Z", + "rssBytes": 29556736, + "cpuTimeMs": 141, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:57:53.460Z", + "rssBytes": 29556736, + "cpuTimeMs": 141, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:57:53.980Z", + "rssBytes": 29556736, + "cpuTimeMs": 141, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:57:54.491Z", + "rssBytes": 29556736, + "cpuTimeMs": 141, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:57:55.013Z", + "rssBytes": 29556736, + "cpuTimeMs": 141, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:57:55.526Z", + "rssBytes": 29556736, + "cpuTimeMs": 141, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:57:56.044Z", + "rssBytes": 29556736, + "cpuTimeMs": 141, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:57:56.556Z", + "rssBytes": 29556736, + "cpuTimeMs": 141, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:57:57.073Z", + "rssBytes": 29556736, + "cpuTimeMs": 141, + "processCount": 1 + } + ] + }, + "coordinator": { + "peakRssBytes": 126640128, + "cpuTimeMs": 578 + }, + "conformance": { + "connect": { + "passed": true + }, + "javascript": { + "passed": true + }, + "dom": { + "passed": true + }, + "forms": { + "passed": true, + "actionability": "dom-non-visual" + }, + "screenshot": { + "passed": true, + "supported": false, + "state": "unsupported", + "enforcement": "preflight" + }, + "teardown": { + "passed": true, + "rootProcessExited": true, + "transportDisposed": true + } + } + }, + { + "timings": { + "launchMs": 761.961500000034, + "connectMs": 8.025499999988824, + "workloadMs": 76.02679999999236, + "shutdownMs": 703.4151999999885 + }, + "browserTree": { + "peakRssBytes": 28831744, + "cpuTimeMs": 94, + "processCount": 1, + "sampleCount": 24, + "observations": [ + { + "sampledAt": "2026-09-02T19:57:58.423Z", + "rssBytes": 16162816, + "cpuTimeMs": 16, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:57:58.997Z", + "rssBytes": 28831744, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:57:59.556Z", + "rssBytes": 28831744, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:58:00.081Z", + "rssBytes": 28831744, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:58:00.624Z", + "rssBytes": 28831744, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:58:01.144Z", + "rssBytes": 28831744, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:58:01.670Z", + "rssBytes": 28831744, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:58:02.187Z", + "rssBytes": 28831744, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:58:02.701Z", + "rssBytes": 28831744, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:58:03.223Z", + "rssBytes": 28831744, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:58:03.747Z", + "rssBytes": 28831744, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:58:04.262Z", + "rssBytes": 28831744, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:58:04.777Z", + "rssBytes": 28831744, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:58:05.304Z", + "rssBytes": 28831744, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:58:05.829Z", + "rssBytes": 28831744, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:58:06.376Z", + "rssBytes": 28831744, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:58:06.912Z", + "rssBytes": 28831744, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:58:07.452Z", + "rssBytes": 28831744, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:58:07.995Z", + "rssBytes": 28831744, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:58:08.530Z", + "rssBytes": 28831744, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:58:09.081Z", + "rssBytes": 28831744, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:58:09.621Z", + "rssBytes": 28831744, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:58:10.149Z", + "rssBytes": 28831744, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:58:10.678Z", + "rssBytes": 28831744, + "cpuTimeMs": 94, + "processCount": 1 + } + ] + }, + "coordinator": { + "peakRssBytes": 114204672, + "cpuTimeMs": 422 + }, + "conformance": { + "connect": { + "passed": true + }, + "javascript": { + "passed": true + }, + "dom": { + "passed": true + }, + "forms": { + "passed": true, + "actionability": "dom-non-visual" + }, + "screenshot": { + "passed": true, + "supported": false, + "state": "unsupported", + "enforcement": "preflight" + }, + "teardown": { + "passed": true, + "rootProcessExited": true, + "transportDisposed": true + } + } + } + ] +} diff --git a/docs/benchmarks/artifacts/obscura-0.2.1-constrained-non-visual-30mib-2026-09-03-rc1.json b/docs/benchmarks/artifacts/obscura-0.2.1-constrained-non-visual-30mib-2026-09-03-rc1.json new file mode 100644 index 0000000..6e5ec73 --- /dev/null +++ b/docs/benchmarks/artifacts/obscura-0.2.1-constrained-non-visual-30mib-2026-09-03-rc1.json @@ -0,0 +1,3909 @@ +{ + "schemaVersion": "cockroach.lightweight-browser-benchmark.v2", + "generatedAt": "2026-09-02T20:34:11.005Z", + "implementation": "obscura", + "resourceProfile": "constrained", + "renderingPolicy": "visual-actions-denied", + "executable": { + "name": "obscura.exe", + "bytes": 58097152, + "sha256": "5b609fb46bc00da79e450fb0fbd34bd442e565b1394f4af95433e0b341078221", + "version": "obscura 0.2.1" + }, + "sourceIdentity": { + "gitCommit": "deb6c25b4c0bc1927a631e3b062464b4f4cc2775", + "workingTreeDirty": true, + "sourceScope": "Runtime, schemas, benchmark/package/build configuration, and tests; generated documentation and benchmark artifacts are excluded.", + "identityNormalization": "Relative paths use forward slashes and text line endings are normalized to LF before hashing.", + "sourceFileCount": 100, + "sourceTreeSha256": "fb0c4597e39f319dd9b6f3bab02777c395e9d8d84906981bf939a39b470e7279", + "runtimeBuildFileCount": 200, + "runtimeBuildSha256": "6738efa4000ba482db83c9dc95ba2f21caed31de96f30dcd342e5dc722d86025", + "benchmarkScriptSha256": "08a5294f2d446765f712b93c9bfaaca010b1d043ded638d1f4f57b5038c97e86" + }, + "machine": { + "platform": "win32", + "architecture": "x64", + "release": "10.0.26200", + "logicalCpuCount": 16, + "cpuModel": "AMD Ryzen 7 4800H with Radeon Graphics ", + "totalMemoryBytes": 16557887488, + "freeMemoryBytesAtReport": 4148514816, + "node": "v24.15.0" + }, + "methodology": { + "warmup": 1, + "iterations": 20, + "settleMs": 500, + "startupTimeoutMs": 15000, + "actionTimeoutMs": 10000, + "teardownTimeoutMs": 5000, + "resourceSamples": 10, + "resourceSampleIntervalMs": 25, + "targetMiB": 30, + "targetDefinition": "Every measured owned-browser process-tree RSS sample is at or below the target and every required CDP/JavaScript/DOM/form/screenshot-preflight/teardown check passes.", + "memoryDefinition": "Aggregate RSS for the complete owned browser process tree; the Node coordinator is disclosed separately and is not hidden in browser RSS.", + "renderingControl": "Cockroach Browser declares a no-visual-action policy and rejects capture at preflight. This does not assert that the selected engine binary exposes or received a renderer-disable launch switch.", + "workload": "One runtime-owned loopback CDP server, one non-visual data-URL document, JavaScript evaluation, DOM query, input, and click.", + "requiredChecks": [ + "connect", + "javascript", + "dom", + "forms", + "screenshot", + "teardown" + ], + "optionalChecks": [], + "energy": { + "available": false, + "reason": "No calibrated platform energy counter is available; CPU time is not watts." + } + }, + "verdict": { + "passed": true, + "everyRunWithinTarget": true, + "requiredConformancePassed": true, + "targetBytes": 31457280, + "maxObservedBrowserTreeRssBytes": 29622272 + }, + "summary": { + "peakBrowserTreeRssBytes": { + "median": 29347840, + "p95": 29569024, + "min": 28893184, + "max": 29622272 + }, + "browserCpuTimeMs": { + "median": 94, + "p95": 156, + "min": 63, + "max": 172 + }, + "processCount": { + "median": 1, + "p95": 1, + "min": 1, + "max": 1 + }, + "launchMs": { + "median": 819.5642999999982, + "p95": 1023.2062000000151, + "min": 764.1333999999915, + "max": 1143.2284999999974 + }, + "connectMs": { + "median": 9.60879999998724, + "p95": 17.389500000019325, + "min": 7.880500000013853, + "max": 23.149099999998725 + }, + "workloadMs": { + "median": 84.00669999996899, + "p95": 107.48889999999665, + "min": 74.14630000002217, + "max": 119.59150000000955 + }, + "shutdownMs": { + "median": 704.2668000000413, + "p95": 954.4192999999796, + "min": 670.5003999999608, + "max": 1125.2972000000009 + }, + "coordinatorPeakRssBytes": { + "median": 110088192, + "p95": 128659456, + "min": 101769216, + "max": 128659456 + } + }, + "samples": [ + { + "timings": { + "launchMs": 873.0357999999997, + "connectMs": 23.149099999998725, + "workloadMs": 93.86089999999967, + "shutdownMs": 844.2731999999996 + }, + "browserTree": { + "peakRssBytes": 29622272, + "cpuTimeMs": 94, + "processCount": 1, + "sampleCount": 24, + "observations": [ + { + "sampledAt": "2026-09-02T20:29:23.687Z", + "rssBytes": 16302080, + "cpuTimeMs": 16, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:29:24.331Z", + "rssBytes": 29622272, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:29:25.188Z", + "rssBytes": 29622272, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:29:25.864Z", + "rssBytes": 29622272, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:29:26.657Z", + "rssBytes": 29622272, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:29:27.599Z", + "rssBytes": 29622272, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:29:28.202Z", + "rssBytes": 29622272, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:29:28.795Z", + "rssBytes": 29622272, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:29:29.439Z", + "rssBytes": 29622272, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:29:30.123Z", + "rssBytes": 29622272, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:29:30.714Z", + "rssBytes": 29622272, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:29:31.282Z", + "rssBytes": 29622272, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:29:31.865Z", + "rssBytes": 29622272, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:29:32.432Z", + "rssBytes": 29622272, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:29:32.994Z", + "rssBytes": 29622272, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:29:33.557Z", + "rssBytes": 29622272, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:29:34.157Z", + "rssBytes": 29622272, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:29:34.724Z", + "rssBytes": 29622272, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:29:35.291Z", + "rssBytes": 29622272, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:29:35.868Z", + "rssBytes": 29622272, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:29:36.431Z", + "rssBytes": 29622272, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:29:37.010Z", + "rssBytes": 29622272, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:29:37.583Z", + "rssBytes": 29622272, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:29:38.156Z", + "rssBytes": 29622272, + "cpuTimeMs": 94, + "processCount": 1 + } + ] + }, + "coordinator": { + "peakRssBytes": 101769216, + "cpuTimeMs": 828 + }, + "conformance": { + "connect": { + "passed": true + }, + "javascript": { + "passed": true + }, + "dom": { + "passed": true + }, + "forms": { + "passed": true, + "actionability": "dom-non-visual" + }, + "screenshot": { + "passed": true, + "supported": false, + "state": "unsupported", + "enforcement": "preflight" + }, + "teardown": { + "passed": true, + "rootProcessExited": true, + "transportDisposed": true + } + } + }, + { + "timings": { + "launchMs": 957.7161000000015, + "connectMs": 14.298099999999977, + "workloadMs": 107.48889999999665, + "shutdownMs": 703.6197000000029 + }, + "browserTree": { + "peakRssBytes": 29446144, + "cpuTimeMs": 125, + "processCount": 1, + "sampleCount": 24, + "observations": [ + { + "sampledAt": "2026-09-02T20:29:39.853Z", + "rssBytes": 16166912, + "cpuTimeMs": 31, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:29:40.529Z", + "rssBytes": 29446144, + "cpuTimeMs": 125, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:29:41.152Z", + "rssBytes": 29446144, + "cpuTimeMs": 125, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:29:41.724Z", + "rssBytes": 29446144, + "cpuTimeMs": 125, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:29:42.298Z", + "rssBytes": 29446144, + "cpuTimeMs": 125, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:29:42.892Z", + "rssBytes": 29446144, + "cpuTimeMs": 125, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:29:43.530Z", + "rssBytes": 29446144, + "cpuTimeMs": 125, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:29:44.150Z", + "rssBytes": 29446144, + "cpuTimeMs": 125, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:29:44.787Z", + "rssBytes": 29446144, + "cpuTimeMs": 125, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:29:45.404Z", + "rssBytes": 29446144, + "cpuTimeMs": 125, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:29:45.983Z", + "rssBytes": 29446144, + "cpuTimeMs": 125, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:29:46.556Z", + "rssBytes": 29446144, + "cpuTimeMs": 125, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:29:47.129Z", + "rssBytes": 29446144, + "cpuTimeMs": 125, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:29:47.700Z", + "rssBytes": 29446144, + "cpuTimeMs": 125, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:29:48.268Z", + "rssBytes": 29446144, + "cpuTimeMs": 125, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:29:48.841Z", + "rssBytes": 29446144, + "cpuTimeMs": 125, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:29:49.431Z", + "rssBytes": 29446144, + "cpuTimeMs": 125, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:29:50.010Z", + "rssBytes": 29446144, + "cpuTimeMs": 125, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:29:50.587Z", + "rssBytes": 29446144, + "cpuTimeMs": 125, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:29:51.165Z", + "rssBytes": 29446144, + "cpuTimeMs": 125, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:29:51.703Z", + "rssBytes": 29446144, + "cpuTimeMs": 125, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:29:52.281Z", + "rssBytes": 29446144, + "cpuTimeMs": 125, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:29:52.839Z", + "rssBytes": 29446144, + "cpuTimeMs": 125, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:29:53.367Z", + "rssBytes": 29446144, + "cpuTimeMs": 125, + "processCount": 1 + } + ] + }, + "coordinator": { + "peakRssBytes": 106823680, + "cpuTimeMs": 844 + }, + "conformance": { + "connect": { + "passed": true + }, + "javascript": { + "passed": true + }, + "dom": { + "passed": true + }, + "forms": { + "passed": true, + "actionability": "dom-non-visual" + }, + "screenshot": { + "passed": true, + "supported": false, + "state": "unsupported", + "enforcement": "preflight" + }, + "teardown": { + "passed": true, + "rootProcessExited": true, + "transportDisposed": true + } + } + }, + { + "timings": { + "launchMs": 818.498599999999, + "connectMs": 10.083700000002864, + "workloadMs": 84.05510000000504, + "shutdownMs": 732.8411000000051 + }, + "browserTree": { + "peakRssBytes": 29417472, + "cpuTimeMs": 94, + "processCount": 1, + "sampleCount": 24, + "observations": [ + { + "sampledAt": "2026-09-02T20:29:54.775Z", + "rssBytes": 16125952, + "cpuTimeMs": 31, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:29:55.349Z", + "rssBytes": 29417472, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:29:55.916Z", + "rssBytes": 29417472, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:29:56.439Z", + "rssBytes": 29417472, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:29:56.969Z", + "rssBytes": 29417472, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:29:57.493Z", + "rssBytes": 29417472, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:29:58.029Z", + "rssBytes": 29417472, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:29:58.550Z", + "rssBytes": 29417472, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:29:59.082Z", + "rssBytes": 29417472, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:29:59.612Z", + "rssBytes": 29417472, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:30:00.142Z", + "rssBytes": 29417472, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:30:00.732Z", + "rssBytes": 29417472, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:30:01.255Z", + "rssBytes": 29417472, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:30:01.786Z", + "rssBytes": 29417472, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:30:02.316Z", + "rssBytes": 29417472, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:30:02.846Z", + "rssBytes": 29417472, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:30:03.374Z", + "rssBytes": 29417472, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:30:03.898Z", + "rssBytes": 29417472, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:30:04.442Z", + "rssBytes": 29417472, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:30:05.022Z", + "rssBytes": 29417472, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:30:05.570Z", + "rssBytes": 29417472, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:30:06.126Z", + "rssBytes": 29417472, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:30:06.680Z", + "rssBytes": 29417472, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:30:07.228Z", + "rssBytes": 29417472, + "cpuTimeMs": 94, + "processCount": 1 + } + ] + }, + "coordinator": { + "peakRssBytes": 106508288, + "cpuTimeMs": 703 + }, + "conformance": { + "connect": { + "passed": true + }, + "javascript": { + "passed": true + }, + "dom": { + "passed": true + }, + "forms": { + "passed": true, + "actionability": "dom-non-visual" + }, + "screenshot": { + "passed": true, + "supported": false, + "state": "unsupported", + "enforcement": "preflight" + }, + "teardown": { + "passed": true, + "rootProcessExited": true, + "transportDisposed": true + } + } + }, + { + "timings": { + "launchMs": 821.5866000000024, + "connectMs": 9.10399999999936, + "workloadMs": 79.39570000000094, + "shutdownMs": 700.3338999999978 + }, + "browserTree": { + "peakRssBytes": 29310976, + "cpuTimeMs": 78, + "processCount": 1, + "sampleCount": 24, + "observations": [ + { + "sampledAt": "2026-09-02T20:30:08.673Z", + "rssBytes": 16154624, + "cpuTimeMs": 16, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:30:09.249Z", + "rssBytes": 29310976, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:30:09.812Z", + "rssBytes": 29310976, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:30:10.359Z", + "rssBytes": 29310976, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:30:10.891Z", + "rssBytes": 29310976, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:30:11.422Z", + "rssBytes": 29310976, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:30:11.951Z", + "rssBytes": 29310976, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:30:12.485Z", + "rssBytes": 29310976, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:30:13.012Z", + "rssBytes": 29310976, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:30:13.579Z", + "rssBytes": 29310976, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:30:14.182Z", + "rssBytes": 29310976, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:30:14.713Z", + "rssBytes": 29310976, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:30:15.249Z", + "rssBytes": 29310976, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:30:15.775Z", + "rssBytes": 29310976, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:30:16.301Z", + "rssBytes": 29310976, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:30:16.845Z", + "rssBytes": 29310976, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:30:17.383Z", + "rssBytes": 29310976, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:30:17.913Z", + "rssBytes": 29310976, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:30:18.439Z", + "rssBytes": 29310976, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:30:18.962Z", + "rssBytes": 29310976, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:30:19.487Z", + "rssBytes": 29310976, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:30:20.020Z", + "rssBytes": 29310976, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:30:20.556Z", + "rssBytes": 29310976, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:30:21.089Z", + "rssBytes": 29310976, + "cpuTimeMs": 78, + "processCount": 1 + } + ] + }, + "coordinator": { + "peakRssBytes": 105140224, + "cpuTimeMs": 610 + }, + "conformance": { + "connect": { + "passed": true + }, + "javascript": { + "passed": true + }, + "dom": { + "passed": true + }, + "forms": { + "passed": true, + "actionability": "dom-non-visual" + }, + "screenshot": { + "passed": true, + "supported": false, + "state": "unsupported", + "enforcement": "preflight" + }, + "teardown": { + "passed": true, + "rootProcessExited": true, + "transportDisposed": true + } + } + }, + { + "timings": { + "launchMs": 844.338699999993, + "connectMs": 10.724799999996321, + "workloadMs": 95.56429999999818, + "shutdownMs": 749.2734999999957 + }, + "browserTree": { + "peakRssBytes": 29380608, + "cpuTimeMs": 94, + "processCount": 1, + "sampleCount": 24, + "observations": [ + { + "sampledAt": "2026-09-02T20:30:22.521Z", + "rssBytes": 16138240, + "cpuTimeMs": 16, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:30:23.152Z", + "rssBytes": 29380608, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:30:23.704Z", + "rssBytes": 29380608, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:30:24.228Z", + "rssBytes": 29380608, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:30:24.757Z", + "rssBytes": 29380608, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:30:25.292Z", + "rssBytes": 29380608, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:30:25.829Z", + "rssBytes": 29380608, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:30:26.363Z", + "rssBytes": 29380608, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:30:26.898Z", + "rssBytes": 29380608, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:30:27.419Z", + "rssBytes": 29380608, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:30:27.960Z", + "rssBytes": 29380608, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:30:28.492Z", + "rssBytes": 29380608, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:30:29.026Z", + "rssBytes": 29380608, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:30:29.544Z", + "rssBytes": 29380608, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:30:30.071Z", + "rssBytes": 29380608, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:30:30.591Z", + "rssBytes": 29380608, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:30:31.114Z", + "rssBytes": 29380608, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:30:31.689Z", + "rssBytes": 29380608, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:30:32.263Z", + "rssBytes": 29380608, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:30:32.822Z", + "rssBytes": 29380608, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:30:33.362Z", + "rssBytes": 29380608, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:30:33.897Z", + "rssBytes": 29380608, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:30:34.430Z", + "rssBytes": 29380608, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:30:34.970Z", + "rssBytes": 29380608, + "cpuTimeMs": 94, + "processCount": 1 + } + ] + }, + "coordinator": { + "peakRssBytes": 110088192, + "cpuTimeMs": 562 + }, + "conformance": { + "connect": { + "passed": true + }, + "javascript": { + "passed": true + }, + "dom": { + "passed": true + }, + "forms": { + "passed": true, + "actionability": "dom-non-visual" + }, + "screenshot": { + "passed": true, + "supported": false, + "state": "unsupported", + "enforcement": "preflight" + }, + "teardown": { + "passed": true, + "rootProcessExited": true, + "transportDisposed": true + } + } + }, + { + "timings": { + "launchMs": 819.5642999999982, + "connectMs": 7.880500000013853, + "workloadMs": 80.64579999999842, + "shutdownMs": 759.7467000000033 + }, + "browserTree": { + "peakRssBytes": 29368320, + "cpuTimeMs": 94, + "processCount": 1, + "sampleCount": 24, + "observations": [ + { + "sampledAt": "2026-09-02T20:30:36.443Z", + "rssBytes": 16199680, + "cpuTimeMs": 31, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:30:37.008Z", + "rssBytes": 29368320, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:30:37.612Z", + "rssBytes": 29368320, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:30:38.174Z", + "rssBytes": 29368320, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:30:38.703Z", + "rssBytes": 29368320, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:30:39.228Z", + "rssBytes": 29368320, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:30:39.760Z", + "rssBytes": 29368320, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:30:40.274Z", + "rssBytes": 29368320, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:30:40.803Z", + "rssBytes": 29368320, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:30:41.321Z", + "rssBytes": 29368320, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:30:41.840Z", + "rssBytes": 29368320, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:30:42.360Z", + "rssBytes": 29368320, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:30:42.947Z", + "rssBytes": 29368320, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:30:43.547Z", + "rssBytes": 29368320, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:30:44.082Z", + "rssBytes": 29368320, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:30:44.614Z", + "rssBytes": 29368320, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:30:45.149Z", + "rssBytes": 29368320, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:30:45.680Z", + "rssBytes": 29368320, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:30:46.208Z", + "rssBytes": 29368320, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:30:46.735Z", + "rssBytes": 29368320, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:30:47.261Z", + "rssBytes": 29368320, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:30:47.795Z", + "rssBytes": 29368320, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:30:48.323Z", + "rssBytes": 29368320, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:30:48.859Z", + "rssBytes": 29368320, + "cpuTimeMs": 94, + "processCount": 1 + } + ] + }, + "coordinator": { + "peakRssBytes": 108150784, + "cpuTimeMs": 688 + }, + "conformance": { + "connect": { + "passed": true + }, + "javascript": { + "passed": true + }, + "dom": { + "passed": true + }, + "forms": { + "passed": true, + "actionability": "dom-non-visual" + }, + "screenshot": { + "passed": true, + "supported": false, + "state": "unsupported", + "enforcement": "preflight" + }, + "teardown": { + "passed": true, + "rootProcessExited": true, + "transportDisposed": true + } + } + }, + { + "timings": { + "launchMs": 788.2201999999961, + "connectMs": 8.693299999998999, + "workloadMs": 76.9933999999921, + "shutdownMs": 692.3208000000013 + }, + "browserTree": { + "peakRssBytes": 29016064, + "cpuTimeMs": 78, + "processCount": 1, + "sampleCount": 24, + "observations": [ + { + "sampledAt": "2026-09-02T20:30:50.323Z", + "rssBytes": 16236544, + "cpuTimeMs": 16, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:30:50.897Z", + "rssBytes": 29016064, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:30:51.452Z", + "rssBytes": 29016064, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:30:51.976Z", + "rssBytes": 29016064, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:30:52.501Z", + "rssBytes": 29016064, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:30:53.035Z", + "rssBytes": 29016064, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:30:53.570Z", + "rssBytes": 29016064, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:30:54.100Z", + "rssBytes": 29016064, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:30:54.633Z", + "rssBytes": 29016064, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:30:55.164Z", + "rssBytes": 29016064, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:30:55.740Z", + "rssBytes": 29016064, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:30:56.315Z", + "rssBytes": 29016064, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:30:56.847Z", + "rssBytes": 29016064, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:30:57.369Z", + "rssBytes": 29016064, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:30:57.899Z", + "rssBytes": 29016064, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:30:58.418Z", + "rssBytes": 29016064, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:30:58.956Z", + "rssBytes": 29016064, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:30:59.488Z", + "rssBytes": 29016064, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:31:00.016Z", + "rssBytes": 29016064, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:31:00.540Z", + "rssBytes": 29016064, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:31:01.104Z", + "rssBytes": 29016064, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:31:01.632Z", + "rssBytes": 29016064, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:31:02.166Z", + "rssBytes": 29016064, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:31:02.690Z", + "rssBytes": 29016064, + "cpuTimeMs": 78, + "processCount": 1 + } + ] + }, + "coordinator": { + "peakRssBytes": 107585536, + "cpuTimeMs": 765 + }, + "conformance": { + "connect": { + "passed": true + }, + "javascript": { + "passed": true + }, + "dom": { + "passed": true + }, + "forms": { + "passed": true, + "actionability": "dom-non-visual" + }, + "screenshot": { + "passed": true, + "supported": false, + "state": "unsupported", + "enforcement": "preflight" + }, + "teardown": { + "passed": true, + "rootProcessExited": true, + "transportDisposed": true + } + } + }, + { + "timings": { + "launchMs": 814.6594000000041, + "connectMs": 9.224400000006426, + "workloadMs": 81.35559999999532, + "shutdownMs": 800.9196999999986 + }, + "browserTree": { + "peakRssBytes": 29503488, + "cpuTimeMs": 141, + "processCount": 1, + "sampleCount": 24, + "observations": [ + { + "sampledAt": "2026-09-02T20:31:04.078Z", + "rssBytes": 16248832, + "cpuTimeMs": 31, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:31:04.643Z", + "rssBytes": 29503488, + "cpuTimeMs": 141, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:31:05.249Z", + "rssBytes": 29503488, + "cpuTimeMs": 141, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:31:05.874Z", + "rssBytes": 29503488, + "cpuTimeMs": 141, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:31:06.435Z", + "rssBytes": 29503488, + "cpuTimeMs": 141, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:31:06.967Z", + "rssBytes": 29503488, + "cpuTimeMs": 141, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:31:07.492Z", + "rssBytes": 29503488, + "cpuTimeMs": 141, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:31:08.060Z", + "rssBytes": 29503488, + "cpuTimeMs": 141, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:31:08.611Z", + "rssBytes": 29503488, + "cpuTimeMs": 141, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:31:09.142Z", + "rssBytes": 29503488, + "cpuTimeMs": 141, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:31:09.666Z", + "rssBytes": 29503488, + "cpuTimeMs": 141, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:31:10.186Z", + "rssBytes": 29503488, + "cpuTimeMs": 141, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:31:10.748Z", + "rssBytes": 29503488, + "cpuTimeMs": 141, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:31:11.315Z", + "rssBytes": 29503488, + "cpuTimeMs": 141, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:31:11.883Z", + "rssBytes": 29503488, + "cpuTimeMs": 141, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:31:12.439Z", + "rssBytes": 29503488, + "cpuTimeMs": 141, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:31:12.984Z", + "rssBytes": 29503488, + "cpuTimeMs": 141, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:31:13.574Z", + "rssBytes": 29503488, + "cpuTimeMs": 141, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:31:14.137Z", + "rssBytes": 29503488, + "cpuTimeMs": 141, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:31:14.680Z", + "rssBytes": 29503488, + "cpuTimeMs": 141, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:31:15.222Z", + "rssBytes": 29503488, + "cpuTimeMs": 141, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:31:15.775Z", + "rssBytes": 29503488, + "cpuTimeMs": 141, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:31:16.313Z", + "rssBytes": 29503488, + "cpuTimeMs": 141, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:31:16.890Z", + "rssBytes": 29503488, + "cpuTimeMs": 141, + "processCount": 1 + } + ] + }, + "coordinator": { + "peakRssBytes": 106131456, + "cpuTimeMs": 500 + }, + "conformance": { + "connect": { + "passed": true + }, + "javascript": { + "passed": true + }, + "dom": { + "passed": true + }, + "forms": { + "passed": true, + "actionability": "dom-non-visual" + }, + "screenshot": { + "passed": true, + "supported": false, + "state": "unsupported", + "enforcement": "preflight" + }, + "teardown": { + "passed": true, + "rootProcessExited": true, + "transportDisposed": true + } + } + }, + { + "timings": { + "launchMs": 826.0862999999954, + "connectMs": 9.60879999998724, + "workloadMs": 89.75909999999567, + "shutdownMs": 696.4921000000031 + }, + "browserTree": { + "peakRssBytes": 28930048, + "cpuTimeMs": 63, + "processCount": 1, + "sampleCount": 24, + "observations": [ + { + "sampledAt": "2026-09-02T20:31:18.428Z", + "rssBytes": 16244736, + "cpuTimeMs": 16, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:31:19.036Z", + "rssBytes": 28930048, + "cpuTimeMs": 63, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:31:19.642Z", + "rssBytes": 28930048, + "cpuTimeMs": 63, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:31:20.162Z", + "rssBytes": 28930048, + "cpuTimeMs": 63, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:31:20.693Z", + "rssBytes": 28930048, + "cpuTimeMs": 63, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:31:21.213Z", + "rssBytes": 28930048, + "cpuTimeMs": 63, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:31:21.749Z", + "rssBytes": 28930048, + "cpuTimeMs": 63, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:31:22.274Z", + "rssBytes": 28930048, + "cpuTimeMs": 63, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:31:22.811Z", + "rssBytes": 28930048, + "cpuTimeMs": 63, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:31:23.396Z", + "rssBytes": 28930048, + "cpuTimeMs": 63, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:31:23.996Z", + "rssBytes": 28930048, + "cpuTimeMs": 63, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:31:24.669Z", + "rssBytes": 28930048, + "cpuTimeMs": 63, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:31:25.238Z", + "rssBytes": 28930048, + "cpuTimeMs": 63, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:31:25.862Z", + "rssBytes": 28930048, + "cpuTimeMs": 63, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:31:26.466Z", + "rssBytes": 28930048, + "cpuTimeMs": 63, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:31:27.020Z", + "rssBytes": 28930048, + "cpuTimeMs": 63, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:31:27.580Z", + "rssBytes": 28930048, + "cpuTimeMs": 63, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:31:28.210Z", + "rssBytes": 28930048, + "cpuTimeMs": 63, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:31:28.788Z", + "rssBytes": 28930048, + "cpuTimeMs": 63, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:31:29.318Z", + "rssBytes": 28930048, + "cpuTimeMs": 63, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:31:29.858Z", + "rssBytes": 28930048, + "cpuTimeMs": 63, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:31:30.386Z", + "rssBytes": 28930048, + "cpuTimeMs": 63, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:31:30.925Z", + "rssBytes": 28930048, + "cpuTimeMs": 63, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:31:31.454Z", + "rssBytes": 28930048, + "cpuTimeMs": 63, + "processCount": 1 + } + ] + }, + "coordinator": { + "peakRssBytes": 112394240, + "cpuTimeMs": 640 + }, + "conformance": { + "connect": { + "passed": true + }, + "javascript": { + "passed": true + }, + "dom": { + "passed": true + }, + "forms": { + "passed": true, + "actionability": "dom-non-visual" + }, + "screenshot": { + "passed": true, + "supported": false, + "state": "unsupported", + "enforcement": "preflight" + }, + "teardown": { + "passed": true, + "rootProcessExited": true, + "transportDisposed": true + } + } + }, + { + "timings": { + "launchMs": 991.4586999999883, + "connectMs": 14.114000000001397, + "workloadMs": 119.59150000000955, + "shutdownMs": 689.0924999999988 + }, + "browserTree": { + "peakRssBytes": 29569024, + "cpuTimeMs": 156, + "processCount": 1, + "sampleCount": 23, + "observations": [ + { + "sampledAt": "2026-09-02T20:31:33.026Z", + "rssBytes": 16302080, + "cpuTimeMs": 47, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:31:33.804Z", + "rssBytes": 29569024, + "cpuTimeMs": 156, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:31:34.474Z", + "rssBytes": 29569024, + "cpuTimeMs": 156, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:31:35.016Z", + "rssBytes": 29569024, + "cpuTimeMs": 156, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:31:35.554Z", + "rssBytes": 29569024, + "cpuTimeMs": 156, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:31:36.078Z", + "rssBytes": 29569024, + "cpuTimeMs": 156, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:31:36.603Z", + "rssBytes": 29569024, + "cpuTimeMs": 156, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:31:37.134Z", + "rssBytes": 29569024, + "cpuTimeMs": 156, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:31:37.668Z", + "rssBytes": 29569024, + "cpuTimeMs": 156, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:31:38.201Z", + "rssBytes": 29569024, + "cpuTimeMs": 156, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:31:38.733Z", + "rssBytes": 29569024, + "cpuTimeMs": 156, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:31:39.365Z", + "rssBytes": 29569024, + "cpuTimeMs": 156, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:31:39.950Z", + "rssBytes": 29569024, + "cpuTimeMs": 156, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:31:40.504Z", + "rssBytes": 29569024, + "cpuTimeMs": 156, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:31:41.058Z", + "rssBytes": 29569024, + "cpuTimeMs": 156, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:31:41.688Z", + "rssBytes": 29569024, + "cpuTimeMs": 156, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:31:42.310Z", + "rssBytes": 29569024, + "cpuTimeMs": 156, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:31:42.880Z", + "rssBytes": 29569024, + "cpuTimeMs": 156, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:31:43.491Z", + "rssBytes": 29569024, + "cpuTimeMs": 156, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:31:44.060Z", + "rssBytes": 29569024, + "cpuTimeMs": 156, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:31:44.585Z", + "rssBytes": 29569024, + "cpuTimeMs": 156, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:31:45.119Z", + "rssBytes": 29569024, + "cpuTimeMs": 156, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:31:45.638Z", + "rssBytes": 29569024, + "cpuTimeMs": 156, + "processCount": 1 + } + ] + }, + "coordinator": { + "peakRssBytes": 116596736, + "cpuTimeMs": 689 + }, + "conformance": { + "connect": { + "passed": true + }, + "javascript": { + "passed": true + }, + "dom": { + "passed": true + }, + "forms": { + "passed": true, + "actionability": "dom-non-visual" + }, + "screenshot": { + "passed": true, + "supported": false, + "state": "unsupported", + "enforcement": "preflight" + }, + "teardown": { + "passed": true, + "rootProcessExited": true, + "transportDisposed": true + } + } + }, + { + "timings": { + "launchMs": 797.7700999999943, + "connectMs": 8.436499999981606, + "workloadMs": 80.60450000001583, + "shutdownMs": 1125.2972000000009 + }, + "browserTree": { + "peakRssBytes": 29347840, + "cpuTimeMs": 141, + "processCount": 1, + "sampleCount": 24, + "observations": [ + { + "sampledAt": "2026-09-02T20:31:47.023Z", + "rssBytes": 16138240, + "cpuTimeMs": 31, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:31:47.629Z", + "rssBytes": 29347840, + "cpuTimeMs": 141, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:31:48.229Z", + "rssBytes": 29347840, + "cpuTimeMs": 141, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:31:48.800Z", + "rssBytes": 29347840, + "cpuTimeMs": 141, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:31:49.392Z", + "rssBytes": 29347840, + "cpuTimeMs": 141, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:31:49.961Z", + "rssBytes": 29347840, + "cpuTimeMs": 141, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:31:50.582Z", + "rssBytes": 29347840, + "cpuTimeMs": 141, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:31:51.215Z", + "rssBytes": 29347840, + "cpuTimeMs": 141, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:31:51.785Z", + "rssBytes": 29347840, + "cpuTimeMs": 141, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:31:52.313Z", + "rssBytes": 29347840, + "cpuTimeMs": 141, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:31:52.846Z", + "rssBytes": 29347840, + "cpuTimeMs": 141, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:31:53.372Z", + "rssBytes": 29347840, + "cpuTimeMs": 141, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:31:53.904Z", + "rssBytes": 29347840, + "cpuTimeMs": 141, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:31:54.468Z", + "rssBytes": 29347840, + "cpuTimeMs": 141, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:31:55.042Z", + "rssBytes": 29347840, + "cpuTimeMs": 141, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:31:55.606Z", + "rssBytes": 29347840, + "cpuTimeMs": 141, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:31:56.169Z", + "rssBytes": 29347840, + "cpuTimeMs": 141, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:31:56.739Z", + "rssBytes": 29347840, + "cpuTimeMs": 141, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:31:57.313Z", + "rssBytes": 29347840, + "cpuTimeMs": 141, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:31:57.891Z", + "rssBytes": 29347840, + "cpuTimeMs": 141, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:31:58.499Z", + "rssBytes": 29347840, + "cpuTimeMs": 141, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:31:59.102Z", + "rssBytes": 29347840, + "cpuTimeMs": 141, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:31:59.701Z", + "rssBytes": 29347840, + "cpuTimeMs": 141, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:32:00.275Z", + "rssBytes": 29347840, + "cpuTimeMs": 141, + "processCount": 1 + } + ] + }, + "coordinator": { + "peakRssBytes": 109432832, + "cpuTimeMs": 608 + }, + "conformance": { + "connect": { + "passed": true + }, + "javascript": { + "passed": true + }, + "dom": { + "passed": true + }, + "forms": { + "passed": true, + "actionability": "dom-non-visual" + }, + "screenshot": { + "passed": true, + "supported": false, + "state": "unsupported", + "enforcement": "preflight" + }, + "teardown": { + "passed": true, + "rootProcessExited": true, + "transportDisposed": true + } + } + }, + { + "timings": { + "launchMs": 1143.2284999999974, + "connectMs": 10.723599999997532, + "workloadMs": 107.37309999999707, + "shutdownMs": 738.3056000000215 + }, + "browserTree": { + "peakRssBytes": 29351936, + "cpuTimeMs": 125, + "processCount": 1, + "sampleCount": 23, + "observations": [ + { + "sampledAt": "2026-09-02T20:32:02.447Z", + "rssBytes": 16211968, + "cpuTimeMs": 31, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:32:03.195Z", + "rssBytes": 29351936, + "cpuTimeMs": 125, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:32:03.940Z", + "rssBytes": 29351936, + "cpuTimeMs": 125, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:32:04.619Z", + "rssBytes": 29351936, + "cpuTimeMs": 125, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:32:05.281Z", + "rssBytes": 29351936, + "cpuTimeMs": 125, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:32:05.929Z", + "rssBytes": 29351936, + "cpuTimeMs": 125, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:32:06.678Z", + "rssBytes": 29351936, + "cpuTimeMs": 125, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:32:07.289Z", + "rssBytes": 29351936, + "cpuTimeMs": 125, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:32:07.927Z", + "rssBytes": 29351936, + "cpuTimeMs": 125, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:32:08.665Z", + "rssBytes": 29351936, + "cpuTimeMs": 125, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:32:09.327Z", + "rssBytes": 29351936, + "cpuTimeMs": 125, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:32:09.881Z", + "rssBytes": 29351936, + "cpuTimeMs": 125, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:32:10.485Z", + "rssBytes": 29351936, + "cpuTimeMs": 125, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:32:11.110Z", + "rssBytes": 29351936, + "cpuTimeMs": 125, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:32:11.693Z", + "rssBytes": 29351936, + "cpuTimeMs": 125, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:32:12.338Z", + "rssBytes": 29351936, + "cpuTimeMs": 125, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:32:13.088Z", + "rssBytes": 29351936, + "cpuTimeMs": 125, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:32:13.784Z", + "rssBytes": 29351936, + "cpuTimeMs": 125, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:32:14.369Z", + "rssBytes": 29351936, + "cpuTimeMs": 125, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:32:14.940Z", + "rssBytes": 29351936, + "cpuTimeMs": 125, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:32:15.688Z", + "rssBytes": 29351936, + "cpuTimeMs": 125, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:32:16.274Z", + "rssBytes": 29351936, + "cpuTimeMs": 125, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:32:16.854Z", + "rssBytes": 29351936, + "cpuTimeMs": 125, + "processCount": 1 + } + ] + }, + "coordinator": { + "peakRssBytes": 111144960, + "cpuTimeMs": 844 + }, + "conformance": { + "connect": { + "passed": true + }, + "javascript": { + "passed": true + }, + "dom": { + "passed": true + }, + "forms": { + "passed": true, + "actionability": "dom-non-visual" + }, + "screenshot": { + "passed": true, + "supported": false, + "state": "unsupported", + "enforcement": "preflight" + }, + "teardown": { + "passed": true, + "rootProcessExited": true, + "transportDisposed": true + } + } + }, + { + "timings": { + "launchMs": 882.3825999999826, + "connectMs": 17.389500000019325, + "workloadMs": 90.78750000000582, + "shutdownMs": 686.9629000000132 + }, + "browserTree": { + "peakRssBytes": 29073408, + "cpuTimeMs": 125, + "processCount": 1, + "sampleCount": 24, + "observations": [ + { + "sampledAt": "2026-09-02T20:32:18.336Z", + "rssBytes": 16330752, + "cpuTimeMs": 16, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:32:18.968Z", + "rssBytes": 29073408, + "cpuTimeMs": 125, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:32:19.603Z", + "rssBytes": 29073408, + "cpuTimeMs": 125, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:32:20.190Z", + "rssBytes": 29073408, + "cpuTimeMs": 125, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:32:20.797Z", + "rssBytes": 29073408, + "cpuTimeMs": 125, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:32:21.383Z", + "rssBytes": 29073408, + "cpuTimeMs": 125, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:32:22.006Z", + "rssBytes": 29073408, + "cpuTimeMs": 125, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:32:22.684Z", + "rssBytes": 29073408, + "cpuTimeMs": 125, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:32:23.319Z", + "rssBytes": 29073408, + "cpuTimeMs": 125, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:32:23.908Z", + "rssBytes": 29073408, + "cpuTimeMs": 125, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:32:24.463Z", + "rssBytes": 29073408, + "cpuTimeMs": 125, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:32:24.989Z", + "rssBytes": 29073408, + "cpuTimeMs": 125, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:32:25.520Z", + "rssBytes": 29073408, + "cpuTimeMs": 125, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:32:26.047Z", + "rssBytes": 29073408, + "cpuTimeMs": 125, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:32:26.653Z", + "rssBytes": 29073408, + "cpuTimeMs": 125, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:32:27.289Z", + "rssBytes": 29073408, + "cpuTimeMs": 125, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:32:27.855Z", + "rssBytes": 29073408, + "cpuTimeMs": 125, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:32:28.391Z", + "rssBytes": 29073408, + "cpuTimeMs": 125, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:32:28.912Z", + "rssBytes": 29073408, + "cpuTimeMs": 125, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:32:29.436Z", + "rssBytes": 29073408, + "cpuTimeMs": 125, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:32:29.959Z", + "rssBytes": 29073408, + "cpuTimeMs": 125, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:32:30.475Z", + "rssBytes": 29073408, + "cpuTimeMs": 125, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:32:31.004Z", + "rssBytes": 29073408, + "cpuTimeMs": 125, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:32:31.530Z", + "rssBytes": 29073408, + "cpuTimeMs": 125, + "processCount": 1 + } + ] + }, + "coordinator": { + "peakRssBytes": 114851840, + "cpuTimeMs": 704 + }, + "conformance": { + "connect": { + "passed": true + }, + "javascript": { + "passed": true + }, + "dom": { + "passed": true + }, + "forms": { + "passed": true, + "actionability": "dom-non-visual" + }, + "screenshot": { + "passed": true, + "supported": false, + "state": "unsupported", + "enforcement": "preflight" + }, + "teardown": { + "passed": true, + "rootProcessExited": true, + "transportDisposed": true + } + } + }, + { + "timings": { + "launchMs": 769.3974000000162, + "connectMs": 9.280100000003586, + "workloadMs": 74.14630000002217, + "shutdownMs": 691.3109000000113 + }, + "browserTree": { + "peakRssBytes": 28950528, + "cpuTimeMs": 125, + "processCount": 1, + "sampleCount": 24, + "observations": [ + { + "sampledAt": "2026-09-02T20:32:32.872Z", + "rssBytes": 16138240, + "cpuTimeMs": 31, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:32:33.447Z", + "rssBytes": 28950528, + "cpuTimeMs": 125, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:32:34.049Z", + "rssBytes": 28950528, + "cpuTimeMs": 125, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:32:34.613Z", + "rssBytes": 28950528, + "cpuTimeMs": 125, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:32:35.148Z", + "rssBytes": 28950528, + "cpuTimeMs": 125, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:32:35.673Z", + "rssBytes": 28950528, + "cpuTimeMs": 125, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:32:36.198Z", + "rssBytes": 28950528, + "cpuTimeMs": 125, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:32:36.751Z", + "rssBytes": 28950528, + "cpuTimeMs": 125, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:32:37.310Z", + "rssBytes": 28950528, + "cpuTimeMs": 125, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:32:37.883Z", + "rssBytes": 28950528, + "cpuTimeMs": 125, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:32:38.448Z", + "rssBytes": 28950528, + "cpuTimeMs": 125, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:32:38.999Z", + "rssBytes": 28950528, + "cpuTimeMs": 125, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:32:39.537Z", + "rssBytes": 28950528, + "cpuTimeMs": 125, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:32:40.100Z", + "rssBytes": 28950528, + "cpuTimeMs": 125, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:32:40.657Z", + "rssBytes": 28950528, + "cpuTimeMs": 125, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:32:41.228Z", + "rssBytes": 28950528, + "cpuTimeMs": 125, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:32:41.778Z", + "rssBytes": 28950528, + "cpuTimeMs": 125, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:32:42.361Z", + "rssBytes": 28950528, + "cpuTimeMs": 125, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:32:42.903Z", + "rssBytes": 28950528, + "cpuTimeMs": 125, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:32:43.476Z", + "rssBytes": 28950528, + "cpuTimeMs": 125, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:32:44.004Z", + "rssBytes": 28950528, + "cpuTimeMs": 125, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:32:44.538Z", + "rssBytes": 28950528, + "cpuTimeMs": 125, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:32:45.093Z", + "rssBytes": 28950528, + "cpuTimeMs": 125, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:32:45.622Z", + "rssBytes": 28950528, + "cpuTimeMs": 125, + "processCount": 1 + } + ] + }, + "coordinator": { + "peakRssBytes": 125972480, + "cpuTimeMs": 499 + }, + "conformance": { + "connect": { + "passed": true + }, + "javascript": { + "passed": true + }, + "dom": { + "passed": true + }, + "forms": { + "passed": true, + "actionability": "dom-non-visual" + }, + "screenshot": { + "passed": true, + "supported": false, + "state": "unsupported", + "enforcement": "preflight" + }, + "teardown": { + "passed": true, + "rootProcessExited": true, + "transportDisposed": true + } + } + }, + { + "timings": { + "launchMs": 800.8046000000031, + "connectMs": 10.361499999999069, + "workloadMs": 89.80340000000433, + "shutdownMs": 954.4192999999796 + }, + "browserTree": { + "peakRssBytes": 29483008, + "cpuTimeMs": 125, + "processCount": 1, + "sampleCount": 24, + "observations": [ + { + "sampledAt": "2026-09-02T20:32:47.009Z", + "rssBytes": 16257024, + "cpuTimeMs": 16, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:32:47.598Z", + "rssBytes": 29483008, + "cpuTimeMs": 125, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:32:48.167Z", + "rssBytes": 29483008, + "cpuTimeMs": 125, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:32:48.697Z", + "rssBytes": 29483008, + "cpuTimeMs": 125, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:32:49.229Z", + "rssBytes": 29483008, + "cpuTimeMs": 125, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:32:49.791Z", + "rssBytes": 29483008, + "cpuTimeMs": 125, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:32:50.346Z", + "rssBytes": 29483008, + "cpuTimeMs": 125, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:32:50.915Z", + "rssBytes": 29483008, + "cpuTimeMs": 125, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:32:51.461Z", + "rssBytes": 29483008, + "cpuTimeMs": 125, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:32:51.994Z", + "rssBytes": 29483008, + "cpuTimeMs": 125, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:32:52.515Z", + "rssBytes": 29483008, + "cpuTimeMs": 125, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:32:53.062Z", + "rssBytes": 29483008, + "cpuTimeMs": 125, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:32:53.585Z", + "rssBytes": 29483008, + "cpuTimeMs": 125, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:32:54.110Z", + "rssBytes": 29483008, + "cpuTimeMs": 125, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:32:54.625Z", + "rssBytes": 29483008, + "cpuTimeMs": 125, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:32:55.155Z", + "rssBytes": 29483008, + "cpuTimeMs": 125, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:32:55.674Z", + "rssBytes": 29483008, + "cpuTimeMs": 125, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:32:56.198Z", + "rssBytes": 29483008, + "cpuTimeMs": 125, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:32:56.710Z", + "rssBytes": 29483008, + "cpuTimeMs": 125, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:32:57.228Z", + "rssBytes": 29483008, + "cpuTimeMs": 125, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:32:57.744Z", + "rssBytes": 29483008, + "cpuTimeMs": 125, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:32:58.270Z", + "rssBytes": 29483008, + "cpuTimeMs": 125, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:32:59.048Z", + "rssBytes": 29483008, + "cpuTimeMs": 125, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:32:59.681Z", + "rssBytes": 29483008, + "cpuTimeMs": 125, + "processCount": 1 + } + ] + }, + "coordinator": { + "peakRssBytes": 113000448, + "cpuTimeMs": 751 + }, + "conformance": { + "connect": { + "passed": true + }, + "javascript": { + "passed": true + }, + "dom": { + "passed": true + }, + "forms": { + "passed": true, + "actionability": "dom-non-visual" + }, + "screenshot": { + "passed": true, + "supported": false, + "state": "unsupported", + "enforcement": "preflight" + }, + "teardown": { + "passed": true, + "rootProcessExited": true, + "transportDisposed": true + } + } + }, + { + "timings": { + "launchMs": 1023.2062000000151, + "connectMs": 10.580800000025192, + "workloadMs": 88.63479999999981, + "shutdownMs": 719.8534999999974 + }, + "browserTree": { + "peakRssBytes": 29040640, + "cpuTimeMs": 172, + "processCount": 1, + "sampleCount": 24, + "observations": [ + { + "sampledAt": "2026-09-02T20:33:01.568Z", + "rssBytes": 16220160, + "cpuTimeMs": 63, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:33:02.181Z", + "rssBytes": 29040640, + "cpuTimeMs": 172, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:33:02.745Z", + "rssBytes": 29040640, + "cpuTimeMs": 172, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:33:03.263Z", + "rssBytes": 29040640, + "cpuTimeMs": 172, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:33:03.784Z", + "rssBytes": 29040640, + "cpuTimeMs": 172, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:33:04.305Z", + "rssBytes": 29040640, + "cpuTimeMs": 172, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:33:04.837Z", + "rssBytes": 29040640, + "cpuTimeMs": 172, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:33:05.371Z", + "rssBytes": 29040640, + "cpuTimeMs": 172, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:33:05.944Z", + "rssBytes": 29040640, + "cpuTimeMs": 172, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:33:06.501Z", + "rssBytes": 29040640, + "cpuTimeMs": 172, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:33:07.108Z", + "rssBytes": 29040640, + "cpuTimeMs": 172, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:33:07.663Z", + "rssBytes": 29040640, + "cpuTimeMs": 172, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:33:08.199Z", + "rssBytes": 29040640, + "cpuTimeMs": 172, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:33:08.722Z", + "rssBytes": 29040640, + "cpuTimeMs": 172, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:33:09.241Z", + "rssBytes": 29040640, + "cpuTimeMs": 172, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:33:09.764Z", + "rssBytes": 29040640, + "cpuTimeMs": 172, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:33:10.301Z", + "rssBytes": 29040640, + "cpuTimeMs": 172, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:33:10.823Z", + "rssBytes": 29040640, + "cpuTimeMs": 172, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:33:11.345Z", + "rssBytes": 29040640, + "cpuTimeMs": 172, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:33:11.888Z", + "rssBytes": 29040640, + "cpuTimeMs": 172, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:33:12.452Z", + "rssBytes": 29040640, + "cpuTimeMs": 172, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:33:12.992Z", + "rssBytes": 29040640, + "cpuTimeMs": 172, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:33:13.510Z", + "rssBytes": 29040640, + "cpuTimeMs": 172, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:33:14.053Z", + "rssBytes": 29040640, + "cpuTimeMs": 172, + "processCount": 1 + } + ] + }, + "coordinator": { + "peakRssBytes": 126115840, + "cpuTimeMs": 656 + }, + "conformance": { + "connect": { + "passed": true + }, + "javascript": { + "passed": true + }, + "dom": { + "passed": true + }, + "forms": { + "passed": true, + "actionability": "dom-non-visual" + }, + "screenshot": { + "passed": true, + "supported": false, + "state": "unsupported", + "enforcement": "preflight" + }, + "teardown": { + "passed": true, + "rootProcessExited": true, + "transportDisposed": true + } + } + }, + { + "timings": { + "launchMs": 782.9523000000045, + "connectMs": 8.341899999999441, + "workloadMs": 78.06690000000526, + "shutdownMs": 698.678899999999 + }, + "browserTree": { + "peakRssBytes": 28893184, + "cpuTimeMs": 63, + "processCount": 1, + "sampleCount": 24, + "observations": [ + { + "sampledAt": "2026-09-02T20:33:15.449Z", + "rssBytes": 16257024, + "cpuTimeMs": 16, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:33:16.017Z", + "rssBytes": 28893184, + "cpuTimeMs": 63, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:33:16.566Z", + "rssBytes": 28893184, + "cpuTimeMs": 63, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:33:17.089Z", + "rssBytes": 28893184, + "cpuTimeMs": 63, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:33:17.607Z", + "rssBytes": 28893184, + "cpuTimeMs": 63, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:33:18.135Z", + "rssBytes": 28893184, + "cpuTimeMs": 63, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:33:18.655Z", + "rssBytes": 28893184, + "cpuTimeMs": 63, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:33:19.176Z", + "rssBytes": 28893184, + "cpuTimeMs": 63, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:33:19.691Z", + "rssBytes": 28893184, + "cpuTimeMs": 63, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:33:20.219Z", + "rssBytes": 28893184, + "cpuTimeMs": 63, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:33:20.746Z", + "rssBytes": 28893184, + "cpuTimeMs": 63, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:33:21.267Z", + "rssBytes": 28893184, + "cpuTimeMs": 63, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:33:21.798Z", + "rssBytes": 28893184, + "cpuTimeMs": 63, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:33:22.442Z", + "rssBytes": 28893184, + "cpuTimeMs": 63, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:33:23.059Z", + "rssBytes": 28893184, + "cpuTimeMs": 63, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:33:23.748Z", + "rssBytes": 28893184, + "cpuTimeMs": 63, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:33:24.439Z", + "rssBytes": 28893184, + "cpuTimeMs": 63, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:33:25.048Z", + "rssBytes": 28893184, + "cpuTimeMs": 63, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:33:25.673Z", + "rssBytes": 28893184, + "cpuTimeMs": 63, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:33:26.262Z", + "rssBytes": 28893184, + "cpuTimeMs": 63, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:33:26.813Z", + "rssBytes": 28893184, + "cpuTimeMs": 63, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:33:27.371Z", + "rssBytes": 28893184, + "cpuTimeMs": 63, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:33:27.911Z", + "rssBytes": 28893184, + "cpuTimeMs": 63, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:33:28.446Z", + "rssBytes": 28893184, + "cpuTimeMs": 63, + "processCount": 1 + } + ] + }, + "coordinator": { + "peakRssBytes": 126115840, + "cpuTimeMs": 515 + }, + "conformance": { + "connect": { + "passed": true + }, + "javascript": { + "passed": true + }, + "dom": { + "passed": true + }, + "forms": { + "passed": true, + "actionability": "dom-non-visual" + }, + "screenshot": { + "passed": true, + "supported": false, + "state": "unsupported", + "enforcement": "preflight" + }, + "teardown": { + "passed": true, + "rootProcessExited": true, + "transportDisposed": true + } + } + }, + { + "timings": { + "launchMs": 772.8347999999824, + "connectMs": 8.286100000026636, + "workloadMs": 74.74809999996796, + "shutdownMs": 724.0903999999864 + }, + "browserTree": { + "peakRssBytes": 29143040, + "cpuTimeMs": 78, + "processCount": 1, + "sampleCount": 24, + "observations": [ + { + "sampledAt": "2026-09-02T20:33:29.837Z", + "rssBytes": 16302080, + "cpuTimeMs": 16, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:33:30.409Z", + "rssBytes": 29143040, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:33:30.959Z", + "rssBytes": 29143040, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:33:31.484Z", + "rssBytes": 29143040, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:33:32.038Z", + "rssBytes": 29143040, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:33:32.560Z", + "rssBytes": 29143040, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:33:33.084Z", + "rssBytes": 29143040, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:33:33.608Z", + "rssBytes": 29143040, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:33:34.167Z", + "rssBytes": 29143040, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:33:34.724Z", + "rssBytes": 29143040, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:33:35.298Z", + "rssBytes": 29143040, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:33:35.912Z", + "rssBytes": 29143040, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:33:36.479Z", + "rssBytes": 29143040, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:33:37.074Z", + "rssBytes": 29143040, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:33:37.617Z", + "rssBytes": 29143040, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:33:38.137Z", + "rssBytes": 29143040, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:33:38.658Z", + "rssBytes": 29143040, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:33:39.187Z", + "rssBytes": 29143040, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:33:39.704Z", + "rssBytes": 29143040, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:33:40.225Z", + "rssBytes": 29143040, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:33:40.742Z", + "rssBytes": 29143040, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:33:41.264Z", + "rssBytes": 29143040, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:33:41.795Z", + "rssBytes": 29143040, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:33:42.327Z", + "rssBytes": 29143040, + "cpuTimeMs": 78, + "processCount": 1 + } + ] + }, + "coordinator": { + "peakRssBytes": 107204608, + "cpuTimeMs": 641 + }, + "conformance": { + "connect": { + "passed": true + }, + "javascript": { + "passed": true + }, + "dom": { + "passed": true + }, + "forms": { + "passed": true, + "actionability": "dom-non-visual" + }, + "screenshot": { + "passed": true, + "supported": false, + "state": "unsupported", + "enforcement": "preflight" + }, + "teardown": { + "passed": true, + "rootProcessExited": true, + "transportDisposed": true + } + } + }, + { + "timings": { + "launchMs": 828.9107999999542, + "connectMs": 10.805600000021514, + "workloadMs": 84.00669999996899, + "shutdownMs": 704.2668000000413 + }, + "browserTree": { + "peakRssBytes": 29028352, + "cpuTimeMs": 141, + "processCount": 1, + "sampleCount": 24, + "observations": [ + { + "sampledAt": "2026-09-02T20:33:43.748Z", + "rssBytes": 16199680, + "cpuTimeMs": 16, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:33:44.377Z", + "rssBytes": 29028352, + "cpuTimeMs": 141, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:33:45.036Z", + "rssBytes": 29028352, + "cpuTimeMs": 141, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:33:45.627Z", + "rssBytes": 29028352, + "cpuTimeMs": 141, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:33:46.189Z", + "rssBytes": 29028352, + "cpuTimeMs": 141, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:33:46.726Z", + "rssBytes": 29028352, + "cpuTimeMs": 141, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:33:47.244Z", + "rssBytes": 29028352, + "cpuTimeMs": 141, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:33:47.775Z", + "rssBytes": 29028352, + "cpuTimeMs": 141, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:33:48.296Z", + "rssBytes": 29028352, + "cpuTimeMs": 141, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:33:48.823Z", + "rssBytes": 29028352, + "cpuTimeMs": 141, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:33:49.350Z", + "rssBytes": 29028352, + "cpuTimeMs": 141, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:33:49.872Z", + "rssBytes": 29028352, + "cpuTimeMs": 141, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:33:50.398Z", + "rssBytes": 29028352, + "cpuTimeMs": 141, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:33:50.937Z", + "rssBytes": 29028352, + "cpuTimeMs": 141, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:33:51.492Z", + "rssBytes": 29028352, + "cpuTimeMs": 141, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:33:52.018Z", + "rssBytes": 29028352, + "cpuTimeMs": 141, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:33:52.545Z", + "rssBytes": 29028352, + "cpuTimeMs": 141, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:33:53.087Z", + "rssBytes": 29028352, + "cpuTimeMs": 141, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:33:53.612Z", + "rssBytes": 29028352, + "cpuTimeMs": 141, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:33:54.163Z", + "rssBytes": 29028352, + "cpuTimeMs": 141, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:33:54.697Z", + "rssBytes": 29028352, + "cpuTimeMs": 141, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:33:55.225Z", + "rssBytes": 29028352, + "cpuTimeMs": 141, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:33:55.766Z", + "rssBytes": 29028352, + "cpuTimeMs": 141, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:33:56.293Z", + "rssBytes": 29028352, + "cpuTimeMs": 141, + "processCount": 1 + } + ] + }, + "coordinator": { + "peakRssBytes": 128659456, + "cpuTimeMs": 453 + }, + "conformance": { + "connect": { + "passed": true + }, + "javascript": { + "passed": true + }, + "dom": { + "passed": true + }, + "forms": { + "passed": true, + "actionability": "dom-non-visual" + }, + "screenshot": { + "passed": true, + "supported": false, + "state": "unsupported", + "enforcement": "preflight" + }, + "teardown": { + "passed": true, + "rootProcessExited": true, + "transportDisposed": true + } + } + }, + { + "timings": { + "launchMs": 764.1333999999915, + "connectMs": 9.029199999989942, + "workloadMs": 76.3405999999959, + "shutdownMs": 670.5003999999608 + }, + "browserTree": { + "peakRssBytes": 29495296, + "cpuTimeMs": 78, + "processCount": 1, + "sampleCount": 24, + "observations": [ + { + "sampledAt": "2026-09-02T20:33:57.647Z", + "rssBytes": 16293888, + "cpuTimeMs": 31, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:33:58.222Z", + "rssBytes": 29495296, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:33:58.768Z", + "rssBytes": 29495296, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:33:59.291Z", + "rssBytes": 29495296, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:33:59.809Z", + "rssBytes": 29495296, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:34:00.339Z", + "rssBytes": 29495296, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:34:00.943Z", + "rssBytes": 29495296, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:34:01.514Z", + "rssBytes": 29495296, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:34:02.062Z", + "rssBytes": 29495296, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:34:02.596Z", + "rssBytes": 29495296, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:34:03.116Z", + "rssBytes": 29495296, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:34:03.632Z", + "rssBytes": 29495296, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:34:04.143Z", + "rssBytes": 29495296, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:34:04.663Z", + "rssBytes": 29495296, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:34:05.180Z", + "rssBytes": 29495296, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:34:05.690Z", + "rssBytes": 29495296, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:34:06.212Z", + "rssBytes": 29495296, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:34:06.734Z", + "rssBytes": 29495296, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:34:07.250Z", + "rssBytes": 29495296, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:34:07.759Z", + "rssBytes": 29495296, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:34:08.276Z", + "rssBytes": 29495296, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:34:08.789Z", + "rssBytes": 29495296, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:34:09.307Z", + "rssBytes": 29495296, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T20:34:09.824Z", + "rssBytes": 29495296, + "cpuTimeMs": 78, + "processCount": 1 + } + ] + }, + "coordinator": { + "peakRssBytes": 128659456, + "cpuTimeMs": 454 + }, + "conformance": { + "connect": { + "passed": true + }, + "javascript": { + "passed": true + }, + "dom": { + "passed": true + }, + "forms": { + "passed": true, + "actionability": "dom-non-visual" + }, + "screenshot": { + "passed": true, + "supported": false, + "state": "unsupported", + "enforcement": "preflight" + }, + "teardown": { + "passed": true, + "rootProcessExited": true, + "transportDisposed": true + } + } + } + ] +} diff --git a/docs/benchmarks/artifacts/obscura-0.2.1-constrained-non-visual-30mib-2026-09-03.json b/docs/benchmarks/artifacts/obscura-0.2.1-constrained-non-visual-30mib-2026-09-03.json new file mode 100644 index 0000000..e755675 --- /dev/null +++ b/docs/benchmarks/artifacts/obscura-0.2.1-constrained-non-visual-30mib-2026-09-03.json @@ -0,0 +1,3921 @@ +{ + "schemaVersion": "cockroach.lightweight-browser-benchmark.v2", + "generatedAt": "2026-09-02T19:53:16.262Z", + "implementation": "obscura", + "resourceProfile": "constrained", + "renderingPolicy": "visual-actions-denied", + "executable": { + "name": "obscura.exe", + "bytes": 58097152, + "sha256": "5b609fb46bc00da79e450fb0fbd34bd442e565b1394f4af95433e0b341078221", + "version": "obscura 0.2.1" + }, + "sourceIdentity": { + "gitCommit": "deb6c25b4c0bc1927a631e3b062464b4f4cc2775", + "workingTreeDirty": true, + "sourceScope": "Runtime, schemas, benchmark/package/build configuration, and tests; generated documentation and benchmark artifacts are excluded.", + "identityNormalization": "Relative paths use forward slashes and text line endings are normalized to LF before hashing.", + "sourceFileCount": 100, + "sourceTreeSha256": "7cd941b6dc2f12139627ff4e2b5ea15acd9f22368d203c87dd5170765829abf7", + "runtimeBuildFileCount": 200, + "runtimeBuildSha256": "730a61e4c1fc8c20fcb6906d8d84af2b0e2f544b35a8da9a2a1eee6a37ed22fd", + "benchmarkScriptSha256": "08a5294f2d446765f712b93c9bfaaca010b1d043ded638d1f4f57b5038c97e86" + }, + "machine": { + "platform": "win32", + "architecture": "x64", + "release": "10.0.26200", + "logicalCpuCount": 16, + "cpuModel": "AMD Ryzen 7 4800H with Radeon Graphics ", + "totalMemoryBytes": 16557887488, + "freeMemoryBytesAtReport": 5978824704, + "node": "v24.15.0" + }, + "methodology": { + "warmup": 1, + "iterations": 20, + "settleMs": 500, + "startupTimeoutMs": 15000, + "actionTimeoutMs": 10000, + "teardownTimeoutMs": 5000, + "resourceSamples": 10, + "resourceSampleIntervalMs": 25, + "targetMiB": 30, + "targetDefinition": "Every measured owned-browser process-tree RSS sample is at or below the target and every required CDP/JavaScript/DOM/form/screenshot-preflight/teardown check passes.", + "memoryDefinition": "Aggregate RSS for the complete owned browser process tree; the Node coordinator is disclosed separately and is not hidden in browser RSS.", + "renderingControl": "Cockroach Browser declares a no-visual-action policy and rejects capture at preflight. This does not assert that the selected engine binary exposes or received a renderer-disable launch switch.", + "workload": "One runtime-owned loopback CDP server, one non-visual data-URL document, JavaScript evaluation, DOM query, input, and click.", + "requiredChecks": [ + "connect", + "javascript", + "dom", + "forms", + "screenshot", + "teardown" + ], + "optionalChecks": [], + "energy": { + "available": false, + "reason": "No calibrated platform energy counter is available; CPU time is not watts." + } + }, + "verdict": { + "passed": true, + "everyRunWithinTarget": true, + "requiredConformancePassed": true, + "targetBytes": 31457280, + "maxObservedBrowserTreeRssBytes": 29716480 + }, + "summary": { + "peakBrowserTreeRssBytes": { + "median": 29372416, + "p95": 29659136, + "min": 28827648, + "max": 29716480 + }, + "browserCpuTimeMs": { + "median": 94, + "p95": 125, + "min": 47, + "max": 234 + }, + "processCount": { + "median": 1, + "p95": 1, + "min": 1, + "max": 1 + }, + "launchMs": { + "median": 788.5651000000071, + "p95": 806.8325000000004, + "min": 739.6468999999997, + "max": 820.8680999999997 + }, + "connectMs": { + "median": 8.806599999996251, + "p95": 10.222100000013597, + "min": 7.139100000000326, + "max": 17.367499999998472 + }, + "workloadMs": { + "median": 82.20029999999679, + "p95": 89.89480000000003, + "min": 71.29990000001271, + "max": 90.94499999997788 + }, + "shutdownMs": { + "median": 689.5881999999983, + "p95": 735.2600999999995, + "min": 677.3313000000053, + "max": 736.9794000000111 + }, + "coordinatorPeakRssBytes": { + "median": 110706688, + "p95": 129122304, + "min": 103612416, + "max": 129667072 + } + }, + "samples": [ + { + "timings": { + "launchMs": 801.3378000000012, + "connectMs": 17.367499999998472, + "workloadMs": 89.89480000000003, + "shutdownMs": 691.2321999999986 + }, + "browserTree": { + "peakRssBytes": 28938240, + "cpuTimeMs": 78, + "processCount": 1, + "sampleCount": 24, + "observations": [ + { + "sampledAt": "2026-09-02T19:48:45.088Z", + "rssBytes": 16199680, + "cpuTimeMs": 16, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:48:45.643Z", + "rssBytes": 28938240, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:48:46.201Z", + "rssBytes": 28938240, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:48:46.721Z", + "rssBytes": 28938240, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:48:47.244Z", + "rssBytes": 28938240, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:48:47.773Z", + "rssBytes": 28938240, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:48:48.292Z", + "rssBytes": 28938240, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:48:48.813Z", + "rssBytes": 28938240, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:48:49.333Z", + "rssBytes": 28938240, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:48:49.860Z", + "rssBytes": 28938240, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:48:50.380Z", + "rssBytes": 28938240, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:48:50.908Z", + "rssBytes": 28938240, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:48:51.423Z", + "rssBytes": 28938240, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:48:51.952Z", + "rssBytes": 28938240, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:48:52.471Z", + "rssBytes": 28938240, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:48:52.995Z", + "rssBytes": 28938240, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:48:53.520Z", + "rssBytes": 28938240, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:48:54.052Z", + "rssBytes": 28938240, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:48:54.593Z", + "rssBytes": 28938240, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:48:55.111Z", + "rssBytes": 28938240, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:48:55.638Z", + "rssBytes": 28938240, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:48:56.160Z", + "rssBytes": 28938240, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:48:56.683Z", + "rssBytes": 28938240, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:48:57.208Z", + "rssBytes": 28938240, + "cpuTimeMs": 78, + "processCount": 1 + } + ] + }, + "coordinator": { + "peakRssBytes": 103612416, + "cpuTimeMs": 782 + }, + "conformance": { + "connect": { + "passed": true + }, + "javascript": { + "passed": true + }, + "dom": { + "passed": true + }, + "forms": { + "passed": true, + "actionability": "dom-non-visual" + }, + "screenshot": { + "passed": true, + "supported": false, + "state": "unsupported", + "enforcement": "preflight" + }, + "teardown": { + "passed": true, + "rootProcessExited": true, + "transportDisposed": true + } + } + }, + { + "timings": { + "launchMs": 806.8325000000004, + "connectMs": 8.425500000001193, + "workloadMs": 87.21679999999833, + "shutdownMs": 735.2600999999995 + }, + "browserTree": { + "peakRssBytes": 29659136, + "cpuTimeMs": 109, + "processCount": 1, + "sampleCount": 24, + "observations": [ + { + "sampledAt": "2026-09-02T19:48:58.616Z", + "rssBytes": 16297984, + "cpuTimeMs": 31, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:48:59.195Z", + "rssBytes": 29659136, + "cpuTimeMs": 109, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:48:59.743Z", + "rssBytes": 29659136, + "cpuTimeMs": 109, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:49:00.278Z", + "rssBytes": 29659136, + "cpuTimeMs": 109, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:49:00.832Z", + "rssBytes": 29659136, + "cpuTimeMs": 109, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:49:01.355Z", + "rssBytes": 29659136, + "cpuTimeMs": 109, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:49:01.881Z", + "rssBytes": 29659136, + "cpuTimeMs": 109, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:49:02.398Z", + "rssBytes": 29659136, + "cpuTimeMs": 109, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:49:02.934Z", + "rssBytes": 29659136, + "cpuTimeMs": 109, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:49:03.455Z", + "rssBytes": 29659136, + "cpuTimeMs": 109, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:49:03.983Z", + "rssBytes": 29659136, + "cpuTimeMs": 109, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:49:04.515Z", + "rssBytes": 29659136, + "cpuTimeMs": 109, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:49:05.035Z", + "rssBytes": 29659136, + "cpuTimeMs": 109, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:49:05.561Z", + "rssBytes": 29659136, + "cpuTimeMs": 109, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:49:06.088Z", + "rssBytes": 29659136, + "cpuTimeMs": 109, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:49:06.607Z", + "rssBytes": 29659136, + "cpuTimeMs": 109, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:49:07.127Z", + "rssBytes": 29659136, + "cpuTimeMs": 109, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:49:07.644Z", + "rssBytes": 29659136, + "cpuTimeMs": 109, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:49:08.164Z", + "rssBytes": 29659136, + "cpuTimeMs": 109, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:49:08.686Z", + "rssBytes": 29659136, + "cpuTimeMs": 109, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:49:09.212Z", + "rssBytes": 29659136, + "cpuTimeMs": 109, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:49:09.750Z", + "rssBytes": 29659136, + "cpuTimeMs": 109, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:49:10.280Z", + "rssBytes": 29659136, + "cpuTimeMs": 109, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:49:10.804Z", + "rssBytes": 29659136, + "cpuTimeMs": 109, + "processCount": 1 + } + ] + }, + "coordinator": { + "peakRssBytes": 104787968, + "cpuTimeMs": 687 + }, + "conformance": { + "connect": { + "passed": true + }, + "javascript": { + "passed": true + }, + "dom": { + "passed": true + }, + "forms": { + "passed": true, + "actionability": "dom-non-visual" + }, + "screenshot": { + "passed": true, + "supported": false, + "state": "unsupported", + "enforcement": "preflight" + }, + "teardown": { + "passed": true, + "rootProcessExited": true, + "transportDisposed": true + } + } + }, + { + "timings": { + "launchMs": 820.8680999999997, + "connectMs": 9.203999999997905, + "workloadMs": 83.14110000000073, + "shutdownMs": 689.6494000000021 + }, + "browserTree": { + "peakRssBytes": 29716480, + "cpuTimeMs": 94, + "processCount": 1, + "sampleCount": 24, + "observations": [ + { + "sampledAt": "2026-09-02T19:49:12.263Z", + "rssBytes": 16367616, + "cpuTimeMs": 31, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:49:12.830Z", + "rssBytes": 29716480, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:49:13.385Z", + "rssBytes": 29716480, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:49:13.966Z", + "rssBytes": 29716480, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:49:14.497Z", + "rssBytes": 29716480, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:49:15.020Z", + "rssBytes": 29716480, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:49:15.536Z", + "rssBytes": 29716480, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:49:16.057Z", + "rssBytes": 29716480, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:49:16.581Z", + "rssBytes": 29716480, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:49:17.100Z", + "rssBytes": 29716480, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:49:17.630Z", + "rssBytes": 29716480, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:49:18.155Z", + "rssBytes": 29716480, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:49:18.712Z", + "rssBytes": 29716480, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:49:19.247Z", + "rssBytes": 29716480, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:49:19.788Z", + "rssBytes": 29716480, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:49:20.333Z", + "rssBytes": 29716480, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:49:20.860Z", + "rssBytes": 29716480, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:49:21.400Z", + "rssBytes": 29716480, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:49:21.950Z", + "rssBytes": 29716480, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:49:22.473Z", + "rssBytes": 29716480, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:49:23.009Z", + "rssBytes": 29716480, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:49:23.572Z", + "rssBytes": 29716480, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:49:24.092Z", + "rssBytes": 29716480, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:49:24.614Z", + "rssBytes": 29716480, + "cpuTimeMs": 94, + "processCount": 1 + } + ] + }, + "coordinator": { + "peakRssBytes": 103686144, + "cpuTimeMs": 797 + }, + "conformance": { + "connect": { + "passed": true + }, + "javascript": { + "passed": true + }, + "dom": { + "passed": true + }, + "forms": { + "passed": true, + "actionability": "dom-non-visual" + }, + "screenshot": { + "passed": true, + "supported": false, + "state": "unsupported", + "enforcement": "preflight" + }, + "teardown": { + "passed": true, + "rootProcessExited": true, + "transportDisposed": true + } + } + }, + { + "timings": { + "launchMs": 739.6468999999997, + "connectMs": 7.856599999999162, + "workloadMs": 83.64160000000265, + "shutdownMs": 686.5280999999959 + }, + "browserTree": { + "peakRssBytes": 28827648, + "cpuTimeMs": 109, + "processCount": 1, + "sampleCount": 24, + "observations": [ + { + "sampledAt": "2026-09-02T19:49:25.987Z", + "rssBytes": 16175104, + "cpuTimeMs": 16, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:49:26.545Z", + "rssBytes": 28827648, + "cpuTimeMs": 109, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:49:27.100Z", + "rssBytes": 28827648, + "cpuTimeMs": 109, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:49:27.617Z", + "rssBytes": 28827648, + "cpuTimeMs": 109, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:49:28.148Z", + "rssBytes": 28827648, + "cpuTimeMs": 109, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:49:28.672Z", + "rssBytes": 28827648, + "cpuTimeMs": 109, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:49:29.197Z", + "rssBytes": 28827648, + "cpuTimeMs": 109, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:49:29.716Z", + "rssBytes": 28827648, + "cpuTimeMs": 109, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:49:30.241Z", + "rssBytes": 28827648, + "cpuTimeMs": 109, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:49:30.769Z", + "rssBytes": 28827648, + "cpuTimeMs": 109, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:49:31.291Z", + "rssBytes": 28827648, + "cpuTimeMs": 109, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:49:31.819Z", + "rssBytes": 28827648, + "cpuTimeMs": 109, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:49:32.339Z", + "rssBytes": 28827648, + "cpuTimeMs": 109, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:49:32.861Z", + "rssBytes": 28827648, + "cpuTimeMs": 109, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:49:33.383Z", + "rssBytes": 28827648, + "cpuTimeMs": 109, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:49:33.913Z", + "rssBytes": 28827648, + "cpuTimeMs": 109, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:49:34.437Z", + "rssBytes": 28827648, + "cpuTimeMs": 109, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:49:34.965Z", + "rssBytes": 28827648, + "cpuTimeMs": 109, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:49:35.492Z", + "rssBytes": 28827648, + "cpuTimeMs": 109, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:49:36.029Z", + "rssBytes": 28827648, + "cpuTimeMs": 109, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:49:36.560Z", + "rssBytes": 28827648, + "cpuTimeMs": 109, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:49:37.085Z", + "rssBytes": 28827648, + "cpuTimeMs": 109, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:49:37.603Z", + "rssBytes": 28827648, + "cpuTimeMs": 109, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:49:38.120Z", + "rssBytes": 28827648, + "cpuTimeMs": 109, + "processCount": 1 + } + ] + }, + "coordinator": { + "peakRssBytes": 108089344, + "cpuTimeMs": 718 + }, + "conformance": { + "connect": { + "passed": true + }, + "javascript": { + "passed": true + }, + "dom": { + "passed": true + }, + "forms": { + "passed": true, + "actionability": "dom-non-visual" + }, + "screenshot": { + "passed": true, + "supported": false, + "state": "unsupported", + "enforcement": "preflight" + }, + "teardown": { + "passed": true, + "rootProcessExited": true, + "transportDisposed": true + } + } + }, + { + "timings": { + "launchMs": 794.8464999999997, + "connectMs": 9.459699999992154, + "workloadMs": 83.07749999999942, + "shutdownMs": 683.8094999999885 + }, + "browserTree": { + "peakRssBytes": 29478912, + "cpuTimeMs": 78, + "processCount": 1, + "sampleCount": 24, + "observations": [ + { + "sampledAt": "2026-09-02T19:49:39.500Z", + "rssBytes": 16220160, + "cpuTimeMs": 31, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:49:40.060Z", + "rssBytes": 29478912, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:49:40.614Z", + "rssBytes": 29478912, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:49:41.142Z", + "rssBytes": 29478912, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:49:41.674Z", + "rssBytes": 29478912, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:49:42.196Z", + "rssBytes": 29478912, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:49:42.758Z", + "rssBytes": 29478912, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:49:43.301Z", + "rssBytes": 29478912, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:49:43.850Z", + "rssBytes": 29478912, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:49:44.382Z", + "rssBytes": 29478912, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:49:44.909Z", + "rssBytes": 29478912, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:49:45.433Z", + "rssBytes": 29478912, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:49:45.955Z", + "rssBytes": 29478912, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:49:46.491Z", + "rssBytes": 29478912, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:49:47.019Z", + "rssBytes": 29478912, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:49:47.540Z", + "rssBytes": 29478912, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:49:48.069Z", + "rssBytes": 29478912, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:49:48.593Z", + "rssBytes": 29478912, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:49:49.115Z", + "rssBytes": 29478912, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:49:49.635Z", + "rssBytes": 29478912, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:49:50.162Z", + "rssBytes": 29478912, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:49:50.673Z", + "rssBytes": 29478912, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:49:51.195Z", + "rssBytes": 29478912, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:49:51.712Z", + "rssBytes": 29478912, + "cpuTimeMs": 78, + "processCount": 1 + } + ] + }, + "coordinator": { + "peakRssBytes": 110477312, + "cpuTimeMs": 689 + }, + "conformance": { + "connect": { + "passed": true + }, + "javascript": { + "passed": true + }, + "dom": { + "passed": true + }, + "forms": { + "passed": true, + "actionability": "dom-non-visual" + }, + "screenshot": { + "passed": true, + "supported": false, + "state": "unsupported", + "enforcement": "preflight" + }, + "teardown": { + "passed": true, + "rootProcessExited": true, + "transportDisposed": true + } + } + }, + { + "timings": { + "launchMs": 781.7139999999927, + "connectMs": 9.069799999997485, + "workloadMs": 77.783500000005, + "shutdownMs": 687.6994999999879 + }, + "browserTree": { + "peakRssBytes": 28831744, + "cpuTimeMs": 125, + "processCount": 1, + "sampleCount": 24, + "observations": [ + { + "sampledAt": "2026-09-02T19:49:53.065Z", + "rssBytes": 16203776, + "cpuTimeMs": 47, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:49:53.633Z", + "rssBytes": 28831744, + "cpuTimeMs": 125, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:49:54.196Z", + "rssBytes": 28831744, + "cpuTimeMs": 125, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:49:54.724Z", + "rssBytes": 28831744, + "cpuTimeMs": 125, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:49:55.245Z", + "rssBytes": 28831744, + "cpuTimeMs": 125, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:49:55.767Z", + "rssBytes": 28831744, + "cpuTimeMs": 125, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:49:56.292Z", + "rssBytes": 28831744, + "cpuTimeMs": 125, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:49:56.821Z", + "rssBytes": 28831744, + "cpuTimeMs": 125, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:49:57.342Z", + "rssBytes": 28831744, + "cpuTimeMs": 125, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:49:57.877Z", + "rssBytes": 28831744, + "cpuTimeMs": 125, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:49:58.406Z", + "rssBytes": 28831744, + "cpuTimeMs": 125, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:49:58.927Z", + "rssBytes": 28831744, + "cpuTimeMs": 125, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:49:59.452Z", + "rssBytes": 28831744, + "cpuTimeMs": 125, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:49:59.971Z", + "rssBytes": 28831744, + "cpuTimeMs": 125, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:50:00.555Z", + "rssBytes": 28831744, + "cpuTimeMs": 125, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:50:01.083Z", + "rssBytes": 28831744, + "cpuTimeMs": 125, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:50:01.604Z", + "rssBytes": 28831744, + "cpuTimeMs": 125, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:50:02.125Z", + "rssBytes": 28831744, + "cpuTimeMs": 125, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:50:02.646Z", + "rssBytes": 28831744, + "cpuTimeMs": 125, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:50:03.164Z", + "rssBytes": 28831744, + "cpuTimeMs": 125, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:50:03.691Z", + "rssBytes": 28831744, + "cpuTimeMs": 125, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:50:04.209Z", + "rssBytes": 28831744, + "cpuTimeMs": 125, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:50:04.734Z", + "rssBytes": 28831744, + "cpuTimeMs": 125, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:50:05.257Z", + "rssBytes": 28831744, + "cpuTimeMs": 125, + "processCount": 1 + } + ] + }, + "coordinator": { + "peakRssBytes": 113037312, + "cpuTimeMs": 546 + }, + "conformance": { + "connect": { + "passed": true + }, + "javascript": { + "passed": true + }, + "dom": { + "passed": true + }, + "forms": { + "passed": true, + "actionability": "dom-non-visual" + }, + "screenshot": { + "passed": true, + "supported": false, + "state": "unsupported", + "enforcement": "preflight" + }, + "teardown": { + "passed": true, + "rootProcessExited": true, + "transportDisposed": true + } + } + }, + { + "timings": { + "launchMs": 779.8451999999961, + "connectMs": 9.971900000004098, + "workloadMs": 76.47669999999925, + "shutdownMs": 687.8106000000116 + }, + "browserTree": { + "peakRssBytes": 29380608, + "cpuTimeMs": 78, + "processCount": 1, + "sampleCount": 24, + "observations": [ + { + "sampledAt": "2026-09-02T19:50:06.621Z", + "rssBytes": 16228352, + "cpuTimeMs": 16, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:50:07.180Z", + "rssBytes": 29380608, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:50:07.728Z", + "rssBytes": 29380608, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:50:08.246Z", + "rssBytes": 29380608, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:50:08.768Z", + "rssBytes": 29380608, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:50:09.301Z", + "rssBytes": 29380608, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:50:09.834Z", + "rssBytes": 29380608, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:50:10.358Z", + "rssBytes": 29380608, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:50:10.875Z", + "rssBytes": 29380608, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:50:11.399Z", + "rssBytes": 29380608, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:50:11.922Z", + "rssBytes": 29380608, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:50:12.440Z", + "rssBytes": 29380608, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:50:12.960Z", + "rssBytes": 29380608, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:50:13.484Z", + "rssBytes": 29380608, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:50:14.018Z", + "rssBytes": 29380608, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:50:14.538Z", + "rssBytes": 29380608, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:50:15.054Z", + "rssBytes": 29380608, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:50:15.578Z", + "rssBytes": 29380608, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:50:16.100Z", + "rssBytes": 29380608, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:50:16.626Z", + "rssBytes": 29380608, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:50:17.142Z", + "rssBytes": 29380608, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:50:17.665Z", + "rssBytes": 29380608, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:50:18.184Z", + "rssBytes": 29380608, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:50:18.703Z", + "rssBytes": 29380608, + "cpuTimeMs": 78, + "processCount": 1 + } + ] + }, + "coordinator": { + "peakRssBytes": 108814336, + "cpuTimeMs": 625 + }, + "conformance": { + "connect": { + "passed": true + }, + "javascript": { + "passed": true + }, + "dom": { + "passed": true + }, + "forms": { + "passed": true, + "actionability": "dom-non-visual" + }, + "screenshot": { + "passed": true, + "supported": false, + "state": "unsupported", + "enforcement": "preflight" + }, + "teardown": { + "passed": true, + "rootProcessExited": true, + "transportDisposed": true + } + } + }, + { + "timings": { + "launchMs": 790.1956999999966, + "connectMs": 9.737900000007357, + "workloadMs": 81.9258000000118, + "shutdownMs": 677.3313000000053 + }, + "browserTree": { + "peakRssBytes": 29327360, + "cpuTimeMs": 78, + "processCount": 1, + "sampleCount": 24, + "observations": [ + { + "sampledAt": "2026-09-02T19:50:20.065Z", + "rssBytes": 16216064, + "cpuTimeMs": 16, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:50:20.642Z", + "rssBytes": 29327360, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:50:21.215Z", + "rssBytes": 29327360, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:50:21.742Z", + "rssBytes": 29327360, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:50:22.268Z", + "rssBytes": 29327360, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:50:22.788Z", + "rssBytes": 29327360, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:50:23.308Z", + "rssBytes": 29327360, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:50:23.828Z", + "rssBytes": 29327360, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:50:24.357Z", + "rssBytes": 29327360, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:50:24.933Z", + "rssBytes": 29327360, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:50:25.468Z", + "rssBytes": 29327360, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:50:25.993Z", + "rssBytes": 29327360, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:50:26.534Z", + "rssBytes": 29327360, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:50:27.064Z", + "rssBytes": 29327360, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:50:27.591Z", + "rssBytes": 29327360, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:50:28.140Z", + "rssBytes": 29327360, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:50:28.683Z", + "rssBytes": 29327360, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:50:29.214Z", + "rssBytes": 29327360, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:50:29.748Z", + "rssBytes": 29327360, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:50:30.278Z", + "rssBytes": 29327360, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:50:30.830Z", + "rssBytes": 29327360, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:50:31.347Z", + "rssBytes": 29327360, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:50:31.872Z", + "rssBytes": 29327360, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:50:32.397Z", + "rssBytes": 29327360, + "cpuTimeMs": 78, + "processCount": 1 + } + ] + }, + "coordinator": { + "peakRssBytes": 107397120, + "cpuTimeMs": 501 + }, + "conformance": { + "connect": { + "passed": true + }, + "javascript": { + "passed": true + }, + "dom": { + "passed": true + }, + "forms": { + "passed": true, + "actionability": "dom-non-visual" + }, + "screenshot": { + "passed": true, + "supported": false, + "state": "unsupported", + "enforcement": "preflight" + }, + "teardown": { + "passed": true, + "rootProcessExited": true, + "transportDisposed": true + } + } + }, + { + "timings": { + "launchMs": 792.812699999995, + "connectMs": 8.806599999996251, + "workloadMs": 83.3008000000118, + "shutdownMs": 689.1183000000019 + }, + "browserTree": { + "peakRssBytes": 29642752, + "cpuTimeMs": 109, + "processCount": 1, + "sampleCount": 24, + "observations": [ + { + "sampledAt": "2026-09-02T19:50:33.775Z", + "rssBytes": 16314368, + "cpuTimeMs": 47, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:50:34.337Z", + "rssBytes": 29642752, + "cpuTimeMs": 109, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:50:34.904Z", + "rssBytes": 29642752, + "cpuTimeMs": 109, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:50:35.459Z", + "rssBytes": 29642752, + "cpuTimeMs": 109, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:50:36.007Z", + "rssBytes": 29642752, + "cpuTimeMs": 109, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:50:36.523Z", + "rssBytes": 29642752, + "cpuTimeMs": 109, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:50:37.050Z", + "rssBytes": 29642752, + "cpuTimeMs": 109, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:50:37.570Z", + "rssBytes": 29642752, + "cpuTimeMs": 109, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:50:38.097Z", + "rssBytes": 29642752, + "cpuTimeMs": 109, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:50:38.617Z", + "rssBytes": 29642752, + "cpuTimeMs": 109, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:50:39.138Z", + "rssBytes": 29642752, + "cpuTimeMs": 109, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:50:39.655Z", + "rssBytes": 29642752, + "cpuTimeMs": 109, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:50:40.191Z", + "rssBytes": 29642752, + "cpuTimeMs": 109, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:50:40.716Z", + "rssBytes": 29642752, + "cpuTimeMs": 109, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:50:41.244Z", + "rssBytes": 29642752, + "cpuTimeMs": 109, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:50:41.771Z", + "rssBytes": 29642752, + "cpuTimeMs": 109, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:50:42.299Z", + "rssBytes": 29642752, + "cpuTimeMs": 109, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:50:42.868Z", + "rssBytes": 29642752, + "cpuTimeMs": 109, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:50:43.399Z", + "rssBytes": 29642752, + "cpuTimeMs": 109, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:50:43.929Z", + "rssBytes": 29642752, + "cpuTimeMs": 109, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:50:44.449Z", + "rssBytes": 29642752, + "cpuTimeMs": 109, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:50:44.973Z", + "rssBytes": 29642752, + "cpuTimeMs": 109, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:50:45.504Z", + "rssBytes": 29642752, + "cpuTimeMs": 109, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:50:46.021Z", + "rssBytes": 29642752, + "cpuTimeMs": 109, + "processCount": 1 + } + ] + }, + "coordinator": { + "peakRssBytes": 111230976, + "cpuTimeMs": 702 + }, + "conformance": { + "connect": { + "passed": true + }, + "javascript": { + "passed": true + }, + "dom": { + "passed": true + }, + "forms": { + "passed": true, + "actionability": "dom-non-visual" + }, + "screenshot": { + "passed": true, + "supported": false, + "state": "unsupported", + "enforcement": "preflight" + }, + "teardown": { + "passed": true, + "rootProcessExited": true, + "transportDisposed": true + } + } + }, + { + "timings": { + "launchMs": 787.8942000000097, + "connectMs": 7.57750000001397, + "workloadMs": 75.75169999999343, + "shutdownMs": 717.8287000000128 + }, + "browserTree": { + "peakRssBytes": 29421568, + "cpuTimeMs": 78, + "processCount": 1, + "sampleCount": 24, + "observations": [ + { + "sampledAt": "2026-09-02T19:50:47.407Z", + "rssBytes": 16158720, + "cpuTimeMs": 16, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:50:47.963Z", + "rssBytes": 29421568, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:50:48.514Z", + "rssBytes": 29421568, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:50:49.032Z", + "rssBytes": 29421568, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:50:49.651Z", + "rssBytes": 29421568, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:50:50.188Z", + "rssBytes": 29421568, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:50:50.719Z", + "rssBytes": 29421568, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:50:51.247Z", + "rssBytes": 29421568, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:50:51.780Z", + "rssBytes": 29421568, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:50:52.344Z", + "rssBytes": 29421568, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:50:52.865Z", + "rssBytes": 29421568, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:50:53.411Z", + "rssBytes": 29421568, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:50:53.941Z", + "rssBytes": 29421568, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:50:54.450Z", + "rssBytes": 29421568, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:50:54.975Z", + "rssBytes": 29421568, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:50:55.492Z", + "rssBytes": 29421568, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:50:56.027Z", + "rssBytes": 29421568, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:50:56.543Z", + "rssBytes": 29421568, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:50:57.063Z", + "rssBytes": 29421568, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:50:57.588Z", + "rssBytes": 29421568, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:50:58.114Z", + "rssBytes": 29421568, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:50:58.626Z", + "rssBytes": 29421568, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:50:59.143Z", + "rssBytes": 29421568, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:50:59.657Z", + "rssBytes": 29421568, + "cpuTimeMs": 78, + "processCount": 1 + } + ] + }, + "coordinator": { + "peakRssBytes": 117821440, + "cpuTimeMs": 500 + }, + "conformance": { + "connect": { + "passed": true + }, + "javascript": { + "passed": true + }, + "dom": { + "passed": true + }, + "forms": { + "passed": true, + "actionability": "dom-non-visual" + }, + "screenshot": { + "passed": true, + "supported": false, + "state": "unsupported", + "enforcement": "preflight" + }, + "teardown": { + "passed": true, + "rootProcessExited": true, + "transportDisposed": true + } + } + }, + { + "timings": { + "launchMs": 795.8728999999876, + "connectMs": 8.592399999994086, + "workloadMs": 90.94499999997788, + "shutdownMs": 695.364499999996 + }, + "browserTree": { + "peakRssBytes": 29429760, + "cpuTimeMs": 109, + "processCount": 1, + "sampleCount": 24, + "observations": [ + { + "sampledAt": "2026-09-02T19:51:01.075Z", + "rssBytes": 16072704, + "cpuTimeMs": 16, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:51:01.641Z", + "rssBytes": 29429760, + "cpuTimeMs": 109, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:51:02.202Z", + "rssBytes": 29429760, + "cpuTimeMs": 109, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:51:02.719Z", + "rssBytes": 29429760, + "cpuTimeMs": 109, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:51:03.245Z", + "rssBytes": 29429760, + "cpuTimeMs": 109, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:51:03.766Z", + "rssBytes": 29429760, + "cpuTimeMs": 109, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:51:04.287Z", + "rssBytes": 29429760, + "cpuTimeMs": 109, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:51:04.821Z", + "rssBytes": 29429760, + "cpuTimeMs": 109, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:51:05.347Z", + "rssBytes": 29429760, + "cpuTimeMs": 109, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:51:05.874Z", + "rssBytes": 29429760, + "cpuTimeMs": 109, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:51:06.388Z", + "rssBytes": 29429760, + "cpuTimeMs": 109, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:51:06.906Z", + "rssBytes": 29429760, + "cpuTimeMs": 109, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:51:07.430Z", + "rssBytes": 29429760, + "cpuTimeMs": 109, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:51:07.957Z", + "rssBytes": 29429760, + "cpuTimeMs": 109, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:51:08.489Z", + "rssBytes": 29429760, + "cpuTimeMs": 109, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:51:09.022Z", + "rssBytes": 29429760, + "cpuTimeMs": 109, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:51:09.543Z", + "rssBytes": 29429760, + "cpuTimeMs": 109, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:51:10.063Z", + "rssBytes": 29429760, + "cpuTimeMs": 109, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:51:10.587Z", + "rssBytes": 29429760, + "cpuTimeMs": 109, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:51:11.110Z", + "rssBytes": 29429760, + "cpuTimeMs": 109, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:51:11.633Z", + "rssBytes": 29429760, + "cpuTimeMs": 109, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:51:12.157Z", + "rssBytes": 29429760, + "cpuTimeMs": 109, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:51:12.686Z", + "rssBytes": 29429760, + "cpuTimeMs": 109, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:51:13.201Z", + "rssBytes": 29429760, + "cpuTimeMs": 109, + "processCount": 1 + } + ] + }, + "coordinator": { + "peakRssBytes": 118050816, + "cpuTimeMs": 672 + }, + "conformance": { + "connect": { + "passed": true + }, + "javascript": { + "passed": true + }, + "dom": { + "passed": true + }, + "forms": { + "passed": true, + "actionability": "dom-non-visual" + }, + "screenshot": { + "passed": true, + "supported": false, + "state": "unsupported", + "enforcement": "preflight" + }, + "teardown": { + "passed": true, + "rootProcessExited": true, + "transportDisposed": true + } + } + }, + { + "timings": { + "launchMs": 796.7405999999901, + "connectMs": 8.048200000019278, + "workloadMs": 88.52099999997881, + "shutdownMs": 690.0928999999887 + }, + "browserTree": { + "peakRssBytes": 29364224, + "cpuTimeMs": 234, + "processCount": 1, + "sampleCount": 24, + "observations": [ + { + "sampledAt": "2026-09-02T19:51:14.588Z", + "rssBytes": 16084992, + "cpuTimeMs": 16, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:51:15.186Z", + "rssBytes": 29364224, + "cpuTimeMs": 234, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:51:15.748Z", + "rssBytes": 29364224, + "cpuTimeMs": 234, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:51:16.269Z", + "rssBytes": 29364224, + "cpuTimeMs": 234, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:51:16.791Z", + "rssBytes": 29364224, + "cpuTimeMs": 234, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:51:17.312Z", + "rssBytes": 29364224, + "cpuTimeMs": 234, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:51:17.840Z", + "rssBytes": 29364224, + "cpuTimeMs": 234, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:51:18.363Z", + "rssBytes": 29364224, + "cpuTimeMs": 234, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:51:18.886Z", + "rssBytes": 29364224, + "cpuTimeMs": 234, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:51:19.405Z", + "rssBytes": 29364224, + "cpuTimeMs": 234, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:51:19.928Z", + "rssBytes": 29364224, + "cpuTimeMs": 234, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:51:20.444Z", + "rssBytes": 29364224, + "cpuTimeMs": 234, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:51:20.964Z", + "rssBytes": 29364224, + "cpuTimeMs": 234, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:51:21.476Z", + "rssBytes": 29364224, + "cpuTimeMs": 234, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:51:22.002Z", + "rssBytes": 29364224, + "cpuTimeMs": 234, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:51:22.538Z", + "rssBytes": 29364224, + "cpuTimeMs": 234, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:51:23.057Z", + "rssBytes": 29364224, + "cpuTimeMs": 234, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:51:23.571Z", + "rssBytes": 29364224, + "cpuTimeMs": 234, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:51:24.087Z", + "rssBytes": 29364224, + "cpuTimeMs": 234, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:51:24.620Z", + "rssBytes": 29364224, + "cpuTimeMs": 234, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:51:25.143Z", + "rssBytes": 29364224, + "cpuTimeMs": 234, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:51:25.671Z", + "rssBytes": 29364224, + "cpuTimeMs": 234, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:51:26.197Z", + "rssBytes": 29364224, + "cpuTimeMs": 234, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:51:26.712Z", + "rssBytes": 29364224, + "cpuTimeMs": 234, + "processCount": 1 + } + ] + }, + "coordinator": { + "peakRssBytes": 110706688, + "cpuTimeMs": 656 + }, + "conformance": { + "connect": { + "passed": true + }, + "javascript": { + "passed": true + }, + "dom": { + "passed": true + }, + "forms": { + "passed": true, + "actionability": "dom-non-visual" + }, + "screenshot": { + "passed": true, + "supported": false, + "state": "unsupported", + "enforcement": "preflight" + }, + "teardown": { + "passed": true, + "rootProcessExited": true, + "transportDisposed": true + } + } + }, + { + "timings": { + "launchMs": 774.1214999999793, + "connectMs": 7.139100000000326, + "workloadMs": 83.63520000001881, + "shutdownMs": 685.0718000000052 + }, + "browserTree": { + "peakRssBytes": 29372416, + "cpuTimeMs": 109, + "processCount": 1, + "sampleCount": 24, + "observations": [ + { + "sampledAt": "2026-09-02T19:51:28.078Z", + "rssBytes": 16134144, + "cpuTimeMs": 31, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:51:28.645Z", + "rssBytes": 29372416, + "cpuTimeMs": 109, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:51:29.197Z", + "rssBytes": 29372416, + "cpuTimeMs": 109, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:51:29.712Z", + "rssBytes": 29372416, + "cpuTimeMs": 109, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:51:30.260Z", + "rssBytes": 29372416, + "cpuTimeMs": 109, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:51:30.807Z", + "rssBytes": 29372416, + "cpuTimeMs": 109, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:51:31.336Z", + "rssBytes": 29372416, + "cpuTimeMs": 109, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:51:31.857Z", + "rssBytes": 29372416, + "cpuTimeMs": 109, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:51:32.396Z", + "rssBytes": 29372416, + "cpuTimeMs": 109, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:51:32.930Z", + "rssBytes": 29372416, + "cpuTimeMs": 109, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:51:33.475Z", + "rssBytes": 29372416, + "cpuTimeMs": 109, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:51:34.018Z", + "rssBytes": 29372416, + "cpuTimeMs": 109, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:51:34.541Z", + "rssBytes": 29372416, + "cpuTimeMs": 109, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:51:35.082Z", + "rssBytes": 29372416, + "cpuTimeMs": 109, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:51:35.619Z", + "rssBytes": 29372416, + "cpuTimeMs": 109, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:51:36.138Z", + "rssBytes": 29372416, + "cpuTimeMs": 109, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:51:36.656Z", + "rssBytes": 29372416, + "cpuTimeMs": 109, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:51:37.176Z", + "rssBytes": 29372416, + "cpuTimeMs": 109, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:51:37.692Z", + "rssBytes": 29372416, + "cpuTimeMs": 109, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:51:38.216Z", + "rssBytes": 29372416, + "cpuTimeMs": 109, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:51:38.738Z", + "rssBytes": 29372416, + "cpuTimeMs": 109, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:51:39.251Z", + "rssBytes": 29372416, + "cpuTimeMs": 109, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:51:39.769Z", + "rssBytes": 29372416, + "cpuTimeMs": 109, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:51:40.289Z", + "rssBytes": 29372416, + "cpuTimeMs": 109, + "processCount": 1 + } + ] + }, + "coordinator": { + "peakRssBytes": 107692032, + "cpuTimeMs": 516 + }, + "conformance": { + "connect": { + "passed": true + }, + "javascript": { + "passed": true + }, + "dom": { + "passed": true + }, + "forms": { + "passed": true, + "actionability": "dom-non-visual" + }, + "screenshot": { + "passed": true, + "supported": false, + "state": "unsupported", + "enforcement": "preflight" + }, + "teardown": { + "passed": true, + "rootProcessExited": true, + "transportDisposed": true + } + } + }, + { + "timings": { + "launchMs": 759.4468000000052, + "connectMs": 8.19440000000759, + "workloadMs": 78.58930000002147, + "shutdownMs": 693.1877999999851 + }, + "browserTree": { + "peakRssBytes": 29048832, + "cpuTimeMs": 109, + "processCount": 1, + "sampleCount": 24, + "observations": [ + { + "sampledAt": "2026-09-02T19:51:41.630Z", + "rssBytes": 16216064, + "cpuTimeMs": 63, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:51:42.203Z", + "rssBytes": 29048832, + "cpuTimeMs": 109, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:51:42.800Z", + "rssBytes": 29048832, + "cpuTimeMs": 109, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:51:43.320Z", + "rssBytes": 29048832, + "cpuTimeMs": 109, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:51:43.849Z", + "rssBytes": 29048832, + "cpuTimeMs": 109, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:51:44.367Z", + "rssBytes": 29048832, + "cpuTimeMs": 109, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:51:44.891Z", + "rssBytes": 29048832, + "cpuTimeMs": 109, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:51:45.417Z", + "rssBytes": 29048832, + "cpuTimeMs": 109, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:51:45.941Z", + "rssBytes": 29048832, + "cpuTimeMs": 109, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:51:46.473Z", + "rssBytes": 29048832, + "cpuTimeMs": 109, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:51:46.985Z", + "rssBytes": 29048832, + "cpuTimeMs": 109, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:51:47.498Z", + "rssBytes": 29048832, + "cpuTimeMs": 109, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:51:48.021Z", + "rssBytes": 29048832, + "cpuTimeMs": 109, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:51:48.543Z", + "rssBytes": 29048832, + "cpuTimeMs": 109, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:51:49.081Z", + "rssBytes": 29048832, + "cpuTimeMs": 109, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:51:49.609Z", + "rssBytes": 29048832, + "cpuTimeMs": 109, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:51:50.121Z", + "rssBytes": 29048832, + "cpuTimeMs": 109, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:51:50.645Z", + "rssBytes": 29048832, + "cpuTimeMs": 109, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:51:51.165Z", + "rssBytes": 29048832, + "cpuTimeMs": 109, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:51:51.693Z", + "rssBytes": 29048832, + "cpuTimeMs": 109, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:51:52.209Z", + "rssBytes": 29048832, + "cpuTimeMs": 109, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:51:52.721Z", + "rssBytes": 29048832, + "cpuTimeMs": 109, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:51:53.242Z", + "rssBytes": 29048832, + "cpuTimeMs": 109, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:51:53.762Z", + "rssBytes": 29048832, + "cpuTimeMs": 109, + "processCount": 1 + } + ] + }, + "coordinator": { + "peakRssBytes": 128151552, + "cpuTimeMs": 641 + }, + "conformance": { + "connect": { + "passed": true + }, + "javascript": { + "passed": true + }, + "dom": { + "passed": true + }, + "forms": { + "passed": true, + "actionability": "dom-non-visual" + }, + "screenshot": { + "passed": true, + "supported": false, + "state": "unsupported", + "enforcement": "preflight" + }, + "teardown": { + "passed": true, + "rootProcessExited": true, + "transportDisposed": true + } + } + }, + { + "timings": { + "launchMs": 770.3606999999902, + "connectMs": 8.84440000000177, + "workloadMs": 79.76230000000214, + "shutdownMs": 689.5881999999983 + }, + "browserTree": { + "peakRssBytes": 28975104, + "cpuTimeMs": 78, + "processCount": 1, + "sampleCount": 24, + "observations": [ + { + "sampledAt": "2026-09-02T19:51:55.130Z", + "rssBytes": 16187392, + "cpuTimeMs": 16, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:51:55.716Z", + "rssBytes": 28975104, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:51:56.279Z", + "rssBytes": 28975104, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:51:56.808Z", + "rssBytes": 28975104, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:51:57.335Z", + "rssBytes": 28975104, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:51:57.862Z", + "rssBytes": 28975104, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:51:58.399Z", + "rssBytes": 28975104, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:51:58.942Z", + "rssBytes": 28975104, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:51:59.463Z", + "rssBytes": 28975104, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:51:59.992Z", + "rssBytes": 28975104, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:52:00.548Z", + "rssBytes": 28975104, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:52:01.068Z", + "rssBytes": 28975104, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:52:01.592Z", + "rssBytes": 28975104, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:52:02.120Z", + "rssBytes": 28975104, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:52:02.648Z", + "rssBytes": 28975104, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:52:03.166Z", + "rssBytes": 28975104, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:52:03.691Z", + "rssBytes": 28975104, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:52:04.212Z", + "rssBytes": 28975104, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:52:04.725Z", + "rssBytes": 28975104, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:52:05.246Z", + "rssBytes": 28975104, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:52:05.769Z", + "rssBytes": 28975104, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:52:06.293Z", + "rssBytes": 28975104, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:52:06.818Z", + "rssBytes": 28975104, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:52:07.340Z", + "rssBytes": 28975104, + "cpuTimeMs": 78, + "processCount": 1 + } + ] + }, + "coordinator": { + "peakRssBytes": 127787008, + "cpuTimeMs": 641 + }, + "conformance": { + "connect": { + "passed": true + }, + "javascript": { + "passed": true + }, + "dom": { + "passed": true + }, + "forms": { + "passed": true, + "actionability": "dom-non-visual" + }, + "screenshot": { + "passed": true, + "supported": false, + "state": "unsupported", + "enforcement": "preflight" + }, + "teardown": { + "passed": true, + "rootProcessExited": true, + "transportDisposed": true + } + } + }, + { + "timings": { + "launchMs": 782.3910000000033, + "connectMs": 7.814500000007683, + "workloadMs": 71.29990000001271, + "shutdownMs": 691.179999999993 + }, + "browserTree": { + "peakRssBytes": 29425664, + "cpuTimeMs": 78, + "processCount": 1, + "sampleCount": 24, + "observations": [ + { + "sampledAt": "2026-09-02T19:52:08.689Z", + "rssBytes": 16252928, + "cpuTimeMs": 16, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:52:09.252Z", + "rssBytes": 29425664, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:52:09.805Z", + "rssBytes": 29425664, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:52:10.322Z", + "rssBytes": 29425664, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:52:10.846Z", + "rssBytes": 29425664, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:52:11.367Z", + "rssBytes": 29425664, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:52:11.877Z", + "rssBytes": 29425664, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:52:12.399Z", + "rssBytes": 29425664, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:52:12.919Z", + "rssBytes": 29425664, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:52:13.441Z", + "rssBytes": 29425664, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:52:13.973Z", + "rssBytes": 29425664, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:52:14.521Z", + "rssBytes": 29425664, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:52:15.091Z", + "rssBytes": 29425664, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:52:15.651Z", + "rssBytes": 29425664, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:52:16.173Z", + "rssBytes": 29425664, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:52:16.703Z", + "rssBytes": 29425664, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:52:17.243Z", + "rssBytes": 29425664, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:52:17.769Z", + "rssBytes": 29425664, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:52:18.291Z", + "rssBytes": 29425664, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:52:18.824Z", + "rssBytes": 29425664, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:52:19.345Z", + "rssBytes": 29425664, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:52:19.883Z", + "rssBytes": 29425664, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:52:20.404Z", + "rssBytes": 29425664, + "cpuTimeMs": 78, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:52:20.929Z", + "rssBytes": 29425664, + "cpuTimeMs": 78, + "processCount": 1 + } + ] + }, + "coordinator": { + "peakRssBytes": 113418240, + "cpuTimeMs": 593 + }, + "conformance": { + "connect": { + "passed": true + }, + "javascript": { + "passed": true + }, + "dom": { + "passed": true + }, + "forms": { + "passed": true, + "actionability": "dom-non-visual" + }, + "screenshot": { + "passed": true, + "supported": false, + "state": "unsupported", + "enforcement": "preflight" + }, + "teardown": { + "passed": true, + "rootProcessExited": true, + "transportDisposed": true + } + } + }, + { + "timings": { + "launchMs": 806.8270000000193, + "connectMs": 8.776600000011967, + "workloadMs": 82.20029999999679, + "shutdownMs": 686.7045999999973 + }, + "browserTree": { + "peakRssBytes": 29347840, + "cpuTimeMs": 47, + "processCount": 1, + "sampleCount": 24, + "observations": [ + { + "sampledAt": "2026-09-02T19:52:22.319Z", + "rssBytes": 16318464, + "cpuTimeMs": 16, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:52:22.927Z", + "rssBytes": 29347840, + "cpuTimeMs": 47, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:52:23.481Z", + "rssBytes": 29347840, + "cpuTimeMs": 47, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:52:24.014Z", + "rssBytes": 29347840, + "cpuTimeMs": 47, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:52:24.547Z", + "rssBytes": 29347840, + "cpuTimeMs": 47, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:52:25.075Z", + "rssBytes": 29347840, + "cpuTimeMs": 47, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:52:25.593Z", + "rssBytes": 29347840, + "cpuTimeMs": 47, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:52:26.109Z", + "rssBytes": 29347840, + "cpuTimeMs": 47, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:52:26.630Z", + "rssBytes": 29347840, + "cpuTimeMs": 47, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:52:27.147Z", + "rssBytes": 29347840, + "cpuTimeMs": 47, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:52:27.667Z", + "rssBytes": 29347840, + "cpuTimeMs": 47, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:52:28.181Z", + "rssBytes": 29347840, + "cpuTimeMs": 47, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:52:28.700Z", + "rssBytes": 29347840, + "cpuTimeMs": 47, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:52:29.219Z", + "rssBytes": 29347840, + "cpuTimeMs": 47, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:52:29.744Z", + "rssBytes": 29347840, + "cpuTimeMs": 47, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:52:30.262Z", + "rssBytes": 29347840, + "cpuTimeMs": 47, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:52:30.783Z", + "rssBytes": 29347840, + "cpuTimeMs": 47, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:52:31.302Z", + "rssBytes": 29347840, + "cpuTimeMs": 47, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:52:31.823Z", + "rssBytes": 29347840, + "cpuTimeMs": 47, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:52:32.340Z", + "rssBytes": 29347840, + "cpuTimeMs": 47, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:52:32.863Z", + "rssBytes": 29347840, + "cpuTimeMs": 47, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:52:33.383Z", + "rssBytes": 29347840, + "cpuTimeMs": 47, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:52:33.915Z", + "rssBytes": 29347840, + "cpuTimeMs": 47, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:52:34.438Z", + "rssBytes": 29347840, + "cpuTimeMs": 47, + "processCount": 1 + } + ] + }, + "coordinator": { + "peakRssBytes": 114688000, + "cpuTimeMs": 547 + }, + "conformance": { + "connect": { + "passed": true + }, + "javascript": { + "passed": true + }, + "dom": { + "passed": true + }, + "forms": { + "passed": true, + "actionability": "dom-non-visual" + }, + "screenshot": { + "passed": true, + "supported": false, + "state": "unsupported", + "enforcement": "preflight" + }, + "teardown": { + "passed": true, + "rootProcessExited": true, + "transportDisposed": true + } + } + }, + { + "timings": { + "launchMs": 788.5651000000071, + "connectMs": 10.222100000013597, + "workloadMs": 80.77660000001197, + "shutdownMs": 686.885500000004 + }, + "browserTree": { + "peakRssBytes": 29425664, + "cpuTimeMs": 109, + "processCount": 1, + "sampleCount": 24, + "observations": [ + { + "sampledAt": "2026-09-02T19:52:35.801Z", + "rssBytes": 16269312, + "cpuTimeMs": 16, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:52:36.391Z", + "rssBytes": 29425664, + "cpuTimeMs": 109, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:52:36.977Z", + "rssBytes": 29425664, + "cpuTimeMs": 109, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:52:37.502Z", + "rssBytes": 29425664, + "cpuTimeMs": 109, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:52:38.052Z", + "rssBytes": 29425664, + "cpuTimeMs": 109, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:52:38.593Z", + "rssBytes": 29425664, + "cpuTimeMs": 109, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:52:39.121Z", + "rssBytes": 29425664, + "cpuTimeMs": 109, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:52:39.643Z", + "rssBytes": 29425664, + "cpuTimeMs": 109, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:52:40.188Z", + "rssBytes": 29425664, + "cpuTimeMs": 109, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:52:40.727Z", + "rssBytes": 29425664, + "cpuTimeMs": 109, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:52:41.252Z", + "rssBytes": 29425664, + "cpuTimeMs": 109, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:52:41.773Z", + "rssBytes": 29425664, + "cpuTimeMs": 109, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:52:42.299Z", + "rssBytes": 29425664, + "cpuTimeMs": 109, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:52:42.859Z", + "rssBytes": 29425664, + "cpuTimeMs": 109, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:52:43.376Z", + "rssBytes": 29425664, + "cpuTimeMs": 109, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:52:43.896Z", + "rssBytes": 29425664, + "cpuTimeMs": 109, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:52:44.419Z", + "rssBytes": 29425664, + "cpuTimeMs": 109, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:52:44.942Z", + "rssBytes": 29425664, + "cpuTimeMs": 109, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:52:45.461Z", + "rssBytes": 29425664, + "cpuTimeMs": 109, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:52:45.987Z", + "rssBytes": 29425664, + "cpuTimeMs": 109, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:52:46.513Z", + "rssBytes": 29425664, + "cpuTimeMs": 109, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:52:47.029Z", + "rssBytes": 29425664, + "cpuTimeMs": 109, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:52:47.556Z", + "rssBytes": 29425664, + "cpuTimeMs": 109, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:52:48.078Z", + "rssBytes": 29425664, + "cpuTimeMs": 109, + "processCount": 1 + } + ] + }, + "coordinator": { + "peakRssBytes": 109621248, + "cpuTimeMs": 594 + }, + "conformance": { + "connect": { + "passed": true + }, + "javascript": { + "passed": true + }, + "dom": { + "passed": true + }, + "forms": { + "passed": true, + "actionability": "dom-non-visual" + }, + "screenshot": { + "passed": true, + "supported": false, + "state": "unsupported", + "enforcement": "preflight" + }, + "teardown": { + "passed": true, + "rootProcessExited": true, + "transportDisposed": true + } + } + }, + { + "timings": { + "launchMs": 792.5883000000031, + "connectMs": 10.029799999989336, + "workloadMs": 75.95069999998668, + "shutdownMs": 736.9794000000111 + }, + "browserTree": { + "peakRssBytes": 28950528, + "cpuTimeMs": 63, + "processCount": 1, + "sampleCount": 24, + "observations": [ + { + "sampledAt": "2026-09-02T19:52:49.437Z", + "rssBytes": 16236544, + "cpuTimeMs": 16, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:52:50.015Z", + "rssBytes": 28950528, + "cpuTimeMs": 63, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:52:50.574Z", + "rssBytes": 28950528, + "cpuTimeMs": 63, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:52:51.100Z", + "rssBytes": 28950528, + "cpuTimeMs": 63, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:52:51.620Z", + "rssBytes": 28950528, + "cpuTimeMs": 63, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:52:52.138Z", + "rssBytes": 28950528, + "cpuTimeMs": 63, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:52:52.669Z", + "rssBytes": 28950528, + "cpuTimeMs": 63, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:52:53.185Z", + "rssBytes": 28950528, + "cpuTimeMs": 63, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:52:53.709Z", + "rssBytes": 28950528, + "cpuTimeMs": 63, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:52:54.230Z", + "rssBytes": 28950528, + "cpuTimeMs": 63, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:52:54.763Z", + "rssBytes": 28950528, + "cpuTimeMs": 63, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:52:55.278Z", + "rssBytes": 28950528, + "cpuTimeMs": 63, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:52:55.794Z", + "rssBytes": 28950528, + "cpuTimeMs": 63, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:52:56.307Z", + "rssBytes": 28950528, + "cpuTimeMs": 63, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:52:56.830Z", + "rssBytes": 28950528, + "cpuTimeMs": 63, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:52:57.352Z", + "rssBytes": 28950528, + "cpuTimeMs": 63, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:52:57.875Z", + "rssBytes": 28950528, + "cpuTimeMs": 63, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:52:58.395Z", + "rssBytes": 28950528, + "cpuTimeMs": 63, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:52:58.908Z", + "rssBytes": 28950528, + "cpuTimeMs": 63, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:52:59.427Z", + "rssBytes": 28950528, + "cpuTimeMs": 63, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:52:59.954Z", + "rssBytes": 28950528, + "cpuTimeMs": 63, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:53:00.524Z", + "rssBytes": 28950528, + "cpuTimeMs": 63, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:53:01.048Z", + "rssBytes": 28950528, + "cpuTimeMs": 63, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:53:01.565Z", + "rssBytes": 28950528, + "cpuTimeMs": 63, + "processCount": 1 + } + ] + }, + "coordinator": { + "peakRssBytes": 129667072, + "cpuTimeMs": 531 + }, + "conformance": { + "connect": { + "passed": true + }, + "javascript": { + "passed": true + }, + "dom": { + "passed": true + }, + "forms": { + "passed": true, + "actionability": "dom-non-visual" + }, + "screenshot": { + "passed": true, + "supported": false, + "state": "unsupported", + "enforcement": "preflight" + }, + "teardown": { + "passed": true, + "rootProcessExited": true, + "transportDisposed": true + } + } + }, + { + "timings": { + "launchMs": 781.0146999999997, + "connectMs": 9.290199999988545, + "workloadMs": 82.3807000000379, + "shutdownMs": 690.0692999999737 + }, + "browserTree": { + "peakRssBytes": 29532160, + "cpuTimeMs": 94, + "processCount": 1, + "sampleCount": 24, + "observations": [ + { + "sampledAt": "2026-09-02T19:53:02.979Z", + "rssBytes": 16224256, + "cpuTimeMs": 16, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:53:03.549Z", + "rssBytes": 29532160, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:53:04.110Z", + "rssBytes": 29532160, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:53:04.638Z", + "rssBytes": 29532160, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:53:05.155Z", + "rssBytes": 29532160, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:53:05.683Z", + "rssBytes": 29532160, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:53:06.199Z", + "rssBytes": 29532160, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:53:06.727Z", + "rssBytes": 29532160, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:53:07.249Z", + "rssBytes": 29532160, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:53:07.779Z", + "rssBytes": 29532160, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:53:08.299Z", + "rssBytes": 29532160, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:53:08.817Z", + "rssBytes": 29532160, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:53:09.329Z", + "rssBytes": 29532160, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:53:09.846Z", + "rssBytes": 29532160, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:53:10.363Z", + "rssBytes": 29532160, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:53:10.891Z", + "rssBytes": 29532160, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:53:11.406Z", + "rssBytes": 29532160, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:53:11.926Z", + "rssBytes": 29532160, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:53:12.443Z", + "rssBytes": 29532160, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:53:12.968Z", + "rssBytes": 29532160, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:53:13.487Z", + "rssBytes": 29532160, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:53:14.009Z", + "rssBytes": 29532160, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:53:14.537Z", + "rssBytes": 29532160, + "cpuTimeMs": 94, + "processCount": 1 + }, + { + "sampledAt": "2026-09-02T19:53:15.062Z", + "rssBytes": 29532160, + "cpuTimeMs": 94, + "processCount": 1 + } + ] + }, + "coordinator": { + "peakRssBytes": 129122304, + "cpuTimeMs": 437 + }, + "conformance": { + "connect": { + "passed": true + }, + "javascript": { + "passed": true + }, + "dom": { + "passed": true + }, + "forms": { + "passed": true, + "actionability": "dom-non-visual" + }, + "screenshot": { + "passed": true, + "supported": false, + "state": "unsupported", + "enforcement": "preflight" + }, + "teardown": { + "passed": true, + "rootProcessExited": true, + "transportDisposed": true + } + } + } + ] +} diff --git a/docs/benchmarks/obscura-no-render-2026-09-02.md b/docs/benchmarks/obscura-no-render-2026-09-02.md new file mode 100644 index 0000000..1901cd3 --- /dev/null +++ b/docs/benchmarks/obscura-no-render-2026-09-02.md @@ -0,0 +1,55 @@ +# Historical Obscura non-visual memory proof: 2026-09-02 + +This five-run record is superseded by the [canonical September 3, 2026 proof](./obscura-non-visual-2026-09-03.md). It remains unchanged where values describe its historical artifacts. It is evidence for one pinned binary and fixture, not a product-wide memory guarantee. + +## Subject and method + +- Obscura version: `obscura 0.2.1` +- Executable SHA-256: `5b609fb46bc00da79e450fb0fbd34bd442e565b1394f4af95433e0b341078221` +- Executable size: 58,097,152 bytes +- Host reported by the artifact: `win32` release `10.0.26200`, 16 logical CPUs, Node `v24.15.0` +- Cockroach Browser checkout base: `deb6c25b4c0bc1927a631e3b062464b4f4cc2775`; the proof ran from a modified source worktree, so the base commit alone is not a complete source identifier +- Profile: `constrained` +- Cockroach Browser visual-action policy: `rendering: "none"`; the artifacts did not verify an engine renderer switch +- Runs per target: one warmup plus five measured runs +- Browser measurement: aggregate RSS for the complete runtime-owned browser process tree, sampled from spawn through startup, CDP connection, workload, settle, and shutdown preparation +- Coordinator measurement: reported separately and excluded from browser-tree RSS +- Fixture: one data-URL document exercising CDP connection, JavaScript evaluation, DOM query, text-input mutation, and HTMLElement click dispatch +- Required conformance: connect, JavaScript, DOM, and forms +- Screenshot result: unsupported and rejected by Cockroach Browser capability preflight, as required by the visual-action policy + +## Verdicts + +| Target | Result | Maximum browser-tree RSS | Required checks | Coordinator p95 peak RSS | +| --- | --- | ---: | --- | ---: | +| 30 MiB (31,457,280 bytes) | **Pass** | 29,298,688 bytes (27.941 MiB) | All passed | 112,218,112 bytes (107.020 MiB) | +| 25 MiB (26,214,400 bytes) | **Fail** | 29,523,968 bytes (28.156 MiB) | All passed | 110,841,856 bytes (105.707 MiB) | + +Passing 30 MiB means every measured browser-tree sample stayed at or below 30 MiB and every required conformance check passed. The 25 MiB run failed solely because the browser tree exceeded that target. Neither result says that the Node coordinator, the whole Cockroach Browser application, a rendered Obscura session, or arbitrary sites fit within 30 MiB. + +Obscura upstream has added rendering and screenshot functionality. In this historical fixture, `rendering: "none"` denotes Cockroach Browser's visual-action preflight policy; it does not assert that the selected binary exposed or received a renderer-disable launch switch. The workload did not exercise rendered-page capture. + +## Reproduction commands + +These commands match the recorded benchmark parameters. Use the reviewed binary whose digest is shown above; the example path is the exact path captured in the artifacts. + +```powershell +npm run build + +node scripts/benchmark-lightweight.mjs --implementation obscura --executable 'D:\skill box\.tmp\obscura-v0.2.1-eval\verified-bin\obscura.exe' --sha256 5b609fb46bc00da79e450fb0fbd34bd442e565b1394f4af95433e0b341078221 --resource-profile constrained --warmup 1 --iterations 5 --target-mib 30 --output output/resource-benchmarks/final-proof-obscura-constrained-30mib-20260902.json + +node scripts/benchmark-lightweight.mjs --implementation obscura --executable 'D:\skill box\.tmp\obscura-v0.2.1-eval\verified-bin\obscura.exe' --sha256 5b609fb46bc00da79e450fb0fbd34bd442e565b1394f4af95433e0b341078221 --resource-profile constrained --warmup 1 --iterations 5 --target-mib 25 --output output/resource-benchmarks/final-proof-obscura-constrained-25mib-20260902.json +``` + +The second command is expected to write its JSON report and exit with code `2` because the declared target is not met. + +## Raw artifact identity + +`output/` is intentionally ignored by Git. A reviewer with the proof worktree can verify the raw files without treating them as release assets: + +| Local artifact | SHA-256 | +| --- | --- | +| `output/resource-benchmarks/final-proof-obscura-constrained-30mib-20260902.json` | `f1dc59f761a3be80434ae61dacd49f284581a04d3eb7e080c06adf371cb74efa` | +| `output/resource-benchmarks/final-proof-obscura-constrained-25mib-20260902.json` | `6de15ba47e38c8f438a1ab8ae137fe9f44ace8a287baabfcefce97d15f06520c` | + +For a release decision, repeat on the target deployment hardware with at least 20 measured iterations and compare an unchanged baseline and candidate under the same idle-host conditions. diff --git a/docs/benchmarks/obscura-non-visual-2026-09-03.md b/docs/benchmarks/obscura-non-visual-2026-09-03.md new file mode 100644 index 0000000..54323f9 --- /dev/null +++ b/docs/benchmarks/obscura-non-visual-2026-09-03.md @@ -0,0 +1,77 @@ +# Obscura non-visual memory proof: 2026-09-03 + +This is the canonical lightweight proof record for Cockroach Browser with the reviewed Obscura 0.2.1 binary. It reports the complete owned browser-process-tree RSS for one pinned non-visual fixture and keeps the Node coordinator outside that number. + +## Verdicts + +| Target | Verdict | Maximum observed browser-tree RSS | +| --- | --- | ---: | +| 30 MiB | **PASS** | 29,622,272 bytes | +| 25 MiB | **FAIL** | 29,679,616 bytes | + +The 30 MiB target is 31,457,280 bytes. Every measured owned-browser-tree observation stayed within it, and every required capability check passed. The exact maximum above is authoritative; it is exactly 28.25 MiB. + +The 25 MiB target is 26,214,400 bytes. Every required capability check still passed, but the memory condition did not, so the combined verdict is honestly reported as a failure. + +These are not whole-app memory guarantees. They do not claim that the Node coordinator, an arbitrary page, a rendered page, a persistent or attached session, or a full browser fits either target. Full browser engines normally use far more memory under realistic rendered workloads. + +## Immutable artifacts + +| Artifact | SHA-256 | +| --- | --- | +| `docs/benchmarks/artifacts/obscura-0.2.1-constrained-non-visual-30mib-2026-09-03-rc1.json` | `f90b31d6f5d5096300ac2722ed835db0483a76dc4d51ee85e86604a6634c0aa7` | +| `docs/benchmarks/artifacts/obscura-0.2.1-constrained-non-visual-25mib-2026-09-03-rc1.json` | `581eb93577d6b52c71e02d7e0b71914f88acd0920a6e0e06925aae0a4575d2df` | + +Each JSON artifact retains all 20 measured launches and every timestamped process-tree observation: 478 in the 30 MiB run and 480 in the 25 MiB run, for 958 total. Each launch retained 23 or 24 boundary and continuously sampled observations, including the 10 required steady-state samples at 25 ms intervals. + +## Reviewed identity + +- Executable: `obscura.exe`, version `obscura 0.2.1`, 58,097,152 bytes +- Executable SHA-256: `5b609fb46bc00da79e450fb0fbd34bd442e565b1394f4af95433e0b341078221` +- Git base commit: `deb6c25b4c0bc1927a631e3b062464b4f4cc2775` +- Working tree: dirty; the base commit alone is not the tested source identity +- Source-tree SHA-256: `fb0c4597e39f319dd9b6f3bab02777c395e9d8d84906981bf939a39b470e7279` +- Runtime-build SHA-256: `6738efa4000ba482db83c9dc95ba2f21caed31de96f30dcd342e5dc722d86025` +- Benchmark-harness SHA-256: `08a5294f2d446765f712b93c9bfaaca010b1d043ded638d1f4f57b5038c97e86` +- Source scope: runtime, schemas, benchmark/package/build configuration, and tests; generated documentation and benchmark artifacts are excluded +- Identity normalization: relative paths use forward slashes, and text line endings are normalized to LF before hashing + +The recorded machine was Windows `win32`/`x64`, release `10.0.26200`, with 16 logical CPUs, an AMD Ryzen 7 4800H, 16,557,887,488 bytes of total memory, and Node `v24.15.0`. + +## Method + +- Resource profile: `constrained` +- Runs per target: 1 warmup plus 20 measured launches +- Workload: one runtime-owned loopback CDP server and one non-visual data-URL document +- Operations: connect, JavaScript evaluation, DOM query, text input, and HTMLElement click dispatch +- Required checks: connect, JavaScript, DOM, forms, screenshot preflight denial, and teardown; every check passed in every measured launch +- Measurement: aggregate RSS for the complete runtime-owned browser process tree from spawn through startup, CDP connection, workload, settle, and shutdown preparation +- Sampling: 10 explicit steady-state samples at 25 ms intervals, plus boundary and continuous observations; 23 or 24 observations were retained per measured launch, 958 across both target runs +- Timing bounds: 15,000 ms startup, 10,000 ms action, 5,000 ms teardown, and 500 ms settle +- Target rule: every owned-browser-tree RSS observation must be at or below the target and every required check must pass +- Energy: unavailable; no calibrated platform energy counter was present, and CPU time is not reported as watts + +The Node coordinator memory is measured and reported separately from the owned browser-process-tree RSS. + +`renderingPolicy: "visual-actions-denied"` records Cockroach Browser's screenshot preflight policy only. It does not assert that the selected engine exposes or received a renderer-disable launch switch, and it does not assert any other renderer state. + +## Distributions + +| Target | Minimum | Median | p95 | Maximum | Required capabilities | +| --- | ---: | ---: | ---: | ---: | --- | +| 30 MiB | 28,893,184 bytes | 29,347,840 bytes | 29,569,024 bytes | 29,622,272 bytes | All pass | +| 25 MiB | 28,831,744 bytes | 29,323,264 bytes | 29,634,560 bytes | 29,679,616 bytes | All pass | + +The distribution values summarize each launch's peak owned-browser-tree RSS. The immutable artifacts remain the source of truth for every individual observation, conformance result, timing, coordinator measurement, and teardown record. + +## Verification and reproduction + +Verify the checked-in artifacts, their narrative bindings, and the current source/build identity with: + +```powershell +npm run verify:lightweight-proof +``` + +To repeat the benchmark, build the same source identity, use the reviewed executable digest above, and run the harness with `--implementation obscura --resource-profile constrained --warmup 1 --iterations 20 --resource-samples 10 --resource-sample-interval-ms 25`. Run each target independently and preserve the non-zero exit from the failing target after its JSON report is written. A new run is new evidence and must not overwrite these immutable artifacts. + +The un-suffixed September 3 JSON artifacts and the [September 2 five-run record](./obscura-no-render-2026-09-02.md) remain historical evidence and are superseded by this release-candidate record. diff --git a/docs/capabilities.md b/docs/capabilities.md index 68d9ac6..22c09de 100644 --- a/docs/capabilities.md +++ b/docs/capabilities.md @@ -12,6 +12,8 @@ This file is generated from `src/capabilities.ts`. | `sessions.executable` | sessions | Custom browser executable | **available** | `engine and executablePath` | | `sessions.discovery` | sessions | Cross-platform browser discovery | **available** | `browser discover and discoverBrowserExecutables` | | `sessions.providers` | sessions | Explicit browser providers | **available** | `browserProvider` | +| `sessions.lightweight_cdp` | sessions | Runtime-owned lightweight CDP engine | **adapter** | `browserProvider.kind=lightweight and /v1/engines` | +| `sessions.engine_negotiation` | sessions | Machine-readable engine negotiation | **available** | `/v1/engines and browser_engine_preflight` | | `sessions.raw_playwright` | sessions | Complete raw Playwright API | **available** | `cockroach-browser/automation` | | `sessions.raw_puppeteer` | sessions | Complete raw Puppeteer Core API | **available** | `cockroach-browser/puppeteer` | | `sessions.raw_cdp` | sessions | Raw Chrome DevTools Protocol sessions | **available** | `cockroach-browser/cdp` | @@ -26,7 +28,7 @@ This file is generated from `src/capabilities.ts`. | `sessions.clipboard` | sessions | Policy-gated clipboard | **available** | `clipboard.*` | | `sessions.proxy` | sessions | User-supplied proxy | **available** | `proxy` | | `sessions.locale` | sessions | Locale and timezone | **available** | `locale/timezoneId` | -| `sessions.emulation` | sessions | Device and network emulation | **available** | `emulation.set/clear` | +| `sessions.emulation` | sessions | Bounded page emulation | **available** | `emulation.set/clear` | | `sessions.raw_emulation` | sessions | Complete upstream emulation controls | **available** | `cockroach-browser/automation and cockroach-browser/puppeteer` | | `tabs.multiple` | interaction | Tabs and popups | **available** | `tab.*` | | `tabs.lock` | interaction | Exclusive tab locks | **available** | `tab.lock/unlock/status` | @@ -74,7 +76,8 @@ This file is generated from `src/capabilities.ts`. | `evidence.network_inspect` | evidence | Network inspection | **available** | `network.inspect and browser_network` | | `evidence.network_export` | evidence | Network export | **available** | `network.export` | | `evidence.receipts` | evidence | Hash-chained receipts | **available** | `ActionReceipt` | -| `evidence.extract` | evidence | Text and HTML extraction | **available** | `extract` | +| `evidence.extract` | evidence | Text and raw HTML extraction | **available** | `extract` | +| `evidence.extract_structured` | evidence | Sanitized structured extraction | **available** | `extract.structured` | | `evidence.coverage` | evidence | JavaScript and CSS coverage | **available** | `cockroach-browser/puppeteer Page.coverage` | | `evidence.heap` | evidence | Heap snapshots and runtime object queries | **available** | `cockroach-browser/cdp and cockroach-browser/puppeteer` | | `evidence.screencast` | evidence | Screencasting, metrics, and profiling | **available** | `cockroach-browser/cdp and cockroach-browser/puppeteer` | @@ -95,7 +98,7 @@ This file is generated from `src/capabilities.ts`. | `security.effects` | security | Effect-level policy | **available** | `allowedEffects` | | `security.budgets` | security | Finite budgets | **available** | `budget` | | `security.process_resources` | security | Owned browser process-tree governor | **available** | `session resources, CLI, dashboard, and Prometheus` | -| `security.network_routes` | security | Policy-bounded network routes | **available** | `network.route.*` | +| `security.network_routes` | security | Policy-bounded HTTP routes | **available** | `network.route.*` | | `security.approvals` | security | Exact action approvals | **adapter** | `MaqamApprovalProvider` | | `security.secrets` | security | Secret references | **adapter** | `SecretResolver` | | `deploy.cli` | deployment | Command-line interface | **available** | `cockroach-browser` | @@ -134,6 +137,6 @@ This file is generated from `src/capabilities.ts`. ## Status model -- **available**: implemented in Cockroach Browser 0.4.1 +- **available**: implemented on current `main` for the next release; this does not by itself prove publication in Cockroach Browser 0.5.0-rc.1 - **adapter**: integration contract is present, but another package or host authority is required -- **planned**: documented direction, not part of the current release +- **planned**: documented direction, not implemented in the current source tree diff --git a/docs/capture.md b/docs/capture.md index 420e8f1..d5ef88c 100644 --- a/docs/capture.md +++ b/docs/capture.md @@ -39,6 +39,6 @@ HAR, video, trace, console, and network capture can contain sensitive material. The Qarinah adapter records canonical input and output digests, evidence IDs, the browser receipt hash, and bounded descriptive metadata after filtering cookies, storage values, form values, and secrets. It does not dispatch browser actions or store hidden reasoning. A host may link a mutation outcome to a complete causal receipt chain when one exists, but the recorder does not require or synthesize that chain. -## Release status +## Source status -This manual targets Cockroach Browser 0.4.1. Check [the capability matrix](https://cockroachbrowser.com/docs/capabilities/) before relying on a surface. Available means implemented in this release. Adapter means another authority or package is required. Planned means the surface is not part of this release. +This manual is generated from current `main` for the next Cockroach Browser release. Package examples still identify published line 0.5.0-rc.1 where shown; verify npm provenance and the matching tag before production use. Available means implemented in the current source tree, not necessarily published in 0.5.0-rc.1. Adapter means another authority or package is required. Planned means the surface is not implemented here. diff --git a/docs/crawler.md b/docs/crawler.md index b937ff4..d3c2c19 100644 --- a/docs/crawler.md +++ b/docs/crawler.md @@ -23,6 +23,6 @@ Keep source URL, capture time, content digest, extraction method, and failure st Map once with the crawler, rank candidate pages, then render only the pages that need a browser. This preserves browser budgets and makes the reason for each rendered session visible. -## Release status +## Source status -This manual targets Cockroach Browser 0.4.1. Check [the capability matrix](https://cockroachbrowser.com/docs/capabilities/) before relying on a surface. Available means implemented in this release. Adapter means another authority or package is required. Planned means the surface is not part of this release. +This manual is generated from current `main` for the next Cockroach Browser release. Package examples still identify published line 0.5.0-rc.1 where shown; verify npm provenance and the matching tag before production use. Available means implemented in the current source tree, not necessarily published in 0.5.0-rc.1. Adapter means another authority or package is required. Planned means the surface is not implemented here. diff --git a/docs/deployment.md b/docs/deployment.md index 500e8e0..e4a3441 100644 --- a/docs/deployment.md +++ b/docs/deployment.md @@ -23,14 +23,14 @@ npx cockroach-browser session list --token-file .cockroach-browser/auth-token Pin the package and browser version, use a read-only root filesystem, mount only the data and artifact paths the worker needs, and bind the published port to loopback or a private service network. ``` -docker build -t cockroach-browser:0.4.1 . +docker build -t cockroach-browser:0.5.0-rc.1 . docker run --rm \ --read-only \ --tmpfs /tmp:rw,noexec,nosuid,size=256m \ --shm-size 512m \ --tmpfs /data \ -p 127.0.0.1:43110:43111 \ - cockroach-browser:0.4.1 + cockroach-browser:0.5.0-rc.1 ``` ## Remote worker @@ -39,17 +39,21 @@ Remote binding requires TLS and bearer authentication. Place the worker behind s ## OpenAPI, metrics, and activity -Authenticated operators can inspect /v1/openapi.json, scrape Prometheus text from /v1/metrics, poll /v1/activity, or subscribe to /v1/activity/stream. The activity surface is bounded and actor-filtered; it is not a raw browser telemetry dump. +Any authenticated identity can inspect the relative-base /v1/openapi.json route index. Global /v1/health, Prometheus text from /v1/metrics, and /v1/evidence/verify require the daemon administrator token. Actor tokens can poll /v1/activity or subscribe to /v1/activity/stream only for sessions they may view. OpenAPI indexes every implemented daemon method/path with unique operation IDs and response declarations, but grants no authority by itself. The activity surface is bounded and actor-filtered; it is not a raw browser telemetry dump. ## Team and worker operation -Embed TeamSessionStore to persist owner, viewer, and operator roles with revocation. Use BrowserWorkerPool to choose healthy authenticated workers by capacity, weight, and explicit tags. Keep profile directories local to their owning worker. +Actor-scoped bearer tokens require TeamSessionStore; daemon startup fails closed without it. The administrator token and every actor token must be unique or startup fails with AUTH_TOKEN_COLLISION. Actor-token session creation additionally requires a host-owned actorSessionFactory that derives authoritative session input from individually reviewed request fields and never spreads caller JSON. The server supplies the authenticated actor and claims the session before exposure, closing it if the ownership claim fails. Access mutations replace the in-memory map only after durable persistence succeeds. + +Set daemon admission with BrowserServerOptions.maxSessions (default 32) and maxSessionsPerActor (default 8). The concurrency-safe reservation path counts all non-closed sessions plus pending creates; limit responses are HTTP 429 with SESSION_GLOBAL_LIMIT_EXCEEDED or SESSION_ACTOR_LIMIT_EXCEEDED. maxRequestBytes defaults to 1,048,576 bytes and accepts integers from 1,024 through 16,777,216 bytes. + +Use BrowserWorkerPool to choose healthy authenticated workers by capacity, weight, and explicit tags. Keep profile directories local to their owning worker. ## Release verification Build on Node 22, 24, and 26; run runtime and browser tests; verify the packed npm consumer; audit runtime dependencies; validate the website; inspect the tarball; and match the npm artifact to the reviewed Git commit before publishing. -## Release status +## Source status -This manual targets Cockroach Browser 0.4.1. Check [the capability matrix](https://cockroachbrowser.com/docs/capabilities/) before relying on a surface. Available means implemented in this release. Adapter means another authority or package is required. Planned means the surface is not part of this release. +This manual is generated from current `main` for the next Cockroach Browser release. Package examples still identify published line 0.5.0-rc.1 where shown; verify npm provenance and the matching tag before production use. Available means implemented in the current source tree, not necessarily published in 0.5.0-rc.1. Adapter means another authority or package is required. Planned means the surface is not implemented here. diff --git a/docs/files.md b/docs/files.md index 1a9daf9..07a33f6 100644 --- a/docs/files.md +++ b/docs/files.md @@ -23,6 +23,6 @@ Page PDF generation is available in Chromium sessions and is recorded as evidenc Profile import and export use encrypted storage managed by the profile vault. Do not route profile archives through agent-visible upload or download actions. -## Release status +## Source status -This manual targets Cockroach Browser 0.4.1. Check [the capability matrix](https://cockroachbrowser.com/docs/capabilities/) before relying on a surface. Available means implemented in this release. Adapter means another authority or package is required. Planned means the surface is not part of this release. +This manual is generated from current `main` for the next Cockroach Browser release. Package examples still identify published line 0.5.0-rc.1 where shown; verify npm provenance and the matching tag before production use. Available means implemented in the current source tree, not necessarily published in 0.5.0-rc.1. Adapter means another authority or package is required. Planned means the surface is not implemented here. diff --git a/docs/getting-started.md b/docs/getting-started.md index 06a7ed9..4f45ca6 100644 --- a/docs/getting-started.md +++ b/docs/getting-started.md @@ -83,6 +83,6 @@ await browser.close(); ``` -## Release status +## Source status -This manual targets Cockroach Browser 0.4.1. Check [the capability matrix](https://cockroachbrowser.com/docs/capabilities/) before relying on a surface. Available means implemented in this release. Adapter means another authority or package is required. Planned means the surface is not part of this release. +This manual is generated from current `main` for the next Cockroach Browser release. Package examples still identify published line 0.5.0-rc.1 where shown; verify npm provenance and the matching tag before production use. Available means implemented in the current source tree, not necessarily published in 0.5.0-rc.1. Adapter means another authority or package is required. Planned means the surface is not implemented here. diff --git a/docs/headless-compatibility.md b/docs/headless-compatibility.md index 0ed876b..3be64ba 100644 --- a/docs/headless-compatibility.md +++ b/docs/headless-compatibility.md @@ -13,6 +13,8 @@ This is the maintained, practical headless-browser landscape, not a claim that e | Android Chrome / WebView | W3C WebDriver or Appium endpoint supplied by the operator | Adapter | | iOS Safari / WebView | W3C WebDriver or Appium endpoint supplied by the operator | Adapter | | Remote browser farms | Authenticated fleet provider declaring engines, regions, TTL, proxy classes, and live-view capability | Adapter | +| Obscura | Explicit runtime-owned loopback CDP process, optionally pinned to a reviewed SHA-256 digest | Experimental lightweight provider | +| Lightpanda | Manifest, configuration validation, and machine preflight are present; managed launch fails closed until an engine- or OS-level boundary covers all HTTP, WebSocket, worker, and related egress | Preflight only on current main | Playwright documents Chromium, Firefox, WebKit, Chrome, and Edge channels in its [browser guide](https://playwright.dev/docs/browsers). Puppeteer documents Chrome and Firefox over CDP and WebDriver BiDi in [What is Puppeteer?](https://pptr.dev/guides/what-is-puppeteer). Selenium documents its cross-browser WebDriver implementations in [Supported Browsers](https://www.selenium.dev/documentation/webdriver/browsers/) and the event-driven BiDi surface in [WebDriver BiDi](https://www.selenium.dev/documentation/webdriver/bidi/). @@ -25,15 +27,54 @@ Playwright documents Chromium, Firefox, WebKit, Chrome, and Edge channels in its | Selenium WebDriver / Grid | Cross-language W3C automation and distributed execution | WebDriver and BiDi clients; operator supplies endpoint | | WebdriverIO | JavaScript WebDriver/BiDi test and automation framework | Compatible through external WebDriver endpoints; not embedded | | Cypress | Browser-focused application testing | Alternative test runner; not embedded | +| Playwright MCP | Accessibility-snapshot-oriented MCP tools over Playwright | Compatible external MCP/tool surface; Cockroach keeps its own policy and evidence boundary ([Playwright MCP](https://github.com/microsoft/playwright-mcp)) | +| Browser Use | Python agent orchestration over browsers | Compatible external planner; not embedded ([Browser Use](https://github.com/browser-use/browser-use)) | +| Stagehand | Agent-oriented browser API over Playwright/CDP | Compatible external planner/provider; not embedded ([Stagehand](https://github.com/browserbase/stagehand)) | +| agent-browser | CLI-oriented browser automation for agents | Alternative client/tool surface; not embedded ([agent-browser](https://github.com/vercel-labs/agent-browser)) | | chromedp | Go CDP automation | Alternative CDP client; Cockroach exposes raw CDP and a Go daemon SDK | | Rod | Go CDP automation with waits, frames, shadow DOM, and hijacking | Alternative CDP client; not embedded ([Rod](https://github.com/go-rod/rod)) | | Browserless | Hosted/self-hosted remote browser infrastructure | Connect through explicit CDP or fleet provider; service not bundled | +| Steel | Session-oriented hosted/self-hosted browser infrastructure with live view and debugging | Fleet/live-view design reference or explicit remote provider; service not bundled | | Splash | Scriptable rendering service | Specialized remote renderer; not embedded | | HtmlUnit | JVM browser simulation without a full graphical engine | Specialized DOM/browser simulator; not embedded | +| Servo / Ladybird | Independent browser-engine projects | Experimental engine research; no bounded Cockroach provider is claimed ([Servo](https://github.com/servo/servo), [Ladybird](https://github.com/LadybirdBrowser/ladybird)) | | Ferrum | Ruby CDP automation | Alternative CDP client; Cockroach supplies a Ruby daemon SDK | | Taiko | JavaScript Chromium automation | Alternative Chromium harness; not embedded | | PhantomJS / Zombie.js | Historical headless/simulated-browser projects | Legacy only; not selected for the maintained runtime | +## Independent lightweight engines + +Obscura and Lightpanda are independent engines, not small Chromium builds. They can materially reduce memory for compatible DOM and JavaScript workloads, but neither is presented as a drop-in replacement for every Chromium, Firefox, or WebKit behavior. + +The managed Obscura provider is deliberately explicit: + +- The operator supplies the executable and implementation identity. A reviewed SHA-256 digest is optional, and Cockroach Browser never downloads or silently substitutes a binary. +- The runtime starts one owned process on a reserved `127.0.0.1` port with fixed arguments, bounded diagnostics, and a minimal environment. +- Arbitrary launch flags, remote endpoints, extensions, persistent profiles, headed mode, and proxy credentials are rejected on this route. +- The session discloses the independent provider identity and experimental maturity. A machine-readable engine manifest distinguishes supported, experimental, and unsupported capabilities before an action executes. +- Unsupported work routes to a full engine only when the host deliberately creates that full-engine session; the runtime does not silently change engines inside a governed session. + +Lightpanda has a machine-readable manifest, configuration validation, and host preflight on current `main`, but it is not a managed execution provider yet. Its managed launch fails closed because a page-route hook does not cover every browser egress channel, including WebSockets and worker-originated traffic. Enabling it requires a complete engine- or OS-level network boundary and regression evidence for those paths. + +Obscura documents partial CDP plus Playwright/Puppeteer integration and publishes separate conformance and benchmark evidence in its [engine repository](https://github.com/h4ckf0r0day/obscura) and [benchmark repository](https://github.com/h4ckf0r0day/obscura-benchmark). Obscura 0.2.1 also documents rendering and screenshot improvements in its [release notes](https://github.com/h4ckf0r0day/obscura/releases/tag/v0.2.1). Cockroach Browser's `rendering: "none"` setting means visual actions are denied at capability preflight; it does not assert that the selected binary disabled or omitted its renderer. Lightpanda documents its CDP server and rendererless platform boundary in its [engine repository](https://github.com/lightpanda-io/browser) and [official documentation](https://github.com/lightpanda-io/docs). Those upstream results are evidence about specific versions and fixtures, not a Cockroach Browser guarantee. + +## Negotiate capabilities before launch + +Capability negotiation is machine-readable and independent of session creation: + +- `GET /v1/engines` returns every engine manifest; `GET /v1/engines?engine=obscura` filters it. +- `BrowserClient.engines(engine?)` exposes the same authenticated HTTP surface. +- MCP tool `browser_engines` returns manifests without launching a browser. +- MCP tool `browser_engine_preflight` accepts one engine, one or more exact action kinds, and optional `allowExperimental`. It returns each required capability, state, note, acceptance decision, and unmet set without launching a browser. + +Supported capabilities are admitted. Experimental capabilities fail closed unless the caller explicitly opts in. Unsupported capabilities always fail. The `runtime.owned_launch` manifest entry is supported for Chromium, Firefox, and WebKit; experimental for Obscura; and unsupported for Lightpanda. A successful preflight describes compatibility; it does not create a session, grant origins or credentials, expand policy, or prove that a later page will behave correctly. + +## Measured non-visual result + +On September 3, 2026, the pinned Obscura 0.2.1 binary (`5b609fb46bc00da79e450fb0fbd34bd442e565b1394f4af95433e0b341078221`) ran one warmup plus 20 measured launches per target of the constrained non-visual fixture. Every launch retained ten steady-state samples at 25 ms intervals in addition to boundary observations. The 30 MiB target passed with a maximum complete owned-browser-process-tree RSS of 29,622,272 bytes (28.25 MiB). The 25 MiB target failed with a 29,679,616-byte maximum (28.30 MiB). CDP connection, JavaScript, DOM, forms, screenshot preflight denial, and verified teardown passed in every measured launch. + +The Node coordinator was measured separately. This result is not evidence that the whole app, coordinator, rendered pages, full Chromium/Firefox/WebKit sessions, or arbitrary sites use 30 MiB; full engines use far more memory. See the [canonical proof record](./benchmarks/obscura-non-visual-2026-09-03.md) and [resource-governance methodology](./resource-governance.md). + ## Feature coverage Cockroach Browser exposes two deliberately separate layers: @@ -41,7 +82,9 @@ Cockroach Browser exposes two deliberately separate layers: - The raw layer preserves complete pinned Playwright and Puppeteer APIs, Playwright Test/codegen, CDP, BiDi, and WebDriver/Appium transports. - The bounded agent layer adds origin/effect policy, exact approvals, finite budgets, semantic references, evidence, receipts, challenges, team access, jobs, MCP, SDKs, and a model-driven agent loop. -The bounded runtime currently covers navigation, tabs/popups, forms, keyboard, mouse, drag/drop, open shadow DOM, same-origin frames, JavaScript under policy, cookies/storage/state, uploads/downloads, dialogs, network inspection and routing, screenshots, PDF on Chromium, traces, HAR, video, audits, visual comparison, profiles, proxies, emulation, and Chromium/Firefox/WebKit execution. Advanced unrestricted objects such as arbitrary handles, raw workers, every protocol command, browser extensions, CPU profiles, heap snapshots, and screencasting stay on the raw operator layer rather than being disguised as safe agent actions. +The bounded runtime currently covers navigation, tabs/popups, forms, keyboard, mouse, drag/drop, open shadow DOM, same-origin frames, JavaScript under policy, cookies/storage/state, uploads/downloads, dialogs, network inspection and HTTP(S) routing, WebSocket-handshake validation for runtime-owned full engines, screenshots, PDF on Chromium, traces, HAR, video, audits, visual comparison, profiles, proxies, emulation, and Chromium/Firefox/WebKit execution. Service workers are blocked in bounded full-engine contexts. These application controls do not contain WebRTC/STUN/TURN/UDP, WebTransport/QUIC, attached CDP, lightweight-engine WebSockets, or raw operator traffic; use deployment-owned OS or container egress controls for hostile content. Advanced unrestricted objects such as arbitrary handles, raw workers, every protocol command, browser extensions, CPU profiles, heap snapshots, and screencasting stay on the raw operator layer rather than being disguised as safe agent actions. + +Feature coverage is a routed union, not a claim that one engine implements the entire web platform. The full Playwright/Puppeteer layer owns graphical rendering, tracing, video, browser extensions, persistent profiles, comprehensive frames/shadow DOM, and raw protocol access. The managed Obscura non-visual lane targets compatible navigation, JavaScript, DOM, structured extraction, HTMLElement activation, and input/textarea value assignment without visual pointer fidelity. Lightpanda remains a fail-closed manifest/preflight route until its complete egress boundary exists. WebDriver/Appium and fleet adapters cover operator-owned browsers, devices, regions, and live-view systems. Cockroach Browser policy, approvals, budgets, evidence, and receipts govern bounded-runtime routes; raw and external operator-owned surfaces retain their own authority boundaries. ## Honest boundaries @@ -49,3 +92,4 @@ The bounded runtime currently covers navigation, tabs/popups, forms, keyboard, m - Cockroach Browser does not operate a proxy network, CAPTCHA bypass, residential identity pool, or hosted browser fleet. - Playwright, Puppeteer, Selenium, Cypress, and the language-specific CDP clients are different harnesses over overlapping browser engines. Copying every wrapper would add dependency and security cost without adding engine capability. - “All headless features” means the pinned upstream raw APIs plus the documented bounded actions and adapters. The machine-readable capability registry is the release contract. +- The observed 30 MiB pass applies only to the named pinned Obscura binary, non-visual fixture, host, and complete owned browser process tree. The corresponding 25 MiB run failed, and whole-app/coordinator memory is not included in the 30 MiB result. diff --git a/docs/jobs.md b/docs/jobs.md index e0f4d37..e85e380 100644 --- a/docs/jobs.md +++ b/docs/jobs.md @@ -23,6 +23,6 @@ Maqam and application services should carry stable operation IDs through policy, The built-in job queue is process-local and file-backed. It is useful for one owned worker. Team session ownership and revocable viewer/operator grants are available through TeamSessionStore, and BrowserWorkerPool can route new sessions across reviewed authenticated daemons. Neither turns the local queue into a distributed transaction coordinator. Signed lifecycle delivery remains a separate durable webhook outbox. -## Release status +## Source status -This manual targets Cockroach Browser 0.4.1. Check [the capability matrix](https://cockroachbrowser.com/docs/capabilities/) before relying on a surface. Available means implemented in this release. Adapter means another authority or package is required. Planned means the surface is not part of this release. +This manual is generated from current `main` for the next Cockroach Browser release. Package examples still identify published line 0.5.0-rc.1 where shown; verify npm provenance and the matching tag before production use. Available means implemented in the current source tree, not necessarily published in 0.5.0-rc.1. Adapter means another authority or package is required. Planned means the surface is not implemented here. diff --git a/docs/maqam.md b/docs/maqam.md index ce306cd..280359e 100644 --- a/docs/maqam.md +++ b/docs/maqam.md @@ -27,6 +27,6 @@ Uploads, downloads, clipboard writes, JavaScript, state restore, network interce A session placed behind the Maqam driver must remain host-owned. Do not expose its raw action endpoint or lifecycle methods to the same agent. Maqam governance covers only operations routed through this adapter; trusted-host SDK calls and explicitly enabled raw-action routes remain separate host authority. The browser adapter is an execution boundary, not a second policy system. -## Release status +## Source status -This manual targets Cockroach Browser 0.4.1. Check [the capability matrix](https://cockroachbrowser.com/docs/capabilities/) before relying on a surface. Available means implemented in this release. Adapter means another authority or package is required. Planned means the surface is not part of this release. +This manual is generated from current `main` for the next Cockroach Browser release. Package examples still identify published line 0.5.0-rc.1 where shown; verify npm provenance and the matching tag before production use. Available means implemented in the current source tree, not necessarily published in 0.5.0-rc.1. Adapter means another authority or package is required. Planned means the surface is not implemented here. diff --git a/docs/market-positioning.md b/docs/market-positioning.md new file mode 100644 index 0000000..1bd45d5 --- /dev/null +++ b/docs/market-positioning.md @@ -0,0 +1,199 @@ +# Cockroach Browser market positioning + +**Research date:** 2026-09-03 + +**Purpose:** operational guidance for product, documentation, launch, and sales copy. Pricing and competitor capabilities are time-sensitive and must be rechecked before publication. + +## Category and target users + +**Category:** a governed, local-first, multi-engine browser runtime for AI agents. + +Cockroach Browser is not a new rendering engine and should not be presented as one. It routes work to Chromium, Firefox, WebKit, or an explicitly experimental lightweight provider, then adds capability negotiation, authority controls, resource enforcement, evidence, and host-owned lifecycle management. + +**Primary ICP:** engineers building AI-agent platforms or operational automation that must remain self-hosted, respect data-residency constraints, limit model authority, or produce inspectable evidence. + +**Secondary users:** + +- data extraction and crawling teams whose DOM-oriented jobs are constrained by memory or concurrency; +- QA and developer-tool teams that need full browser fidelity plus a governed agent-facing surface; +- regulated operations teams that require explicit approvals, bounded effects, and evidence retention. + +**Not the initial wedge:** consumer desktop browsing, a turnkey global proxy/CAPTCHA service, or teams whose only requirement is the cheapest hosted Chrome hour. + +## Jobs to be done + +1. Let an agent inspect and operate a website without inheriting ambient profiles, credentials, origins, or machine authority. +2. Select a lightweight DOM lane or a full-fidelity browser lane before launch, with unsupported actions rejected rather than silently downgraded. +3. Reuse Playwright, Puppeteer, CDP, BiDi, WebDriver, MCP, and existing browser knowledge. +4. Enforce finite RSS, CPU, duration, evidence, and action budgets across owned sessions. +5. Produce evidence and receipts that an operator can inspect after an agent acts. +6. Run locally or inside customer-controlled infrastructure with authenticated team access. +7. Supply bounded, cited project context without turning retrieved history into browser authority. + +## Market table stakes + +Official product pages consistently promote the following. Cockroach Browser must make these easy to find, but they are not exclusivity claims: + +- Playwright, Puppeteer, or CDP compatibility; +- reliable session lifecycle and concurrency controls; +- persistent profiles and authenticated state; +- screenshots, PDFs, Markdown, and structured extraction; +- traces, recordings, live inspection, or other debugging evidence; +- MCP and model-provider integration; +- proxies, stealth, CAPTCHA handling, or clear provider integrations; +- cloud deployment, self-hosting, or both; +- one-command setup, working templates, and transparent limits. + +## Competitor matrix + +| Product | Primary promoted value | Delivery | Positioning implication for Cockroach Browser | +| --- | --- | --- | --- | +| [Playwright](https://playwright.dev/) | Chromium, Firefox, and WebKit through one API; auto-waiting, assertions, tracing, code generation, CLI, and MCP | Open-source local/CI framework across TypeScript, Python, Java, and .NET | Three-engine support, testing, and MCP are parity. Lead with the governed layer above Playwright. | +| [Puppeteer](https://pptr.dev/guides/what-is-puppeteer) | Chrome and Firefox automation over CDP or WebDriver BiDi; screenshots, PDFs, tracing, extensions, and SPA automation | Node library maintained by the Chrome Browser Automation team | Raw Puppeteer, CDP, and BiDi compatibility are necessary parity. | +| [Browserbase](https://www.browserbase.com/pricing) and [Stagehand](https://www.stagehand.dev/) | Cloud browser fleets, search/fetch, identity, proxies, CAPTCHA, model gateway, recordings, and self-healing AI primitives | Cloud-first infrastructure; Stagehand can also drive a local browser | Strongest all-in-one commercial competitor. Cockroach Browser is differentiated by customer-owned execution and explicit authority/evidence boundaries, not managed global scale. | +| [Browserless](https://www.browserless.io/) | Drop-in Playwright/Puppeteer, BrowserQL, MCP, profiles, stealth, CAPTCHA, proxies, replays, and live debugging | Shared cloud, private fleet, and self-hosted options | Direct competitor for browser infrastructure. Cockroach Browser needs equally fast onboarding and visible observability. | +| [Steel](https://docs.steel.dev/) | Open-source browser API with sessions, profiles, proxies, CAPTCHA, stealth, credentials, replays, and multi-language SDKs | Managed cloud or self-hosted Docker | Open source, self-hosting, and an agent browser API are parity; enforcement and proof must carry the message. | +| [Lightpanda](https://lightpanda.io/docs/) | Machine-native lightweight engine with CDP, HTTP, MCP, a native agent, and deterministic PandaScript replay | Local binary, package managers, Docker, and cloud | AI-native, MCP, and low-memory claims are not exclusive. Its [published benchmark](https://lightpanda.io/docs/core-concepts/benchmarks) includes crawling, repeated automation, and agent-task evaluation. | +| [Obscura](https://github.com/h4ckf0r0day/obscura) | Rust/V8 lightweight browser with CDP, Playwright/Puppeteer compatibility, stealth, rendering, and MCP | Apache-2.0 binaries, Docker, self-hosting, and a managed offer | Obscura is Cockroach Browser's upstream lightweight engine. Claim the pinned integration, policy, and independent measurement, not invention of the engine. See the [v0.2.1 release](https://github.com/h4ckf0r0day/obscura/releases/tag/v0.2.1). | +| [Cloudflare Browser Run](https://developers.cloudflare.com/browser-run/) | Globally distributed headless Chrome, quick actions, crawling, AI extraction, CDP, Playwright, Puppeteer, Stagehand, MCP, live view, and human handoff | Cloudflare Workers and global edge | Sets the bar for low-friction hosted deployment and scale. It does not replace Cockroach Browser's local multi-engine governance position. | +| [Browser Use](https://github.com/browser-use/browser-use) | Autonomous browser tasks, vision, profiles, custom tools, CLI, skills, MCP, and hosted agents | MIT Python framework plus hosted cloud | A built-in agent loop is parity. Interoperate with this ecosystem rather than claiming agent-loop novelty. | + +## Claim taxonomy + +“Unique within the reviewed set” means that the official surfaces above did not document the same product contract on the research date. It is not a universal, legal, or patent-style claim. + +| Cockroach Browser capability | Classification | Approved interpretation | +| --- | --- | --- | +| Per-action capability manifests across full and lightweight engines, with `supported`, `experimental`, and `unsupported` states and no silent fallback | **Unique within the reviewed set** | A caller can inspect whether the chosen engine supports an action before launch. | +| Origin/effect policy, exact approvals, budgets, receipts, and cited Qarinah context with memory explicitly separated from action authority | **Unique within the reviewed set** | Retrieved context can inform the agent but cannot silently expand its browser authority. | +| Build-linked lightweight proof with raw samples, hashes, a verifier, and a separately reported failing target | **Uncommon** | The scoped 30 MiB result can be recomputed from retained measurements. | +| Continuous owned-process-tree RSS/CPU/duration enforcement, sticky violations, and explicit unverified-termination state | **Uncommon** | Limits are enforced during the session and teardown uncertainty remains visible. | +| One routed contract spanning full Chromium/Firefox/WebKit and an experimental lightweight lane | **Uncommon combination** | Compatible DOM work can use the lightweight lane while visual work uses full browsers. | +| Content-addressed evidence and action receipts linked to sessions | **Uncommon** | Outcomes retain inspectable evidence and causal records. | +| Playwright, Puppeteer, CDP, BiDi, MCP, structured extraction, model gateway, Docker, profiles, authentication, multi-tenancy, and quotas | **Parity** | Present these as compatibility and production readiness, not exclusivity. | +| Mobile WebDriver/Appium adapters | **Uncommon but secondary** | Useful breadth, but not the first market wedge. | + +## Approved and forbidden claims + +### Approved exact claims + +- **Category:** “A governed, local-first, multi-engine browser runtime for AI agents.” +- **Hero:** “Full browsers when fidelity matters. A measured 28.25 MiB non-visual lane when it does not.” +- **Measurement:** “The pinned Obscura 0.2.1 constrained non-visual fixture reached a maximum complete owned-browser-process-tree RSS of 29,622,272 bytes (28.25 MiB) across 20 measured launches and passed the 30 MiB target.” +- **Negative result:** “The separately measured 25 MiB target failed with a 29,679,616-byte maximum.” +- **Scope:** “The Node coordinator was measured separately. The result is not a whole-app, arbitrary-page, rendered-page, or full-browser memory guarantee.” +- **Routing:** “Use the lightweight lane for compatible DOM and JavaScript work, and Chromium, Firefox, or WebKit for full fidelity.” +- **Preflight:** “Unsupported actions are rejected before launch; experimental capabilities require explicit opt-in.” +- **Governance:** “Bounded sessions enforce origin, effect, approval, resource, evidence, and lifecycle controls.” +- **Proof:** “The benchmark retains raw observations, artifact hashes, and a verifier.” + +The canonical measurement and its limitations are in the [proof record](./benchmarks/obscura-non-visual-2026-09-03.md). + +### Forbidden claims + +- “28 MB or less,” because the measured result is 28.25 MiB and MiB is not interchangeable with decimal MB. +- “The only,” “the first,” or “no other browser” for low memory, AI control, MCP, self-hosting, or multi-engine automation. +- “Every browser feature in 28 MB” or any implication that the lightweight engine has full-browser feature parity. +- “Faster,” “cheaper,” or “lighter than Chrome/Lightpanda” without an identical, same-host, pinned comparison. +- “Secure sandbox,” “complete isolation,” or “all egress is contained.” OS/container controls remain necessary for hostile content and uncovered protocols. +- “Works on every site,” “unblockable,” or “bypasses CAPTCHAs.” +- “Renderer disabled.” `rendering: "none"` is Cockroach Browser's visual-action policy, not proof that the upstream binary omitted its renderer. +- Claims that Cockroach Browser operates global regions, residential proxies, CAPTCHA solving, stealth infrastructure, or a hosted fleet when it only exposes adapter contracts. +- Customer-count, adoption, reliability, compliance, or savings claims without corresponding current evidence. + +## Homepage feature order + +1. Category and two-lane hero, with the exact scoped 28.25 MiB badge. +2. A runnable sixty-second quickstart and a **Verify the benchmark** call to action. +3. Lightweight DOM lane versus full Chromium/Firefox/WebKit lane. +4. Capability negotiation and fail-closed preflight. +5. Origins, effects, approvals, finite budgets, and verified teardown. +6. Playwright, Puppeteer, CDP, BiDi, MCP, and SDK compatibility. +7. Evidence, receipts, and cited Qarinah provenance. +8. Local daemon, Docker, authenticated teams, and worker deployment. +9. Concrete use cases: governed operations, agent infrastructure, DOM extraction, and cross-browser QA. +10. Reproducible proof, current limitations, and release status. + +Recommended lead copy: + +> **One governed browser runtime for AI agents.** +> +> Route compatible DOM work through a measured 28.25 MiB Obscura lane, fall back to Chromium, Firefox, or WebKit for full fidelity, and enforce origins, effects, approvals, resources, and evidence before the model acts. + +## Distribution channels + +### Product distribution + +- npm package and CLI; +- signed GitHub releases with checksums, provenance, and an SBOM; +- GHCR and Docker Hub images; +- Homebrew plus Scoop or winget after signed binary releases are repeatable; +- MCP registry listing and one-command skills for Codex, Claude Code, Cursor, and comparable clients; +- runnable TypeScript, Python, and Go quickstarts; +- templates for structured extraction, user-approved form submission, evidence-backed operations, and lightweight-to-full fallback. + +### Audience distribution + +- GitHub releases, Discussions, issues, and a public compatibility board; +- a benchmark page with raw artifacts and exact reproduction commands; +- a concise technical paper plus shorter architecture and benchmark articles; +- Show HN and Product Hunt after installation and proof work end to end on a clean host; +- browser-automation, web-scraping, local-AI, developer-tool, and agent-framework communities; +- integration examples for Browser Use, LangChain, CrewAI, Mastra, n8n, and major MCP clients; +- direct design-partner outreach to agent-infrastructure and regulated-automation teams. + +## Ninety-day launch plan + +### Days 0-30: release truth and activation + +- Reconcile version, capability count, benchmark results, limitations, and release commands across the README, paper, site, and package metadata. +- Make a clean-machine install, first session, benchmark verification, and uninstall repeatable. +- Resolve the native Windows full-browser launch issue or make the platform limitation prominent. +- Produce signed packages and container artifacts only after the complete release gate passes. +- Publish four runnable examples covering the primary jobs to be done. +- Measure time-to-first-success and record every failed onboarding step. + +### Days 31-60: comparative evidence and integrations + +- Run same-host, pinned comparisons with identical tasks and non-misleading RSS/PSS labels. +- Add a real-page compatibility corpus, concurrency runs, a long soak, fault injection, and an agent-task evaluation. +- Ship verified MCP/skill setup paths for leading coding agents. +- Publish integration examples for at least three agent frameworks. +- Recruit 5-10 design partners; report them as targets or participants only with explicit permission, never as customers by implication. + +### Days 61-90: public launch and iteration + +- Publish the comprehensive paper, raw results, reproduction instructions, and known limitations together. +- Cut a release candidate, repeat clean-host and cross-platform verification, then publish the stable release if gates pass. +- Launch through developer channels with the scoped proof and runnable demo as the central story. +- Maintain a public compatibility/evidence changelog and respond rapidly to reproducible failures. +- Keep a hosted service as a waitlist or roadmap item until an actual operated service, support boundary, and measured cost model exist. + +Operational success measures should include clean-install success rate, median time to first verified session, benchmark reproduction rate, weekly active projects, four-week retained projects, task success by engine, orphan-process rate, resource-limit enforcement rate, and issue time to resolution. + +## Pricing anchors - time-sensitive + +These are market context observed on 2026-09-03, not evergreen facts. Recheck each linked page immediately before external use. + +- [Browserbase](https://www.browserbase.com/pricing): Free; Developer at $20/month with 25 concurrent browsers and 100 included browser hours; Startup at $99/month with 100 concurrent browsers and 500 hours; custom Scale. +- [Browserless](https://www.browserless.io/pricing): Free with 1,000 units and two concurrent browsers; the annual-billing display showed $25/month for 20,000 units and 10 concurrency, $140 for 180,000 and 40, and $350 for 500,000 and 100. Its [unit documentation](https://docs.browserless.io/overview/unit-consumption) defines browser time as one unit per 30 seconds, plus separate proxy and CAPTCHA consumption. +- [Steel](https://steel.dev/): Launch at $0 plus usage with $30 in one-time credits; Scale at $250/month plus usage with $100 in monthly credits; enterprise custom. +- [Cloudflare Browser Run](https://developers.cloudflare.com/browser-run/pricing/): Workers Free included 10 minutes per day and three concurrent browsers; Workers Paid included 10 browser hours per month, then $0.09 per additional hour, with separate concurrency pricing beyond the included monthly average. + +Do not set or advertise hosted Cockroach Browser pricing until measured cost of service, support obligations, tenancy isolation, and capacity limits exist. The near-term commercial value is governance, private deployment, evidence, and support rather than undifferentiated browser hours. Because the repository is AGPL-3.0-or-later, decide explicitly whether the intended adoption model remains pure copyleft or adds a separately reviewed commercial license; do not leave enterprise users to guess. + +## Evidence roadmap + +The current lightweight proof is useful but deliberately narrow. The external evidence package should grow in this order: + +1. **Release identity:** exact source, build, harness, binary, artifact, machine, and dependency identities; signed artifacts and SBOM. +2. **Correctness:** per-engine action-contract tests, parser/property tests, protocol conformance, and deterministic fixtures. +3. **Comparable performance:** same-host browser-tree RSS/PSS, CPU, latency, throughput, and coordinator overhead against exact pinned baselines. +4. **Compatibility:** at least 100 documented public pages plus deterministic SPA, frame, form, download, and authentication fixtures. +5. **Reliability:** a 24-hour soak, thousands of bounded launches, crash/hang classification, memory-growth slope, and zero unexplained owned-process orphans. +6. **Concurrency:** 1, 10, 25, 50, and 100-session results with admission behavior and resource saturation disclosed. +7. **Agent quality:** task success, cost, latency, step count, and failure taxonomy on a public benchmark and a deterministic in-repository suite. +8. **Security negatives:** SSRF, redirects, DNS rebinding, WebSockets, service workers, private networks, prompt injection, secret redaction, token scope, path traversal, persistence failure, and teardown failure. +9. **Platforms:** Windows, Linux, macOS, Docker, and supported CPU architectures, with failures published alongside passes. + +The competitive evidence standard is visible in the [Obscura benchmark repository](https://github.com/h4ckf0r0day/obscura-benchmark), which includes WPT, an obstacle course, a real-world corpus, reliability, and concurrency tracks; the [Lightpanda benchmark](https://lightpanda.io/docs/core-concepts/benchmarks), which includes crawling, repeated automation, and agent tasks; and [Stagehand Evals](https://www.stagehand.dev/evals), which reports accuracy, cost, and speed. Until comparable Cockroach Browser tracks exist, describe the current result as a rigorous scoped resource proof, not overall performance leadership. diff --git a/docs/mcp.md b/docs/mcp.md index d305309..208cb7b 100644 --- a/docs/mcp.md +++ b/docs/mcp.md @@ -2,7 +2,7 @@ Give an MCP client observations and proposals, not browser ownership. -The native stdio server exposes health, capabilities, sessions, snapshots, paired capture, bounded network observations, audits, and canonical action proposals. It does not expose raw profile management or direct mutation authority. +The native stdio server exposes health, capability and engine manifests, action preflight, sessions, snapshots, paired capture, bounded network observations, audits, and canonical action proposals. It does not expose raw profile management or direct mutation authority. Public manual: https://cockroachbrowser.com/docs/mcp/ @@ -15,7 +15,7 @@ Start the authenticated daemon first. Load its token into the MCP process throug "mcpServers": { "cockroach-browser": { "command": "npx", - "args": ["-y", "cockroach-browser@0.4.1", "mcp"], + "args": ["-y", "cockroach-browser@0.5.0-rc.1", "mcp"], "env": { "COCKROACH_BROWSER_URL": "http://127.0.0.1:43110", "COCKROACH_BROWSER_TOKEN": "" @@ -27,7 +27,11 @@ Start the authenticated daemon first. Load its token into the MCP process throug ## Observation-first tools -The MCP surface provides browser_capabilities, browser_health, browser_sessions, browser_snapshot, browser_audit, browser_capture, browser_network, and browser_propose_action. Capture and network tools return bounded read evidence. A proposal returns canonical action material for a governed dispatcher and does not execute it. +The MCP surface provides browser_capabilities, browser_engines, browser_engine_preflight, browser_health, browser_sessions, browser_snapshot, browser_audit, browser_capture, browser_network, and browser_propose_action. Capture and network tools return bounded read evidence. A proposal returns canonical action material for a governed dispatcher and does not execute it. + +## Preflight an exact engine and action set + +browser_engines returns the same per-engine manifest as authenticated GET /v1/engines without launching a browser. browser_engine_preflight accepts an engine, one or more exact action kinds, and optional allowExperimental. It returns required capabilities, their supported, experimental, or unsupported states, acceptance decisions, and the unmet set. Experimental capabilities fail unless explicitly accepted; unsupported capabilities always fail. Neither tool creates a session or widens host policy. ## Keep lifecycle authority outside the model @@ -38,6 +42,6 @@ Session creation, profile import, login, secret resolution, remote binding, and MCP proposes. Maqam evaluates policy, binds an approval to the exact operation, dispatches through the driver, rejects replay, and records governance evidence. -## Release status +## Source status -This manual targets Cockroach Browser 0.4.1. Check [the capability matrix](https://cockroachbrowser.com/docs/capabilities/) before relying on a surface. Available means implemented in this release. Adapter means another authority or package is required. Planned means the surface is not part of this release. +This manual is generated from current `main` for the next Cockroach Browser release. Package examples still identify published line 0.5.0-rc.1 where shown; verify npm provenance and the matching tag before production use. Available means implemented in the current source tree, not necessarily published in 0.5.0-rc.1. Adapter means another authority or package is required. Planned means the surface is not implemented here. diff --git a/docs/network.md b/docs/network.md index 0f24b46..c888d76 100644 --- a/docs/network.md +++ b/docs/network.md @@ -1,14 +1,16 @@ -# Network boundary +# Network controls -The browser may render a page. It does not inherit your whole network. +Route browser HTTP(S) deliberately, and add deployment isolation for hostile content. -Every navigation and subresource request is checked against the session's explicit origin policy. Public adapters block loopback and private-network targets by default. +Bounded contexts check intercepted HTTP(S) navigation and subresource requests against the session origin policy. Public adapters block loopback and private-network targets by default, but application routing is not a complete protocol sandbox. Public manual: https://cockroachbrowser.com/docs/network/ ## Start from an allowlist -List exact HTTPS origins whenever possible. Redirects and subresources are re-evaluated, so an admitted start URL cannot silently widen the session. Denied origins take precedence. +List exact HTTPS origins whenever possible. Redirects and intercepted HTTP(S) subresources are re-evaluated, so an admitted start URL cannot silently widen those routed requests. Denied origins take precedence. Runtime-owned full-engine bounded contexts also validate WebSocket handshakes and block service workers in balanced and lean profiles. Balanced preserves ordinary images, media, and fonts, while lean additionally blocks those three asset classes. + +These controls do not contain WebRTC/STUN/TURN/UDP, WebTransport/QUIC, attached CDP, lightweight-engine WebSockets, or the unrestricted raw Playwright and Puppeteer operator lane. Use an OS, container, firewall, or equivalent egress boundary when hostile content requires complete protocol isolation. ## Private networks require an owned deployment decision @@ -67,6 +69,6 @@ npx cockroach-browser network export \ The daemon binds to localhost by default. Remote binding requires an explicit setting, TLS certificate and key, bearer authentication, and a CORS allowlist. Public unauthenticated server binding is not supported. -## Release status +## Source status -This manual targets Cockroach Browser 0.4.1. Check [the capability matrix](https://cockroachbrowser.com/docs/capabilities/) before relying on a surface. Available means implemented in this release. Adapter means another authority or package is required. Planned means the surface is not part of this release. +This manual is generated from current `main` for the next Cockroach Browser release. Package examples still identify published line 0.5.0-rc.1 where shown; verify npm provenance and the matching tag before production use. Available means implemented in the current source tree, not necessarily published in 0.5.0-rc.1. Adapter means another authority or package is required. Planned means the surface is not implemented here. diff --git a/docs/operator-install.md b/docs/operator-install.md index 302485a..0ab961a 100644 --- a/docs/operator-install.md +++ b/docs/operator-install.md @@ -63,6 +63,6 @@ cockroach-browser service status The generated service cannot add remote binding, raw-action routes, session host configuration, profile discovery, or privilege escalation. Those remain separate trusted-host decisions. Use Maqam for consequential browser actions and retain the bearer token in the owner-scoped data directory rather than shell history. -## Release status +## Source status -This manual targets Cockroach Browser 0.4.1. Check [the capability matrix](https://cockroachbrowser.com/docs/capabilities/) before relying on a surface. Available means implemented in this release. Adapter means another authority or package is required. Planned means the surface is not part of this release. +This manual is generated from current `main` for the next Cockroach Browser release. Package examples still identify published line 0.5.0-rc.1 where shown; verify npm provenance and the matching tag before production use. Available means implemented in the current source tree, not necessarily published in 0.5.0-rc.1. Adapter means another authority or package is required. Planned means the surface is not implemented here. diff --git a/docs/operator-runtime.md b/docs/operator-runtime.md index 1225bef..7d09cc5 100644 --- a/docs/operator-runtime.md +++ b/docs/operator-runtime.md @@ -8,7 +8,7 @@ Public manual: https://cockroachbrowser.com/docs/operator-runtime/ ## Inspect browser and daemon state -browser discover reports installed compatible browsers. doctor verifies Node, Chromium, the data root, and local service readiness. The authenticated daemon publishes /v1/health, /v1/openapi.json, and Prometheus text at /v1/metrics. +browser discover reports installed compatible browsers. doctor verifies Node, Chromium, the data root, and local service readiness. Any authenticated identity can read the relative-base /v1/openapi.json route index. Global /v1/health, Prometheus text at /v1/metrics, and /v1/evidence/verify require the daemon administrator token; actor tokens remain limited to actor-scoped records. The OpenAPI document indexes every implemented daemon method/path with a unique operationId and an explicit success response; it is route discovery, not an authorization grant. ## Follow the activity stream @@ -30,7 +30,15 @@ cockroach-browser session graph --id "$SESSION_ID" \ ## Share control without sharing profiles -TeamSessionStore persists one owner plus revocable viewer and operator grants. Viewers can inspect; operators can use explicitly enabled action routes; owners manage access and closure. Grant generations and revocations are durable, while raw cookies and browser profiles never enter the access record. +TeamSessionStore persists one owner plus revocable viewer and operator grants. Viewers can inspect; operators can use explicitly enabled action routes; owners manage access and closure. A mutation replaces the in-memory ownership map only after durable persistence succeeds, so a failed write leaves the prior access state intact. Raw cookies and browser profiles never enter the access record. + +Configuring actor-scoped bearer tokens without TeamSessionStore is a startup error. The administrator token and every actor token must be unique or startup fails with AUTH_TOKEN_COLLISION. Actor-token session creation is also disabled until the host provides actorSessionFactory. The callback must construct authoritative session input from individually reviewed request fields rather than spreading caller JSON. The server replaces the actor with the authenticated identity and claims the session in the store before returning it; claim failure closes the new session. + +## Bound concurrent session admission + +BrowserServerOptions.maxSessions defaults to 32 non-closed sessions for the daemon; maxSessionsPerActor defaults to 8 non-closed sessions assigned to one actor. Pending creates reserve their slots through a concurrency-safe serialized admission step, so parallel requests cannot oversubscribe either ceiling. + +Ceiling rejections return HTTP 429 with stable codes: SESSION_GLOBAL_LIMIT_EXCEEDED or SESSION_ACTOR_LIMIT_EXCEEDED. Closed sessions do not count. maxRequestBytes defaults to 1,048,576 bytes and accepts only integers from 1,024 through 16,777,216 bytes. ## Route across authenticated workers @@ -41,6 +49,6 @@ BrowserWorkerPool checks authenticated daemon health, capacity, weight, and expl cache.clear, console.clear, and network.clear are explicit policy-evaluated actions. They clear only the authorized session's runtime state and produce receipts; they do not erase evidence already committed to the evidence ledger. -## Release status +## Source status -This manual targets Cockroach Browser 0.4.1. Check [the capability matrix](https://cockroachbrowser.com/docs/capabilities/) before relying on a surface. Available means implemented in this release. Adapter means another authority or package is required. Planned means the surface is not part of this release. +This manual is generated from current `main` for the next Cockroach Browser release. Package examples still identify published line 0.5.0-rc.1 where shown; verify npm provenance and the matching tag before production use. Available means implemented in the current source tree, not necessarily published in 0.5.0-rc.1. Adapter means another authority or package is required. Planned means the surface is not implemented here. diff --git a/docs/productloop.md b/docs/productloop.md index 0660bbe..0610580 100644 --- a/docs/productloop.md +++ b/docs/productloop.md @@ -23,6 +23,6 @@ Browser evidence proves what Chromium observed and executed. Maqam proves the po The ProductLoop integration in 0.3.0 is a structural capability snapshot for a host-owned adapter, not direct connector registration. The browser runtime, SDK, CLI, HTTP API, MCP server, evidence chain, and local dashboard are implemented in the package. -## Release status +## Source status -This manual targets Cockroach Browser 0.4.1. Check [the capability matrix](https://cockroachbrowser.com/docs/capabilities/) before relying on a surface. Available means implemented in this release. Adapter means another authority or package is required. Planned means the surface is not part of this release. +This manual is generated from current `main` for the next Cockroach Browser release. Package examples still identify published line 0.5.0-rc.1 where shown; verify npm provenance and the matching tag before production use. Available means implemented in the current source tree, not necessarily published in 0.5.0-rc.1. Adapter means another authority or package is required. Planned means the surface is not implemented here. diff --git a/docs/qarinah.md b/docs/qarinah.md index 7383635..2a4a2df 100644 --- a/docs/qarinah.md +++ b/docs/qarinah.md @@ -2,13 +2,15 @@ Turn browser outcomes into cited memory without turning memory into a dispatcher. -Qarinah can record sanitized browser outcomes, source URLs, receipt hashes, and evidence IDs so later agents retrieve compact, cited project context. +The optional Qarinah adapters emit versioned metadata-only outcomes to a host sink and can retrieve a bounded cited history pack for the built-in agent without granting browser authority. Public manual: https://cockroachbrowser.com/docs/qarinah/ ## Record metadata, not browser secrets -The adapter removes cookies, storage values, form values, secret references, and hidden reasoning. It records the canonical input digest, output digest, browser receipt hash, evidence IDs, source URL, and bounded descriptive metadata as cited context links. The host supplies the persistence callback supported by its installed Qarinah release. +The recorder emits cockroach.browser-memory.v2 with event type, session ID, optional actor, a SHA-256 purposeDigest instead of raw purpose, timestamp, optional input and output digests, evidence IDs, optional receipt hash, and filtered metadata. The metadata allowlist is action, status, input/output digest, receipt hash/ID, evidence IDs, policy digest, mode, effect, risk, and completion time. Values are depth-, length-, and count-bounded; authorization, cookie, credential, password, passphrase, secret, token, storage, form-value, and API-key fields are removed recursively. + +The envelope contains no sourceUrl, raw page content, browser-profile content, hidden reasoning, or raw session purpose. The host supplies the persistence callback supported by its installed Qarinah release. BrowserRuntime.contextRecorderTimeoutMs bounds the recorder wait, defaults to 1,000 ms, and accepts integers from 1 through 120,000 ms; rejection or timeout is reported operationally without replacing browser work. ## Keep memory read-only with respect to the browser @@ -18,11 +20,26 @@ Qarinah never creates a session, changes policy, approves an action, or dispatch A read outcome needs citations and receipt metadata, not a synthetic mutation chain. For consequential mutations, a host may connect public evidence, browser observation, Qarinah memory, Maqam decision, approved tool execution, observed result, and permanent receipt when every stage exists. The integration does not invent missing stages or require one cross-system transaction. -## Cross-tool context +## Retrieve bounded context for the browser agent + +createQarinahAgentContextProvider forwards the current session ID, agent task as query, an agent-selected character ceiling, a configured result limit from 1 to 128, and optional cancellation to a host-owned retrieveBrowserContext callback. The callback returns a summary plus citation IDs with optional receipt hashes and evidence IDs. + +The browser agent validates citation counts and identifier sizes, preserves citation anchors when truncating, and serializes the bounded pack as an untrusted user-role observation behind a trusted system boundary. Qarinah content never becomes a system instruction or authority. Runtime origin, action, effect, approval, and resource policy still decides every browser operation. + +``` +const contextProvider = createQarinahAgentContextProvider( + hostProvidedQarinahMemorySource, + { limit: 24 } +); -The same cited memory pack can be consumed by coding agents and CLIs that support the Qarinah integration. Authority remains scoped by workspace and source provenance. +const agent = new BrowserAgent({ + runtime, + gateway, + contextProvider +}); +``` -## Release status +## Source status -This manual targets Cockroach Browser 0.4.1. Check [the capability matrix](https://cockroachbrowser.com/docs/capabilities/) before relying on a surface. Available means implemented in this release. Adapter means another authority or package is required. Planned means the surface is not part of this release. +This manual is generated from current `main` for the next Cockroach Browser release. Package examples still identify published line 0.5.0-rc.1 where shown; verify npm provenance and the matching tag before production use. Available means implemented in the current source tree, not necessarily published in 0.5.0-rc.1. Adapter means another authority or package is required. Planned means the surface is not implemented here. diff --git a/docs/resource-governance.md b/docs/resource-governance.md index aa300ed..d719f89 100644 --- a/docs/resource-governance.md +++ b/docs/resource-governance.md @@ -1,6 +1,6 @@ # Browser resource governance -Cockroach Browser measures the process tree it launches, exposes the result through the session API, CLI, dashboard, and Prometheus, and fails closed when a periodically observed ceiling is exceeded. +Cockroach Browser periodically measures PID-observable runtime-owned browser-server and lightweight process trees, exposes results through the session API, CLI, dashboard, and Prometheus, and makes the session terminal when a sampled ceiling is exceeded or telemetry disappears. Attached CDP and persistent headed contexts explicitly report process enforcement as unavailable. ## Configure a session @@ -20,7 +20,7 @@ Cockroach Browser measures the process tree it launches, exposes the result thro } ``` -`balanced` preserves ordinary images, media, fonts, and service workers. `lean` blocks those four expensive surfaces while retaining HTML, CSS, JavaScript, forms, frames, semantic snapshots, screenshots of the resulting page, and policy-evaluated interaction. Use balanced for visual fidelity and lean for DOM-oriented automation. WebKit can send video range-request headers to an origin before Playwright aborts the response, so the benchmark separates origin-offered bytes from browser-received resource bytes. +`balanced` preserves ordinary images, media, and fonts. `lean` additionally blocks those three expensive asset classes while retaining HTML, CSS, JavaScript, forms, frames, semantic snapshots, screenshots of the resulting page, and policy-evaluated interaction. Bounded contexts block service workers in both profiles so that surface cannot bypass intercepted HTTP(S) origin routing; raw Playwright and Puppeteer stay on the unrestricted operator lane. This routing is not a complete protocol sandbox and does not contain WebRTC/STUN/TURN/UDP, WebTransport/QUIC, attached CDP, or lightweight-engine WebSockets. Use balanced for visual fidelity and lean for DOM-oriented automation. WebKit can send video range-request headers to an origin before Playwright aborts the response, so the benchmark separates origin-offered bytes from browser-received resource bytes. ## Inspect usage @@ -28,7 +28,7 @@ Cockroach Browser measures the process tree it launches, exposes the result thro cockroach-browser session resources --id SESSION_ID --token-file .cockroach-browser/auth-token ``` -The authenticated `GET /v1/sessions/{id}/resources` route returns current and peak aggregate RSS, cumulative CPU time, process count, configured ceilings, sample time, ownership, and limit state. `/v1/metrics` exports label-free aggregate gauges so session IDs, actors, purposes, and failure reasons do not become Prometheus labels. +The authenticated `GET /v1/sessions/{id}/resources` route returns current and peak aggregate RSS, cumulative CPU time, process count, configured ceilings, sample time, ownership, and limit state for an authorized session. Administrator-only `/v1/metrics` exports label-free aggregate gauges so session IDs, actors, purposes, and failure reasons do not become Prometheus labels. An attached CDP browser is customer-owned. Its full process tree cannot be attributed or terminated safely, so Cockroach Browser reports resource data as unavailable instead of reporting a false zero. Persistent headed sessions are also marked unavailable until Playwright exposes a portable owned-process handle for that launch mode. @@ -42,9 +42,25 @@ The ignored JSON artifact records browser-tree RSS/CPU/process count, Node coord Compare base and candidate commits on the same idle machine. Treat a change as a regression only when both the relative and absolute threshold are crossed: 20% and 32 MiB for median peak RSS, 25% and 250 ms for wall time, or 25% and 500 ms for CPU time. Use at least 20 measured iterations for a release decision. -## Why 20 MiB is not a viable browser target +An independent lightweight CDP binary has a separate, stricter harness: -On the current Windows development host, a fresh Node process alone is about 32 MiB RSS. A real one-page headless browser process tree is substantially larger. The automated resource E2E test deliberately configures 20 MiB and verifies that session creation returns `PROCESS_RSS_BUDGET_EXCEEDED`; it never claims the browser ran inside that amount. +```powershell +npm run benchmark:lightweight -- --implementation obscura --executable C:/reviewed/obscura.exe --sha256 EXPECTED_SHA256 --resource-profile constrained --warmup 1 --iterations 20 --resource-samples 10 --resource-sample-interval-ms 25 --target-mib 30 --output output/resource-benchmarks/obscura.json +``` + +The `--sha256` pin is optional for local experimentation and recommended for reviewed or repeatable evidence; when supplied, the harness verifies it before execution. The harness starts a new owned loopback process for each run, exercises CDP connection, JavaScript, DOM, input, and click behavior, verifies that the visual-action policy rejects screenshot capture before dispatch, confirms owned-process exit and bounded CDP transport disposal during teardown, and retains complete browser-tree RSS/CPU/process observations from spawn through shutdown. Each measured launch contains ten explicit steady-state samples at 25 ms intervals plus boundary and continuous observations. Node coordinator memory is disclosed separately. Exit code `2` means the declared target or required conformance set was not met. A result is never relabelled as passing by excluding a child process, raising the target after measurement, or counting a killed browser as success. + +The [September 3, 2026 canonical proof record](./benchmarks/obscura-non-visual-2026-09-03.md) contains the pinned executable digest, source/build/harness identities, complete methodology, verdicts, distributions, coordinator disclosure, and hashes of the tracked raw JSON artifacts. It records one warmup plus 20 measured launches per target. The [September 2 record](./benchmarks/obscura-no-render-2026-09-02.md) is retained only as superseded historical evidence. + +## What a low-memory target means + +The automated resource E2E test deliberately configures 20 MiB and verifies that session creation returns `PROCESS_RSS_BUDGET_EXCEEDED`; it never claims the browser ran inside that amount. + +The September 3 release-candidate proof used the pinned Obscura 0.2.1 binary, one non-visual data-URL fixture, the constrained profile, one warmup, and 20 measured launches per target. The 30 MiB target passed with a maximum complete owned-browser-tree RSS of 29,622,272 bytes; median was 29,347,840 bytes, p95 was 29,569,024 bytes, and minimum was 28,893,184 bytes. The 25 MiB target failed with a maximum of 29,679,616 bytes; median was 29,323,264 bytes, p95 was 29,634,560 bytes, and minimum was 28,831,744 bytes. Required CDP connection, JavaScript, DOM, forms, screenshot preflight denial, and teardown checks passed in every measured launch. + +Coordinator RSS is disclosed separately and is not hidden inside the browser-tree number. The whole application is therefore not demonstrated at or below 30 MiB. The `rendering: "none"` selection is a Cockroach Browser visual-action preflight policy and does not assert that the selected engine exposes or received a renderer-disable switch. A 30 MiB result may be reported only with the named binary, digest, host, workload, policy, process-tree definition, run count, sampling method, and conformance result. It cannot be generalized to arbitrary or rendered pages, Chromium, Firefox, WebKit, Lightpanda, the Node coordinator, or the full Cockroach Browser process set; full engines use far more memory. + +Published upstream numbers for Obscura and Lightpanda use different versions, hosts, workloads, concurrency, and memory definitions. Treat them as upstream evidence, not as interchangeable Cockroach Browser measurements. Reasonable starting ceilings for the pinned local fixture are 512 MiB for lean Chromium/WebKit, 768 MiB for lean Firefox, and 1 GiB for balanced sessions. Replace these starting points with a ceiling derived from the deployment's measured p95: round up `max(p95 × 1.25, p95 + 64 MiB)` to the next 64 MiB. @@ -52,8 +68,9 @@ Reasonable starting ceilings for the pinned local fixture are 512 MiB for lean C - RSS is the conservative sum of resident memory across the owned process tree. Shared pages may be counted more than once. - CPU is cumulative process CPU time, not utilization percentage or energy. -- Windows defaults to a 10-second sample interval because starting PowerShell/CIM for every sample is costly; POSIX defaults to five seconds. Near-simultaneous sessions share one cached host process inventory, and fresh per-session samples are reused at action boundaries. -- Polling can miss short spikes and CPU from a renderer that exits between samples. A detected breach marks the session failed and closes its owned browser server. +- `resourceSampleIntervalMs` accepts integers from 250 through 60,000 ms. Windows defaults to ten seconds because starting PowerShell/CIM for every sample is costly; POSIX defaults to five seconds. Near-simultaneous sessions share one cached host process inventory, and fresh per-session samples are reused at action boundaries. +- Polling can miss short spikes and CPU from a renderer that exits between samples. A detected breach, duration expiry, or telemetry loss becomes a sticky terminal session error and starts process-first teardown independently of the next action. +- If owned-process termination cannot be verified, the runtime returns `TERMINATION_UNVERIFIED` and retains the failed session and any persistent-profile writer lock for an explicit close retry. - This is fail-closed runtime telemetry, not a real-time kernel boundary. Use Linux cgroup/container limits or Windows Job Objects when a hard memory/CPU boundary is required. The included Compose service applies a hard container memory/CPU/PID envelope. Tune `COCKROACH_BROWSER_CONTAINER_MEMORY_LIMIT`, `COCKROACH_BROWSER_CONTAINER_CPU_LIMIT`, and `COCKROACH_BROWSER_CONTAINER_PID_LIMIT` from measured workloads; do not use a 20 MiB container limit for a real browser. diff --git a/docs/security.md b/docs/security.md index 4a25703..a144fad 100644 --- a/docs/security.md +++ b/docs/security.md @@ -12,11 +12,15 @@ Assume page content is untrusted, agent input may be wrong, downloaded files may ## Challenges move to an authorized operator path -The runtime detects login, consent, CAPTCHA, and access challenges, pauses the automated action path, records evidence, and waits for a human or an explicitly configured resolver operating with the target owner's authorization. The challenge.resolve action is classified as a critical execute effect and requires exact approval by default. Its page-less callback receives only bounded challenge metadata, never cookies, storage, credentials, a Playwright page, or raw browser control. The runtime independently checks the page after the handoff and keeps the session paused when the challenge remains. +The runtime detects login, consent, CAPTCHA, and access challenges, pauses the automated action path, records evidence, and waits for a human or an explicitly configured resolver operating with the target owner's authorization. + +The challenge.resolve action is classified as a critical execute effect and requires exact approval by default. Its page-less callback receives only bounded challenge metadata, never cookies, storage, credentials, a Playwright page, or raw browser control. The runtime independently checks the page after the handoff and keeps the session paused when the challenge remains. ## Governed high-authority controls -Cockroach Browser does not silently expose CAPTCHA or access-control bypass, covert stealth or cloaking, ambient browser cookies or profiles, or public unauthenticated server binding.Use operator-authorized challenge handoff, deterministic compatibility emulation, explicit runtime-owned profiles or encrypted state import, reviewed browser providers, and authenticated loopback or TLS transport. Maqam-ready exact approval keeps consequential operations bound to their reviewed input. +Cockroach Browser does not silently expose CAPTCHA or access-control bypass, covert stealth or cloaking, ambient browser cookies or profiles, or public unauthenticated server binding. + +Use operator-authorized challenge handoff, deterministic compatibility emulation, explicit runtime-owned profiles or encrypted state import, reviewed browser providers, and authenticated loopback or TLS transport. Maqam-ready exact approval keeps consequential operations bound to their reviewed input. ## Exact approval for consequential actions @@ -34,6 +38,6 @@ Use the Maqam adapter for writes, execute effects, uploads, downloads, credentia - Review third-party page terms and obtain authorization for the workflow. -## Release status +## Source status -This manual targets Cockroach Browser 0.4.1. Check [the capability matrix](https://cockroachbrowser.com/docs/capabilities/) before relying on a surface. Available means implemented in this release. Adapter means another authority or package is required. Planned means the surface is not part of this release. +This manual is generated from current `main` for the next Cockroach Browser release. Package examples still identify published line 0.5.0-rc.1 where shown; verify npm provenance and the matching tag before production use. Available means implemented in the current source tree, not necessarily published in 0.5.0-rc.1. Adapter means another authority or package is required. Planned means the surface is not implemented here. diff --git a/docs/sessions.md b/docs/sessions.md index c7c078a..fc0019e 100644 --- a/docs/sessions.md +++ b/docs/sessions.md @@ -2,22 +2,64 @@ A browser session is borrowed authority, not a reusable credential bucket. -Every session has an owner, purpose, mode, origin boundary, action boundary, effect boundary, and finite budget. Profiles are named, isolated, and imported only by an authorized operator. +Every bounded-runtime session has an owner, purpose, mode, HTTP(S) origin policy, action policy, effect policy, and finite application budget. Profiles are named, isolated, and imported only by an authorized operator. Public manual: https://cockroachbrowser.com/docs/sessions/ ## Choose the browser connection -Launch bundled Chromium, discover a reviewed system installation, supply a compatible executable, or attach to a user-selected CDP endpoint. cockroach-browser browser discover reports Chrome, Edge, Brave, and Chromium candidates across Windows, macOS, Linux, ARM64, and Raspberry Pi hosts. Discovery never imports an ambient browser profile. CDP attachment remains explicit: the host names the endpoint and accepts responsibility for that browser.- Bundled: package-managed Chromium. +Launch bundled Chromium, discover a reviewed system installation, supply a compatible executable, attach to a user-selected CDP endpoint, or explicitly start a separately installed Obscura engine. cockroach-browser browser discover reports Chrome, Edge, Brave, and Chromium candidates across Windows, macOS, Linux, ARM64, and Raspberry Pi hosts. Discovery never imports an ambient browser profile. CDP attachment remains explicit: the host names the endpoint and accepts responsibility for that browser. + +- Bundled: package-managed Chromium. - System: one reviewed installed browser channel. - Custom: one explicit compatible executable and bounded arguments. - CDP: attach to an explicitly selected debugging endpoint. +- Lightweight Obscura: start one explicit runtime-owned loopback process, with experimental opt-in and an optional reviewed SHA-256 pin. +- Lightpanda: inspect its manifest and machine preflight, but managed launch currently fails closed pending a complete engine- or OS-level egress boundary. - Extensions: load reviewed unpacked directories in an isolated headed context. ``` cockroach-browser browser discover ``` +## Negotiate the engine before launch + +GET /v1/engines and BrowserClient.engines() return machine-readable manifests for Chromium, Firefox, WebKit, Obscura, and Lightpanda. Add ?engine=obscura or pass one engine ID to filter the result. MCP clients can use browser_engines and browser_engine_preflight. Preflight maps exact action kinds to required engine capabilities without launching a browser: supported work is admitted, experimental work requires explicit opt-in, and unsupported work always fails. The runtime.owned_launch entry is supported for the three full engines, experimental for Obscura, and unsupported for Lightpanda. + +A successful preflight reports compatibility only. It creates no session, grants no origin or credential authority, and cannot prove how an arbitrary page will behave. + +``` +console.log(await browser.engines()); +console.log(await browser.engines("obscura")); +``` + +## Use a lightweight lane only for compatible work + +The managed Obscura provider is an experimental bounded-runtime lane, not a replacement for the full browser engines. Cockroach Browser never downloads its binary automatically and never accepts caller-supplied launch flags for this lane. With rendering: "none", Cockroach Browser denies visual actions during capability preflight; this policy does not assert that the selected Obscura binary disabled or omitted its renderer. Compatible non-visual sessions can navigate, execute JavaScript under policy, inspect DOM state, activate HTML elements, fill text controls, and perform bounded structured extraction. Screenshots, PDFs, video, visual pointer behavior, extensions, and persistent profiles require a deliberate full-engine session. Lightpanda configuration, manifest inspection, and machine preflight exist on current main, but managed launch fails closed pending a complete engine- or OS-level egress boundary. + +The frozen September 3, 2026 RC1 proof used pinned Obscura 0.2.1, one warmup, and 20 measured launches per target. The 30 MiB target passed across 478 retained observations: 28,893,184-byte minimum, 29,347,840-byte median, 29,569,024-byte p95, and 29,622,272-byte maximum complete owned-browser-tree RSS, or 28.25 MiB. The 25 MiB target failed across 480 retained observations: 28,831,744-byte minimum, 29,323,264-byte median, 29,634,560-byte p95, and 29,679,616-byte maximum, or 28.30 MiB. Every required capability check passed across both targets and all 958 observations are retained. The proof binds source tree fb0c4597e39f319dd9b6f3bab02777c395e9d8d84906981bf939a39b470e7279, runtime build 6738efa4000ba482db83c9dc95ba2f21caed31de96f30dcd342e5dc722d86025, and harness 08a5294f2d446765f712b93c9bfaaca010b1d043ded638d1f4f57b5038c97e86. Coordinator RSS was measured separately. This is not a whole-app, arbitrary-page, rendered-page, or full-browser memory guarantee. + +``` +const session = await browser.createSession({ + purpose: "Extract one reviewed documentation page", + engine: "chromium", + mode: "headless", + browserProvider: { + kind: "lightweight", + implementation: "obscura", + executablePath: "C:/reviewed/obscura.exe", + expectedSha256: "sha256:", + rendering: "none", + resourceProfile: "constrained", + allowExperimentalCapabilities: true + }, + policy: { + allowedOrigins: ["https://example.com"], + allowedActions: ["navigate", "snapshot", "extract.structured"] + } +}); +``` + ## Create an explicit persistent profile Persistent profiles preserve cookies, permissions, extension state, and browser storage across headed sessions in a runtime-owned user-data directory. They are never found by scanning a user's normal Chrome or Brave profile. A profile has one active writer, cannot be combined with remote CDP attachment or imported storage state, and can be archived through an exact recoverable operation. @@ -65,13 +107,13 @@ Clipboard reads and writes are separate actions with bounded text output and sec ## Budget every session -The default budget limits actions, session duration, tabs, download bytes, upload bytes, snapshot characters, retained history, network rules, static intercepted responses, and evidence bytes. Narrow these limits for each workflow. A budget is a hard stop, not a billing estimate. +The default budget limits actions, session duration, tabs, download bytes, upload bytes, snapshot characters, retained history, network rules, static intercepted responses, and evidence bytes. Narrow these limits for each workflow. Action and byte ceilings are application-enforced; browser RSS and CPU are periodically sampled telemetry rather than a real-time kernel boundary. ## Close deliberately -Closing a session releases tabs, browser context, traces, and runtime state. Persist a profile only when the operator requested it. Qarinah records cited outcomes and Maqam records governance receipts, but neither receives raw profile material. +A successful close terminates the runtime-owned process before publishing terminal lifecycle telemetry, then releases tabs, browser context, traces, and runtime state. If owned-process termination cannot be verified, the session remains failed and retained with TERMINATION_UNVERIFIED, including any persistent-profile writer lock, so an explicit close can retry. Persist a profile only when the operator requested it. Qarinah records cited outcomes and Maqam records governance receipts, but neither receives raw profile material. -## Release status +## Source status -This manual targets Cockroach Browser 0.4.1. Check [the capability matrix](https://cockroachbrowser.com/docs/capabilities/) before relying on a surface. Available means implemented in this release. Adapter means another authority or package is required. Planned means the surface is not part of this release. +This manual is generated from current `main` for the next Cockroach Browser release. Package examples still identify published line 0.5.0-rc.1 where shown; verify npm provenance and the matching tag before production use. Available means implemented in the current source tree, not necessarily published in 0.5.0-rc.1. Adapter means another authority or package is required. Planned means the surface is not implemented here. diff --git a/docs/webhooks.md b/docs/webhooks.md index 76fde4c..fd02bc5 100644 --- a/docs/webhooks.md +++ b/docs/webhooks.md @@ -8,7 +8,9 @@ Public manual: https://cockroachbrowser.com/docs/webhooks/ ## Configure one endpoint and an opaque key reference -SignedWebhookDispatcher implements BrowserEventPublisher. Attach it to BrowserRuntime to queue session, action, challenge, and evidence events. Endpoint configuration stores only an opaque ref: value. The host-owned resolver returns the actual key during delivery, and the key is never persisted in configuration, queue entries, dead letters, or receipts. Endpoint URLs must use HTTPS and cannot contain credentials, a query string, or a fragment. Configuration resolves the hostname once to reject an invalid destination early; every delivery resolves and validates it again. +SignedWebhookDispatcher implements BrowserEventPublisher. Attach it to BrowserRuntime to queue session, action, challenge, and evidence events. Endpoint configuration stores only an opaque ref: value. The host-owned resolver returns the actual key during delivery, and the key is never persisted in configuration, queue entries, dead letters, or receipts. + +Endpoint URLs must use HTTPS and cannot contain credentials, a query string, or a fragment. Configuration resolves the hostname once to reject an invalid destination early; every delivery resolves and validates it again. ``` import { @@ -54,14 +56,17 @@ await webhooks.upsertEndpoint({ const browser = new BrowserRuntime({ root: ".cockroach-browser/runtime", - eventPublisher: webhooks + eventPublisher: webhooks, + eventPublisherTimeoutMs: 1_000 }); await browser.initialize(); ``` ## Keep publish and drain as separate authorities -publish() validates and sanitizes the event, applies endpoint event filters, enforces the payload and storage ceilings, and atomically appends local queue records. It does not resolve DNS, call the secret resolver, or use the network. drain() is the operator-controlled boundary that revalidates DNS, resolves the referenced key, signs the canonical body, and sends a finite serial batch. Run the drain from a deployment-owned scheduler or worker. An endpoint failure never changes the result of the browser action that produced the lifecycle event. +publish() validates and sanitizes the event, applies endpoint event filters, enforces the payload and storage ceilings, and atomically appends local queue records. It does not resolve DNS, call the secret resolver, or use the network. drain() is the operator-controlled boundary that revalidates DNS, resolves the referenced key, signs the canonical body, and sends a finite serial batch. + +BrowserRuntime.eventPublisherTimeoutMs limits how long lifecycle delivery may delay browser work. It defaults to 1,000 ms and accepts integers from 1 through 120,000 ms. Run the drain from a deployment-owned scheduler or worker. A publisher rejection, timeout, or endpoint failure never replaces the result of the browser action that produced the lifecycle event. ``` // Publishing is local-only. Draining owns DNS, key resolution, and HTTPS. @@ -85,7 +90,9 @@ Endpoint filters can select browser.session.created, browser.session.closed, bro ## Verify the signature before parsing or dispatching -Each request includes the event type, stable delivery ID, timestamp, 128-bit nonce, key ID, and v1= signature. The signature is HMAC-SHA256 over the domain string cockroach-browser.webhook.v1, timestamp, nonce, delivery ID, key ID, and exact body, separated by newlines. verifyWebhookSignature() checks syntax, timestamp tolerance, key binding, and the signature with a timing-safe comparison. The built-in WebhookReplayGuard is a bounded in-process nonce guard and fails closed when full. A multi-process or restart-safe receiver should place the same delivery ID and nonce checks in its durable store. +Each request includes the event type, stable delivery ID, timestamp, 128-bit nonce, key ID, and v1= signature. The signature is HMAC-SHA256 over the domain string cockroach-browser.webhook.v1, timestamp, nonce, delivery ID, key ID, and exact body, separated by newlines. verifyWebhookSignature() checks syntax, timestamp tolerance, key binding, and the signature with a timing-safe comparison. + +The built-in WebhookReplayGuard is a bounded in-process nonce guard and fails closed when full. A multi-process or restart-safe receiver should place the same delivery ID and nonce checks in its durable store. ``` import { @@ -123,29 +130,39 @@ export function verifyIncomingWebhook( ## Deduplicate stable delivery IDs -The normal retry path keeps one deterministic delivery ID for an event and endpoint while creating a fresh timestamp and nonce on each request. Verify the request, begin a receiver transaction, return success immediately when that delivery ID was already committed, otherwise apply the event and commit the ID with the result. This makes at-least-once attempts safe at the receiver. A manual retryDeadLetter() intentionally creates a new delivery ID. Keep the original event ID in application-level reconciliation when an operator needs to connect both attempts. +The normal retry path keeps one deterministic delivery ID for an event and endpoint while creating a fresh timestamp and nonce on each request. Verify the request, begin a receiver transaction, return success immediately when that delivery ID was already committed, otherwise apply the event and commit the ID with the result. This makes at-least-once attempts safe at the receiver. + +A manual retryDeadLetter() intentionally creates a new delivery ID. Keep the original event ID in application-level reconciliation when an operator needs to connect both attempts. ## Retry transient failures and inspect terminal outcomes -HTTP 408, 425, 429, and 5xx responses, plus bounded timeout, DNS, secret-timeout, and transport failures, retry with exponential jitter while attempts and the drain deadline remain. Retry-After is honored up to 30 seconds. Other non-2xx responses become dead letters. Terminal delivered and dead-letter receipts include the attempt log, response status or normalized error, body digest, prior receipt hash, and current receipt hash. Use retryDeadLetter(id) only after fixing the endpoint or key reference. Use purgeDeadLetter(id) for an explicit retention decision, not as an automatic cleanup path. +HTTP 408, 425, 429, and 5xx responses, plus bounded timeout, DNS, secret-timeout, and transport failures, retry with exponential jitter while attempts and the drain deadline remain. Retry-After is honored up to 30 seconds. Other non-2xx responses become dead letters. Terminal delivered and dead-letter receipts include the attempt log, response status or normalized error, body digest, prior receipt hash, and current receipt hash. + +Use retryDeadLetter(id) only after fixing the endpoint or key reference. Use purgeDeadLetter(id) for an explicit retention decision, not as an automatic cleanup path. ## Recover interrupted local writes and verify integrity -Initialization recovers interrupted fan-out, dead-letter retry, and terminal receipt transactions from deployment-owned files. An interrupted network attempt is recorded as failed and is retried only when its configured budget remains. Conflicting recovery state fails closed for operator review. verify() recomputes every terminal delivery digest and the single linked receipt chain, detecting altered records, duplicate hashes, forks, cycles, unlinked receipts, and a mismatched persisted head. Initialization refuses to continue when this integrity check fails. +Initialization recovers interrupted fan-out, dead-letter retry, and terminal receipt transactions from deployment-owned files. An interrupted network attempt is recorded as failed and is retried only when its configured budget remains. Conflicting recovery state fails closed for operator review. + +verify() recomputes every terminal delivery digest and the single linked receipt chain, detecting altered records, duplicate hashes, forks, cycles, unlinked receipts, and a mismatched persisted head. Initialization refuses to continue when this integrity check fails. ## Keep every queue finite -Defaults are a 64 KiB payload, 10,000 queued deliveries, 256 MiB of webhook storage, three attempts, a five-second endpoint timeout, 25 items per drain, a 60-second drain deadline, and a 4 KiB response ceiling. Configurable limits remain bounded: payloads from 1 KiB to 1 MiB, queues from 1 to 100,000 entries, storage from 1 MiB to 10 GiB, attempts from one to five, endpoint timeouts from 250 ms to 30 seconds, drain batches from one to 1,000, and drain deadlines from one second to ten minutes. health() reports queued records, terminal receipts, dead letters, used bytes, and configured queue and storage ceilings. Diagnostics can observe queued, delivered, dead-letter, capacity, and recovered states but cannot alter delivery. +Defaults are a 64 KiB payload, 10,000 queued deliveries, 256 MiB of webhook storage, three attempts, a five-second endpoint timeout, 25 items per drain, a 60-second drain deadline, and a 4 KiB response ceiling. Configurable limits remain bounded: payloads from 1 KiB to 1 MiB, queues from 1 to 100,000 entries, storage from 1 MiB to 10 GiB, attempts from one to five, endpoint timeouts from 250 ms to 30 seconds, drain batches from one to 1,000, and drain deadlines from one second to ten minutes. + +health() reports queued records, terminal receipts, dead letters, used bytes, and configured queue and storage ceilings. Diagnostics can observe queued, delivered, dead-letter, capacity, and recovered states but cannot alter delivery. ## Preserve the network and challenge boundary -Every attempt admits only public HTTPS destinations, pins the connection to a validated public address, preserves TLS hostname verification, rejects private, loopback, translated, and mixed public/private DNS results, and never follows redirects. Response bodies are discarded after a 4 KiB ceiling. The dispatcher does not attach browser cookies, profile state, ambient credentials, or URL tokens. A webhook is an outbound integration, not a browser challenge solver. Redirects and access-control responses are rejected or dead-lettered according to the retry rules. The dispatcher does not bypass login, consent, CAPTCHA, rate limits, or endpoint authorization. +Every attempt admits only public HTTPS destinations, pins the connection to a validated public address, preserves TLS hostname verification, rejects private, loopback, translated, and mixed public/private DNS results, and never follows redirects. Response bodies are discarded after a 4 KiB ceiling. The dispatcher does not attach browser cookies, profile state, ambient credentials, or URL tokens. + +A webhook is an outbound integration, not a browser challenge solver. Redirects and access-control responses are rejected or dead-lettered according to the retry rules. The dispatcher does not bypass login, consent, CAPTCHA, rate limits, or endpoint authorization. ## Understand the durability promise This is a local durable at-least-once outbox for one deployment-owned filesystem. It persists selected events before delivery, recovers interrupted local transactions, retries within finite policy, and records terminal outcomes. It is not a distributed queue, a cross-host consensus system, or an exactly-once transport. Receiver-side deduplication by stable delivery ID is required. -## Release status +## Source status -This manual targets Cockroach Browser 0.4.1. Check [the capability matrix](https://cockroachbrowser.com/docs/capabilities/) before relying on a surface. Available means implemented in this release. Adapter means another authority or package is required. Planned means the surface is not part of this release. +This manual is generated from current `main` for the next Cockroach Browser release. Package examples still identify published line 0.5.0-rc.1 where shown; verify npm provenance and the matching tag before production use. Available means implemented in the current source tree, not necessarily published in 0.5.0-rc.1. Adapter means another authority or package is required. Planned means the surface is not implemented here. diff --git a/docs/whitepaper.md b/docs/whitepaper.md index 06b41d9..e8bd3f9 100644 --- a/docs/whitepaper.md +++ b/docs/whitepaper.md @@ -1,307 +1,778 @@ -# Cockroach Browser: A Local-First Browser Runtime for AI Agents +# Cockroach Browser: A Governed Multi-Engine Runtime for AI Agents **Author:** Ajnas N B -**Paper version:** 1.1 -**Implementation:** Cockroach Browser 0.3.0 (`v0.3.0`, commit `76c1095a6066bfa0242d8ea4413b0643133c977c`) -**Date:** August 2026 +**Paper version:** 1.2 +**Implementation:** Cockroach Browser next release candidate 0.5.0-rc.1 +**Date:** 2026-09-03 **Software license:** AGPL-3.0-or-later **Document license:** Creative Commons Attribution 4.0 International **Concept DOI:** [10.5281/zenodo.21701791](https://doi.org/10.5281/zenodo.21701791) -**Status:** Implementation-backed technical white paper for Cockroach Browser 0.3.0. The paper has not undergone independent peer review or independent security certification. +**Status:** Implementation-backed release-candidate paper. This paper has not undergone independent peer review, independent security certification, or a third-party comparative benchmark. ## Abstract -AI agents can use a browser to inspect dynamic applications, fill forms, download files, capture evidence, and complete operational workflows. A conventional automation process, however, often inherits more authority than the task requires: ambient browser profiles, persistent cookies, arbitrary origins, unrestricted JavaScript, local files, broad network reach, or an unauthenticated remote control port. +AI agents need browser capability because important work happens inside dynamic, stateful web applications. The same capability also creates an unusually broad authority boundary: a browser can read authenticated data, mutate external systems, upload or download files, execute page code, contact network destinations, and retain credentials. A useful agent browser therefore needs more than a headless launch command. It needs an explicit answer to which engine is being used, which operations are supported, which origins and effects are authorized, how much resource use is allowed, what evidence is retained, and what happens when enforcement or teardown cannot be verified. -Cockroach Browser is a local-first TypeScript and Chromium runtime that separates browser capability from ambient machine authority. A host creates an explicit session with a purpose, allowed origins, allowed actions, allowed effects, and finite budgets. The runtime then provides semantic page references, browser interactions, screenshots, PDFs, traces, network observations, audits, and hash-linked receipts within that session. Login, consent, CAPTCHA, and access challenges pause for human handling or a separately authorized resolver; they are not bypassed. +Cockroach Browser is an open-source, local-first, multi-engine browser runtime for AI agents. It routes full-fidelity work to Chromium, Firefox, or WebKit and can route compatible non-visual DOM and JavaScript work to an explicitly installed experimental lightweight engine. A machine-readable engine manifest reports supported, experimental, and unsupported capabilities before launch. The governed runtime binds each session to a purpose, origins, actions, effects, approvals, finite budgets, authenticated identity, evidence, and lifecycle controls. Raw Playwright, Puppeteer, CDP, WebDriver BiDi, WebDriver, and Appium surfaces remain available to trusted operators without being misrepresented as bounded agent actions. -Version 0.3.0 exposes an embedded SDK, an authenticated loopback daemon, a typed client, a command-line interface, an observation-first MCP server, Docker deployment, a local dashboard, per-user service definitions, authenticated jobs and workers, team-scoped session roles, and adapters for Maqam, Qarinah, Cockroach Crawler, and ProductLoop OS. The tagged source-derived capability registry contains 94 entries: 88 directly available surfaces and 6 host-backed adapters. This registry count is a software inventory, not a performance or security score. +The release-candidate architecture also includes bounded structured extraction, a finite-step model-directed agent, an OpenAI-compatible model gateway, cited Qarinah context that cannot grant browser authority, exact Maqam approval integration, team ownership and roles, admission limits, process-tree resource monitoring, content-addressed evidence, and hash-linked receipts. The source-derived capability registry contains 130 entries: 119 available surfaces, 11 host-backed adapters, and no planned entries. The bounded action contract contains 66 action kinds. These are implementation inventories, not performance or security scores. -## 1. Problem +This paper reports one canonical lightweight measurement without generalizing it. On a pinned Windows host, the reviewed Obscura 0.2.1 binary passed a 30 MiB target with a maximum complete owned-browser-process-tree RSS of 29,622,272 bytes, or 28.25 MiB, across 20 measured launches. A separately executed 25 MiB target failed with a maximum of 29,679,616 bytes. The Node coordinator was measured separately. Neither result is a whole-application, arbitrary-page, rendered-page, persistent-session, attached-session, or full-browser memory guarantee. -Browser automation creates a difficult authority boundary. A browser can see and change real systems, but a model should not silently decide which profiles, credentials, origins, local resources, or network destinations become available. +## 1. Problem and category -Three common designs are incomplete: +A browser is both an interpreter of untrusted content and a controller of consequential external systems. Traditional automation libraries expose powerful objects, but the host often has to build policy, identity, budgeting, evidence, teardown verification, and agent-safe semantics around them. A general desktop browser adds ambient profiles and machine state. A hosted browser can be useful, but it moves execution, credentials, and evidence into another operator's service boundary. A narrow HTTP scraper uses fewer resources, but cannot complete stateful or interactive workflows. -1. A general desktop browser gives an agent broad ambient authority. -2. A remote browser service centralizes credentials and evidence in another operator's infrastructure. -3. A narrow scraping interface cannot complete stateful, interactive workflows. +Cockroach Browser occupies a specific category: a governed, local-first, multi-engine browser runtime for AI agents. It is not a new rendering engine. It composes established full-browser engines, experimental independent lightweight engines, automation protocols, policy, resource enforcement, and evidence into one routed contract. -Cockroach Browser treats browser access as a session-scoped capability. The browser remains useful, but the host owns the authority envelope. +The central design principle is separation. Browser capability is separated from ambient machine authority. Governed actions are separated from unrestricted operator automation. Memory is separated from authorization. Browser evidence is separated from governance approval. Lightweight efficiency is separated from full-engine fidelity. Application telemetry is separated from kernel-enforced resource isolation. -## 2. Design goals +## 2. Contributions -The runtime is designed around seven goals: +The 0.5.0-rc.1 development tree contributes the following implementation-backed elements. -1. Real Chromium rendering and interaction. -2. Explicit authority for every session. -3. Finite resource and evidence budgets. -4. Stable observations through snapshot-scoped semantic references. -5. Paired visual and structural evidence. -6. Authenticated local and remote control surfaces. -7. Separate governance, memory, crawling, and orchestration ledgers connected by stable identifiers. +- One explicit engine-selection contract spanning Chromium, Firefox, WebKit, Obscura, and Lightpanda manifests. +- Per-engine capability assessments with supported, experimental, and unsupported states. +- Fail-closed preflight for unsupported actions and explicit opt-in for experimental capabilities. +- No silent engine substitution inside an authorized session. +- A bounded runtime with explicit origins, action kinds, effects, exact approvals, finite budgets, and challenge pauses. +- A separate unrestricted operator layer that re-exports pinned Playwright and Puppeteer APIs and supplies raw CDP, BiDi, WebDriver, Appium, testing, code generation, and fleet adapters. +- Snapshot-scoped semantic references and fresh observations after agent actions. +- Host-side structured extraction from untrusted HTML with aggregate and category-specific limits. +- A finite-step agent loop with schema validation, tool-result bounds, deadlines, context compaction, evidence anchors, and cited history. +- A Qarinah record and retrieval boundary that preserves provenance while refusing to turn memory into authority. +- Team ownership, owner/operator/viewer roles, authenticated actor tokens, durable access updates, and concurrency-safe session admission. +- Continuous PID-observable owned-process-tree RSS, CPU-time, duration, and process-count monitoring with sticky terminal failures. +- Content-addressed evidence and hash-linked action receipts. +- A reproducible, artifact-hashed lightweight resource proof that publishes both a passing target and a failing target. -The system is not designed to bypass access controls, CAPTCHAs, paywalls, rate limits, robots policies, or site authorization. +These contributions describe this implementation. They do not establish novelty in the legal, patent, or universal market sense. -## 3. System model +## 3. Explicit nonclaims -The main actors are: +This paper does not claim that Cockroach Browser is the only or first browser for agents, the fastest browser, the lightest browser, or a replacement for every browser-automation product. -- **Deployment owner:** installs the runtime and owns configuration, secrets, storage, and network exposure. -- **Host application:** creates sessions, supplies explicit authority, and decides which surfaces an agent may reach. -- **Agent:** consumes observations and proposes or requests allowed actions. -- **Browser runtime:** enforces the session policy, drives Chromium, and records evidence. -- **Human reviewer:** handles login, consent, challenges, and consequential approvals when required. -- **Maqam:** evaluates policy, binds exact approvals, dispatches governed actions, and records governance receipts. -- **Qarinah:** preserves cited project memory without becoming browser authority. -- **Cockroach Crawler:** maps and extracts bounded public web content before selected pages require a browser. +It does not claim that every browser feature fits in 28 MB, that the whole application uses 30 MiB or less, or that Chromium, Firefox, WebKit, Lightpanda, the Node coordinator, arbitrary pages, rendered pages, attached sessions, or persistent profiles meet the measured Obscura result. -## 4. Architecture +It does not claim that a renderer was disabled. The measured rendering policy is visual-actions-denied: Cockroach Browser rejected screenshot capture during capability preflight. That policy does not prove that the upstream Obscura binary omitted its renderer or received a renderer-disable switch. -Cockroach Browser has four layers: +It does not claim complete network isolation, a secure sandbox, containment of every browser protocol, universal site compatibility, CAPTCHA bypass, access-control bypass, stealth infrastructure, residential proxies, global regions, an operated hosted fleet, independent security certification, regulatory compliance, customer adoption, production reliability, or cost savings. -### 4.1 Runtime +It does not claim comparative memory, speed, cost, accuracy, or throughput leadership. No same-host, same-workload, pinned cross-product comparison is reported here. -`BrowserRuntime` owns Chromium lifecycle, authorized sessions, action classification, policy checks, budgets, evidence records, receipts, queues, and integrations. +## 4. Actors, assets, and trust boundaries -Version 0.3.0 adds explicit browser-provider selection, runtime-owned persistent profiles, bounded navigation graphs, lifecycle activity records, authenticated jobs, worker routing, and team-scoped access. These surfaces reuse the same session authority model; they do not grant ambient profile, credential, origin, or machine authority. +The deployment owner installs the software and controls configuration, storage, process identity, network exposure, browser binaries, containers, and secret management. -### 4.2 Control surfaces +The host application creates sessions and defines authoritative inputs. It decides which origins, actions, effects, profiles, credentials, providers, and resource ceilings are exposed to an agent. -The same runtime can be reached through: +The agent consumes bounded observations and proposes or requests actions. It does not own session policy, daemon identity, profile discovery, remote binding, or secret resolution. -- an embedded TypeScript SDK -- an authenticated HTTP daemon -- a typed daemon client -- a command-line interface -- an observation-first MCP server -- a local operator dashboard -- an authenticated job surface with read-only automatic retry limits -- authenticated browser workers with capacity and tag-aware routing +The browser runtime evaluates policy, drives the selected engine, enforces application-level budgets, records evidence, and owns the lifecycle of managed processes. -### 4.3 Evidence +The human reviewer handles login, consent, access challenges, and consequential approvals when the workflow requires human authority. -Evidence includes semantic snapshots, screenshots, paired captures, PDFs, Playwright traces, HAR files, video, console records, network metadata, downloads, audit results, annotations, and action receipts. Records are content-addressed and linked to the session that produced them. +Maqam can evaluate policy, bind an approval to an exact canonical operation, dispatch through a registered driver, reject replay or changed input, and record a governance receipt. -### 4.4 Integrations +Qarinah can record bounded metadata-only browser outcomes and retrieve cited project context. It cannot create sessions, change policy, approve actions, or dispatch browser operations. -Adapters connect browser evidence to Maqam policy and approvals, Qarinah project memory, Cockroach Crawler handoffs, and ProductLoop capability descriptions. The adapters do not collapse these systems into one authority or one ledger. +Cockroach Crawler can map and extract bounded public web content before selected pages are handed to a browser for rendering, state, interaction, or evidence. -## 5. Authorized sessions +External engine and fleet providers remain distinct trust boundaries. Cockroach Browser reports their declared capabilities but does not turn an external service contract into a local security guarantee. -Every session states: +Page content, model output, downloaded files, retrieved memory, remote endpoints, and imported browser state are untrusted inputs. Host configuration, policy, approved secrets, and authenticated identity belong on the trusted side of the boundary. -- purpose -- start URL -- allowed origins -- allowed actions -- allowed effects -- actions that require approval -- action, duration, tab, and evidence budgets +## 5. Architectural overview -The runtime requires at least one explicit allowed origin. Private and loopback destinations are denied unless the deployment owner opts in. Redirects are revalidated. Host-controlled executable paths, CDP endpoints, proxy settings, headers, profile secrets, and remote listeners remain unavailable unless the host explicitly exposes them. +Cockroach Browser has two authority planes and two execution lanes. -Named profiles are isolated. Storage state can be imported or exported only through explicit host operations. Runtime-owned persistent profiles require explicit preparation, use a single-writer lock, and cannot be combined with CDP attachment or imported storage state. The runtime does not discover ambient browser profiles or cookies. +The governed plane exposes 66 bounded action kinds through the embedded runtime, authenticated daemon, typed client, CLI, MCP proposal surface, model-directed agent, jobs, and authorized workers. Every operation remains subordinate to session policy and budgets. -## 6. Semantic observations and interaction +The operator plane exposes exact upstream or protocol-level automation for trusted code. It includes pinned Playwright Core, Puppeteer Core, Playwright Test, code generation, CDP, WebDriver BiDi, WebDriver/Appium transport, and fleet adapters. This plane is intentionally powerful. It does not inherit bounded-runtime policy merely because it ships in the same package. -A snapshot converts the current page into bounded text, structure, metadata, and semantic references. Each reference belongs to the observed page revision. A later action cannot silently reuse a stale reference after the page changes. +The full-fidelity lane uses Chromium, Firefox, or WebKit through Playwright. It is intended for rendering, visual evidence, complex interaction, browser state, downloads and uploads, frames, Shadow DOM, traces, HAR, video, and cross-engine validation. -Implemented interactions include navigation, tabs, popups, click and double-click, forms, keyboard and mouse input, drag and drop, bounded scroll, waits, same-origin frames, open Shadow DOM, dialogs, uploads, downloads, extraction, screenshots, PDFs, tracing, policy-gated JavaScript, bounded element inspection, deterministic emulation controls, and ordered action batches. Every attempted batch step receives its own policy decision and receipt. +The lightweight lane uses an explicitly installed and reviewed independent engine when the requested work matches its manifest. The current managed lightweight provider is experimental Obscura over a runtime-owned loopback CDP process. It targets compatible navigation, JavaScript, DOM inspection, bounded structured extraction, HTML element activation, and text-control value assignment. It is not treated as a transparent substitute for a full browser. -Actions are classified by effect and risk before dispatch. A session may permit read observations while requiring an exact Maqam approval for a write, upload, download, dialog acceptance, or arbitrary evaluation. +The main data flow is: -## 7. Evidence and receipts +```text +host policy and identity + | + v +engine manifest and action preflight + | + v +authorized session and finite budgets + | + v +browser action -> observation -> evidence -> receipt + | + +-> optional exact Maqam approval + | + +-> bounded Qarinah outcome and cited context +``` + +The architecture chooses an engine before executing work. If a lightweight session cannot admit a requested action, the caller receives an unmet capability result. Using a full engine requires a deliberate new or separately selected full-engine session; the runtime does not silently swap engines inside the governed session. + +## 6. Engine capability matrix + +The engine matrix is machine-readable and available without launching a browser. Each capability has a state, a note, and an engine identity. A successful preflight means the declared capability set is compatible with the requested actions. It does not create a session, grant an origin, supply credentials, or prove that a specific page will behave correctly. + +### 6.1 Chromium + +- Distribution and bounded integration: bundled Playwright engine, supported. +- Managed owned launch: supported. +- Playwright: supported. +- Puppeteer Core and CDP: supported on the operator surface. +- WebDriver BiDi: experimental through an operator-owned endpoint, not a managed bounded launch. +- Navigation, JavaScript, DOM, forms, same-origin frames, open Shadow DOM, CORS behavior, reviewed emulation, cookies, uploads, downloads, screenshots, video, traces, and runtime-owned persistent profiles: supported. +- PDF generation: supported in the bounded runtime. +- Reviewed unpacked extensions: supported in headed Chromium profiles. + +### 6.2 Firefox + +- Distribution and bounded integration: bundled Playwright engine, supported. +- Managed owned launch and Playwright: supported. +- Puppeteer and BiDi: experimental operator surfaces where upstream support applies. +- CDP: unsupported because this Firefox integration is not a CDP engine. +- Navigation, JavaScript, DOM, forms, same-origin frames, open Shadow DOM, reviewed emulation, cookies, uploads, downloads, screenshots, video, traces, and runtime-owned persistent profiles: supported. +- Bounded PDF generation: unsupported. +- The reviewed unpacked-extension launcher: unsupported because it is Chromium-specific. + +### 6.3 WebKit + +- Distribution and bounded integration: bundled Playwright engine, supported. +- Managed owned launch and Playwright: supported. +- Puppeteer, CDP, and managed BiDi: unsupported for this route. +- Navigation, JavaScript, DOM, forms, same-origin frames, open Shadow DOM, reviewed emulation, cookies, uploads, downloads, screenshots, video, traces, and runtime-owned persistent profiles: supported. +- Bounded PDF generation and the reviewed unpacked-extension launcher: unsupported. +- WebKit is useful for WebKit-engine behavior, but it is not a claim of exact Safari parity on every platform. + +### 6.4 Obscura + +- Distribution: external binary selected by the host. +- Maturity: experimental. +- Managed owned launch: experimental and allowed only for an explicitly installed, opted-in, conformance-tested binary. +- CDP, Playwright-over-CDP, Puppeteer-over-CDP, navigation, JavaScript, DOM, forms, frames, Shadow DOM, CORS behavior, emulation, interception, cookies, uploads, downloads, screenshots, and PDF: experimental and dependent on the selected build. +- Video, Playwright trace artifacts, persistent profiles, and reviewed unpacked extensions: unsupported by the current managed contract. +- Non-visual actionability means DOM activation and value assignment, not visual pointer fidelity. +- The canonical proof in this paper applies only to Obscura 0.2.1 with the recorded digest and fixture. + +### 6.5 Lightpanda + +- Distribution: external. +- Maturity: experimental manifest and preflight. +- Managed owned launch: unsupported in the current release candidate. +- CDP client compatibility is represented for planning and conformance work. +- The runtime fails closed because page-route interception does not establish a complete boundary for WebSockets, workers, WebRTC, WebTransport, and other egress. +- Enabling managed Lightpanda execution requires an engine-level or operating-system-level deny-by-default network boundary plus regression evidence. + +### 6.6 Operator-supplied browsers and devices + +- Safari on macOS can be reached through an operator-supplied W3C WebDriver endpoint. +- Android Chrome and WebView can be reached through W3C WebDriver or Appium. +- iOS Safari and WebView can be reached through W3C WebDriver or Appium. +- Remote browser farms can be represented through an authenticated provider that declares engines, regions, capacity, time-to-live, proxy classes, challenge modes, and live-view behavior. +- These are adapters. Cockroach Browser does not bundle a macOS Safari host, mobile simulator, emulator, device lab, global region network, proxy fleet, CAPTCHA solver, or live-view service. + +## 7. Authorized sessions + +Every governed session states a purpose, start URL, allowed origins, allowed action kinds, allowed effects, actions requiring approval, and finite budgets. The runtime requires at least one explicit allowed origin. Redirects and intercepted subresources are re-evaluated. Denied origins take precedence. + +Private and loopback destinations are denied unless the deployment owner explicitly permits them for the workflow. Executable paths, CDP endpoints, launch flags, proxy credentials, extra headers, profile material, and remote listeners remain host-controlled inputs. -Browser output is useful only when a later reviewer can connect it to the session, input, policy, and observed result. +Named profiles are isolated. Storage state can be imported or exported only through explicit host operations. Runtime-owned persistent profiles require preparation and a single-writer lock. They cannot be combined with CDP attachment or imported storage state. The runtime does not discover ambient desktop profiles or cookies. -Each action receipt records: +The default budget model covers actions, session duration, tabs, owned-process RSS, owned-process CPU time, downloads, uploads, snapshots, retained history, network entries, clipboard bytes, saved states, network rules, static fulfill bytes, intercepted bytes, and evidence bytes. A host should narrow every ceiling to the task rather than treating defaults as a security policy. -- canonical action material and digest -- policy digest -- URL before and after -- result status -- evidence identifiers -- previous receipt hash +Login, consent, CAPTCHA, and access challenges pause the automated path. Challenge resolution is a critical execute effect requiring exact approval by default. An external resolver receives bounded challenge metadata, not raw browser control, cookies, storage, credentials, or the Playwright page. The runtime independently inspects the page after handoff and keeps the session paused when the challenge remains. -The linked receipt chain exposes missing, reordered, or modified records. Evidence verification recomputes content digests without replaying the browser session. +## 8. Action contract and semantic observations -Paired capture records a screenshot and semantic snapshot from the same reviewed page state. Temporary annotations can label selected references without changing application data. +The bounded action registry contains 66 action kinds. It covers navigation, tabs, pointer and keyboard input, forms, files, JavaScript under policy, structured inspection, emulation, cache and record maintenance, challenge handoff, paired capture, annotation, clipboard, network records and routes, storage state, cookies, page storage, screenshots, PDF, traces, extraction, and session tab locks. -The activity ledger and navigation graph are bounded derived views. They make session progress inspectable without turning a browser observation into a claim that page content is factually correct. Browser evidence proves what the configured runtime recorded, not the truth of an external statement. +Actions are classified by effect and risk before dispatch. Effects distinguish read, write, execute, upload, download, and credential-bearing operations. A session can admit observations while requiring a Maqam approval for an exact write, upload, download, credential use, arbitrary evaluation, or challenge resolution. -## 8. Challenge handling +A semantic snapshot converts the current page into bounded text, structure, metadata, and stable-looking references scoped to the observed page revision. Those references are not ambient selectors. A page change invalidates stale references so an action cannot silently target a different element after navigation or mutation. -The runtime detects login, consent, CAPTCHA, and access challenges. Detection pauses the automated path and records a challenge state. A human can complete the required step in a headed session, or an explicitly authorized resolver can handle a supported challenge under host policy. +An ordered action batch does not bypass governance. Each attempted step receives its own validation, policy decision, execution status, and receipt. -The runtime does not claim CAPTCHA bypass, stealth evasion, credential discovery, access-control bypass, or authorization circumvention. +The full-engine bounded runtime includes navigation, back, forward, reload, tabs and popups, click and double click, fill and type, keyboard and mouse operations, drag and drop, waits, same-origin frames, open Shadow DOM, dialogs, uploads, downloads, screenshots, Chromium PDF, traces, HAR, video, console and network records, deterministic emulation controls, cookies and storage, state checkpoints, audits, annotations, and visual comparison where the selected engine supports them. -## 9. Network observations +Advanced unrestricted objects remain on the operator layer. Arbitrary handles, raw workers, every protocol command, extensions beyond the reviewed path, heap snapshots, CPU profiles, screencasting, and unrestricted routing are not relabeled as safe agent tools. -The runtime can record bounded network metadata and export redacted observations. Session policy controls permitted origins and routing behavior. +## 9. Bounded structured extraction -Network interception is limited to policy-bounded request blocking or bounded static fulfillment. Webhook delivery uses public HTTPS destinations, revalidates DNS, rejects private or mixed-address results, preserves TLS hostname verification, and does not follow redirects. +Structured extraction runs through a canonical host-side parser based on Parse5. Page-provided HTML is treated as untrusted input and is not asked to serialize the final evidence object. The extractor emits bounded combinations of visible text, sanitized HTML, Markdown, links, metadata, JSON-LD, and tables. -## 10. MCP +Traversal is iterative and has a hard depth guard of 512. The limits registry caps total extracted characters, category-specific characters, link count, metadata items, JSON-LD items and characters, table count, rows, columns, item size, and DOM nodes. The maximum aggregate content ceiling is 2,000,000 characters and the maximum DOM-node ceiling is 200,000. Lower defaults apply unless the host deliberately raises a field within the registry ceiling. -The native stdio MCP server is observation-first. It exposes: +The runtime, MCP validator, schema, and tests use the same limits registry. Unknown limit fields fail validation. A category limit cannot exceed the aggregate session ceiling. URL output is normalized through safe URL handling rather than copied as trusted instructions. -- `browser_capabilities` -- `browser_health` -- `browser_sessions` -- `browser_snapshot` -- `browser_audit` -- `browser_capture` -- `browser_network` -- `browser_propose_action` +Structured extraction reduces prompt volume for compatible work. It does not prove page truth, sanitize a downloaded document for arbitrary execution, or make an experimental engine equivalent to a full renderer. -`browser_propose_action` returns canonical action material and an input digest. It does not execute the action. Session creation, profile import, login, secret resolution, remote binding, and mutation authority stay with the host. +## 10. The AI agent boundary -## 11. Maqam governance +The built-in BrowserAgent is optional. A host can use its own planner and call the same governed runtime. -For consequential actions, Cockroach Browser can produce a proposal for Maqam. Maqam evaluates policy, binds an approval to the exact operation, dispatches through its registered driver, rejects replay or changed input, and records a governance receipt. +The built-in loop accepts one schema-valid tool action at a time. It rejects unknown action kinds, invalid payloads, multiple tool calls in one turn, and mixed action-and-finish output. Completion is exclusive: an assistant turn cannot both execute a browser action and declare the task finished. -Browser evidence proves what Chromium observed or executed. Maqam proves the policy, approval, and dispatch path. Stable identifiers connect the records without treating them as one database. +After every non-snapshot action, the agent obtains a fresh bounded snapshot before another model decision. The loop retains action receipts, evidence identifiers, and citation anchors so that context reduction does not detach a conclusion from its observable basis. -## 12. Qarinah memory +Finite maxSteps prevents an unbounded planning loop. maxContextChars limits each serialized model turn. maxToolOutputChars bounds tool output. Compaction removes only complete older assistant/tool rounds and retains a digest plus receipt, evidence, and citation anchors. The agent does not expose hidden reasoning as evidence. -The Qarinah adapter records bounded browser outcome material such as canonical input and output digests, evidence identifiers, receipt hashes, and cited descriptive metadata. It filters cookies, storage values, form values, and secrets. +Deadlines cover the model call even when an upstream provider ignores an abort signal. A timed-out provider response cannot later be treated as an accepted action. Runtime policy remains the final action boundary even when model output is syntactically valid. -Qarinah does not dispatch browser actions. It lets later agents retrieve cited project memory without replaying complete project histories or treating a generated summary as the source of truth. +The agent is designed to be inspectable and finite, not universally autonomous. Task success, cost, latency, and failure modes require a separate public evaluation corpus before comparative claims are justified. -## 13. Cockroach Crawler handoff +## 11. Model gateway -Cockroach Crawler handles bounded static HTTP crawling, mapping, feeds, public-source adapters, structured extraction, and document discovery. Cockroach Browser handles stateful rendering, interactive pages, authenticated user-approved sessions, and browser evidence. +The OpenAI-compatible gateway resolves its API key on the trusted host. It applies a deadline to API-key-provider waiting and the HTTP exchange, and it enforces independent request-byte and response-byte ceilings. -A typical workflow maps a site with the crawler, ranks candidate URLs, and opens only selected pages in the browser. This preserves browser budgets and makes the reason for each rendered session explicit. +Non-loopback model endpoints must use HTTPS. The gateway validates response structure and structured tool calls before they reach the agent. A model endpoint is not allowed to modify the browser session's origin, effect, approval, identity, or resource policy. -## 14. Deployment +Compatibility with an OpenAI-style HTTP schema is an integration surface, not a claim that every provider implements identical semantics. Provider selection, data handling, retention, and billing remain deployment-owner decisions. -Cockroach Browser supports: +## 12. Qarinah context and provenance -- project-local npm installation -- global per-user CLI installation -- one-command bootstrap and health checks -- shell completion generation -- current-user autostart definitions -- embedded SDK use -- authenticated loopback daemon -- authenticated remote workers with explicit TLS and remote mode -- Docker -- authenticated remote workers with TLS and strong bearer tokens -- bounded asynchronous jobs with safe automatic retries limited to reads -- explicit team viewer and operator roles for named sessions +The Qarinah recorder emits the versioned cockroach.browser-memory.v2 envelope. It includes event type, session ID, optional actor, timestamp, a SHA-256 purposeDigest instead of the raw purpose, optional input and output digests, evidence identifiers, an optional receipt hash, and filtered descriptive metadata. -Global installation makes the CLI available to the current computer account. It does not grant browser profiles, cookies, origins, credentials, or machine resources. +Recorder metadata is allowlisted. Eligible fields include action, status, input or output digest, receipt hash or identifier, evidence identifiers, policy digest, mode, effect, risk, and completion time. Values are bounded by depth, length, and count. Authorization, cookie, credential, password, passphrase, secret, token, storage, form-value, and API-key fields are removed recursively. -## 15. Security model +The envelope does not contain a source URL, raw page content, browser-profile material, hidden reasoning, or the raw session purpose. The host owns persistence. A recorder deadline defaults to 1,000 ms and can be configured within the implementation's bounds. Recorder rejection or timeout is reported operationally without replacing the browser result. -The default daemon: +The context provider retrieves a bounded summary with citation identifiers and optional receipt and evidence anchors. The agent validates identifier sizes and citation counts, preserves anchors during truncation, and serializes the pack as an untrusted user-role observation behind a trusted system instruction. -- binds to loopback -- requires a cryptographically strong bearer token -- stores generated tokens with restrictive permissions -- rejects non-loopback binding unless remote mode and TLS are explicit -- disables raw HTTP action dispatch by default -- enforces origin and private-network policy -- enforces finite action, duration, tab, upload, download, history, and evidence budgets -- pauses on access challenges -- records content-addressed evidence and hash-linked receipts +Retrieved history can inform a proposal. It cannot create a session, add an origin, enable an action, approve an effect, supply a credential, relax a resource ceiling, or dispatch browser work. Memory never grants authority. -The supported security line is 0.3.x. Private vulnerability reporting is the required disclosure channel. +## 13. Maqam, crawler, and other integrations -## 16. Capability status +Maqam supplies a separate governance proof. For a consequential operation, Cockroach Browser can produce canonical action material and a digest. Maqam can evaluate policy and bind approval to that exact material. Changed input, expired approval, or replay must fail. Browser evidence records what the runtime observed or executed; Maqam records why dispatch was authorized. Stable identifiers connect the two ledgers without pretending they are one transaction system. -The source-derived registry at the annotated `v0.3.0` tag contains: +Cockroach Crawler supplies breadth. It can map static HTTP content, feeds, documents, and public sources at bounded scale. Cockroach Browser supplies browser state, rendering, interactive operations, and evidence. A practical workflow crawls first, ranks candidate URLs, and opens only the pages that need a browser. -- 94 total capability entries -- 88 available in the package -- 6 adapter-backed integrations -- 0 planned entries +ProductLoop and provider integrations expose typed adapter contracts. Adapter status means another package, service, authority, or deployment is required. The presence of an adapter does not claim that Cockroach Browser operates the external system. -The registry covers sessions, explicit browser providers, runtime-owned profiles, state checkpoints, tabs, interaction, frames, dialogs, history, navigation graphs, JavaScript, files, screenshots, paired evidence, PDFs, traces, HAR, video, console, network records, receipts, extraction, audits, challenges, origin policy, private-network policy, effects, budgets, routes, approvals, secrets, CLI, completions, services, bootstrap, SDK, HTTP, MCP, Docker, dashboard, authenticated workers, bounded jobs, team roles, health checks, Maqam, Qarinah, Cockroach Crawler, ProductLoop, and webhooks. +## 14. Evidence and receipts -## 17. Verification method and evidence scope +Evidence can include semantic snapshots, screenshots, paired captures, Chromium PDFs, Playwright traces, HAR, video, console records, network metadata, downloads, audits, annotations, and action results. Evidence records are content-addressed and bound to the session that produced them. -The 0.3.0 tagged implementation is evaluated as an implementation-conformance artifact. The evidence establishes whether committed tests and release checks agree with the documented authority boundary. It does not measure universal website coverage, security against every attacker, task-success rate, latency, or comparative product quality. +An action receipt records canonical action material and digest, policy digest, URL before and after, result status, evidence identifiers, and the previous receipt hash. The linked chain makes missing, reordered, or modified terminal records detectable. Verification recomputes digests without replaying the browser. -The release is verified through: +Paired capture places a screenshot and a semantic snapshot under one reviewed page-state operation. Temporary annotations can label selected references without mutating application data. -- Node.js 22, 24, and 26 CI -- real Chromium boundary tests -- packed npm consumer tests -- hardened Docker smoke tests -- dependency review -- CodeQL -- npm audit -- generated documentation checks -- immutable npm artifact identity checks in the release workflow +Evidence proves what the configured runtime recorded. It does not prove that an external statement is true, that a human intended an action, that the website authorized automation, or that the runtime is free of vulnerabilities. Governance receipts and external facts require their own proof. -The tagged capability inventory is derived mechanically from `src/capabilities.ts`. Security-sensitive tests exercise explicit origins, effects, budgets, isolated profiles, redaction, challenge stop and resume, authenticated control surfaces, action receipts, team roles, and worker boundaries. The release has no numerical performance benchmark and this paper makes no performance claim. +## 15. Team tenancy and admission -The tests establish behavior for the committed fixtures and configured environments. They are not a claim that every website, browser challenge, operating system policy, or third-party integration has been independently certified. +The daemon has an administrator identity and can be configured with unique actor bearer tokens. Actor-scoped tokens require TeamSessionStore; startup fails closed if actor tokens are configured without the ownership store. The administrator token and every actor token must be unique, or startup fails with AUTH_TOKEN_COLLISION. -## 18. Limitations +An actor token cannot directly choose arbitrary session policy. Actor-created sessions additionally require a host-owned actorSessionFactory. That callback derives an authoritative session input from individually reviewed fields. The server replaces any caller-supplied actor with the authenticated identity and claims ownership before exposing the session. If the ownership claim fails, the newly created session is closed. -Cockroach Browser does not: +TeamSessionStore records one owner and revocable operator and viewer grants. Viewers inspect. Operators can use explicitly enabled action routes. Owners manage access and closure. Access mutations replace the in-memory map only after durable persistence succeeds, so a failed durable write leaves the prior access state in place. Raw profiles and cookies are not stored in access records. -- bypass access controls or CAPTCHAs -- grant agents ambient machine authority -- discover arbitrary cookies or profiles -- make remote unauthenticated binding safe -- make browser evidence equivalent to governance approval -- guarantee that a website permits automation -- replace a distributed queue or multi-host consensus system -- claim independent security certification +Session admission is concurrency-safe. The daemon defaults to 32 global non-closed sessions and 8 non-closed sessions per actor. Pending creations reserve capacity so parallel requests cannot oversubscribe a limit. Limit responses use HTTP 429 and distinguish global from actor ceilings. -## 19. Reproducibility +The activity stream is bounded and actor-filtered. Global health, Prometheus metrics, and evidence verification require the administrator identity. An OpenAPI index describes routes but does not grant permission to call them. -The annotated software release is `v0.3.0`, which resolves to commit `76c1095a6066bfa0242d8ea4413b0643133c977c`. The source repository contains the implementation, capability registry, manuals, tests, Docker assets, release workflow, and generated website: +This is application tenancy for an owned deployment. It is not a claim of hostile multi-tenant browser isolation. Strong isolation between mutually untrusted tenants requires separate operating-system or container boundaries, secret domains, storage domains, and network policies. -`https://github.com/AjnasNB/cockroach-browser` +## 16. Resource governance -The public documentation is: +For runtime-owned browser-server and lightweight launches whose process identifiers are observable, Cockroach Browser samples the complete owned process tree. It aggregates resident set size, cumulative CPU time, and process count and compares them with session ceilings. Duration is also enforced. -`https://cockroachbrowser.com` +Sampling runs continuously and at action boundaries. On supported platforms, near-simultaneous sessions can reuse a fresh host process inventory to reduce telemetry overhead. The sampling interval is bounded. Windows uses a higher default because PowerShell and CIM process enumeration are comparatively expensive. -The npm package is: +A detected resource breach, duration expiry, or telemetry loss becomes a sticky terminal error. It does not clear on a later lower sample. The runtime begins process-first teardown independently of the next browser action. -`https://www.npmjs.com/package/cockroach-browser` +Successful close terminates the runtime-owned process before publishing terminal lifecycle state and releasing the remaining context, trace, and runtime records. If termination cannot be verified, the session returns TERMINATION_UNVERIFIED and remains retained for an explicit close retry. A persistent-profile writer lock also remains held rather than creating a second writer against an uncertain process. -The persistent paper series is identified by the concept DOI: +Attached CDP and persistent headed contexts report process enforcement as unavailable when the runtime cannot establish ownership of the complete process tree. This is preferable to reporting a misleading zero or partial measurement. -`https://doi.org/10.5281/zenodo.21701791` +Aggregate RSS is conservative and may count shared pages in more than one process. CPU time is cumulative process CPU time, not utilization percentage and not electrical energy. Polling can miss short-lived spikes or CPU from a child that exits between observations. -To reproduce the main verification: +Application telemetry is not a real-time kernel boundary. Use Linux cgroups or container limits, Windows Job Objects, an operating-system sandbox, or equivalent infrastructure when a hard memory, CPU, process, or egress limit is required. + +## 17. Network boundary + +Governed full-engine sessions revalidate navigation, redirects, and intercepted HTTP or HTTPS subresources against session origin policy. Static fulfillment is bounded by rule and session byte ceilings. Runtime-owned full-engine contexts validate WebSocket handshakes and block service workers so that surface cannot silently bypass intercepted HTTP or HTTPS routing. + +These controls do not contain WebRTC, STUN, TURN, UDP, WebTransport, QUIC, attached CDP traffic, lightweight-engine WebSockets, raw Playwright or Puppeteer traffic, or operator-owned protocol clients. Hostile content requires a deployment-owned operating-system, container, firewall, or equivalent deny-by-default egress boundary. + +Webhook delivery uses public HTTPS destinations, revalidates DNS, rejects private or mixed-address results, preserves TLS hostname verification, and does not follow redirects. Signed webhooks use a separate delivery ledger and replay guard. + +Headless mode is not a sandbox. Rendering, JavaScript, remote CDP, imported state, proxies, uploads, downloads, and browser extensions remain privileged capabilities. + +## 18. Deployment architecture + +The runtime can run in-process as a TypeScript library, as an authenticated local daemon, in a container, or behind an explicitly configured authenticated remote-worker boundary. + +The default daemon binds to loopback and creates a strong bearer token. Non-loopback binding requires explicit remote mode, TLS, authentication, and a reviewed CORS allowlist. Raw HTTP action dispatch is disabled by default. + +The included container path is intended for a Node runtime and real browser processes. A deployment should pin package and browser identities, run with a read-only root filesystem where practical, mount only required data and evidence paths, use a constrained temporary filesystem, and apply memory, CPU, PID, and egress limits derived from measured workloads. + +BrowserWorkerPool can select reviewed authenticated workers by health, declared capacity, weight, and required tags. BrowserFleet can allocate local or external capacity with explicit engines and provider capabilities. These components do not turn the process-local job queue into a distributed transaction coordinator or create a managed global fleet. + +The Cloudflare Worker or static-site deployment associated with project documentation is a documentation and distribution surface. It is not the Node-and-browser execution runtime and should not be marketed as browser execution at the edge. + +The following local and container forms illustrate the intended boundary: + +```bash +npx cockroach-browser serve --host 127.0.0.1 --port 43110 +docker build -t cockroach-browser:0.5.0-rc.1 . +``` + +Remote deployment should add service identity, TLS termination, secret management, network policy, separate tenant isolation where needed, bounded artifact storage, monitoring, and a tested recovery path. + +## 19. Distribution and release integrity + +The primary software distribution is the npm package and CLI. Release candidates should also produce signed GitHub release artifacts with checksums, provenance, and a software bill of materials, plus pinned OCI images in a reviewed registry. Homebrew, Scoop, or winget distribution should follow only after repeatable signed binary releases exist. + +MCP registry discovery and one-command setup for major coding-agent clients can reduce integration friction. Runnable TypeScript, Python, and Go examples should cover structured extraction, user-approved form submission, evidence-backed operations, and deliberate lightweight-to-full-engine routing. + +The repository is AGPL-3.0-or-later. An enterprise distribution strategy should state clearly whether it remains pure copyleft or adds a separately reviewed commercial license. The paper does not set hosted-service pricing because an operated service, support boundary, tenancy model, and measured cost model are not established here. + +The release-candidate identifier in this paper is not an immutable published tag. A production release should bind the reviewed commit, generated package, browser binaries, container image, paper, SBOM, checksums, and provenance before publication. + +## 20. Verification status + +Verification is layered. Schema and unit tests exercise policy, validation, receipts, structured extraction, resource telemetry, server tenancy, engine manifests, and agent behavior. Browser boundary tests exercise real engine processes where the host can launch them. Package tests consume the packed tarball rather than importing the source tree. Proof verification recomputes benchmark claims from retained artifacts. + +A development-tree run associated with this release-candidate work reported 184 ordinary tests: 164 passed, 20 were skipped by their documented environment gates, and none failed. Separate Windows live managed-resource and Obscura lanes reported 18 of 18 passing. A Linux or WSL full-engine and raw-platform lane reported 28 passing with one intentionally skipped headed-only case. These results are a dated development snapshot, not an immutable release attestation. The final release candidate must rerun the complete gate on clean hosts and publish the logs or machine-readable reports. + +The standard repository gate is: ```bash npm ci npm run check -COCKROACH_BROWSER_E2E=1 npm test -node scripts/test-packed-consumer.mjs ``` -## 20. Conclusion +The expanded gate includes type checking, build, tests, API-surface parity, lightweight-proof verification, package checks, site checks, production dependency audit, and tarball inspection. Real-browser and packed-consumer lanes should be executed explicitly in clean environments. + +## 21. Lightweight benchmark question + +The benchmark asks a narrow falsifiable question: + +Can the complete runtime-owned Obscura browser process tree execute one pinned non-visual CDP, JavaScript, DOM, and form fixture, reject a visual action at preflight, and tear down correctly while every measured RSS observation remains at or below a stated target? + +The target is evaluated jointly. A run passes only if every required conformance check succeeds and every retained owned-browser-tree RSS observation is at or below the target. A capability failure and a memory failure both fail the target. + +The benchmark does not ask whether the whole Cockroach Browser application fits the target, whether arbitrary pages fit it, whether the engine renders accurately, whether full browsers fit it, or whether one product is faster or lighter than another. + +## 22. Benchmark identity + +The reviewed executable is obscura.exe, version Obscura 0.2.1, with a file size of 58,097,152 bytes. + +The executable SHA-256 is: + +```text +5b609fb46bc00da79e450fb0fbd34bd442e565b1394f4af95433e0b341078221 +``` + +The Git base commit is deb6c25b4c0bc1927a631e3b062464b4f4cc2775. The measured working tree was dirty, so the base commit alone is not the tested source identity. + +The normalized source-tree SHA-256 is: + +```text +fb0c4597e39f319dd9b6f3bab02777c395e9d8d84906981bf939a39b470e7279 +``` + +The runtime-build SHA-256 is: + +```text +6738efa4000ba482db83c9dc95ba2f21caed31de96f30dcd342e5dc722d86025 +``` + +The benchmark-harness SHA-256 is: + +```text +08a5294f2d446765f712b93c9bfaaca010b1d043ded638d1f4f57b5038c97e86 +``` + +Identity normalization uses forward slashes for relative paths and LF line endings for text. The source identity covers runtime, schemas, benchmark and package configuration, build configuration, and tests. Generated documentation and benchmark artifacts are excluded so that writing an output artifact does not change the measured input identity. + +The recorded machine was Windows win32/x64 release 10.0.26200, with 16 logical CPUs, an AMD Ryzen 7 4800H, 16,557,887,488 bytes of total memory, and Node v24.15.0. + +## 23. Benchmark method + +Each target was run independently with one warmup and 20 measured launches. Each measured launch created one runtime-owned loopback CDP server and loaded one non-visual data-URL document. + +The required operations were connection, JavaScript evaluation, DOM query, text input, and HTMLElement click dispatch. The required checks were CDP connection, JavaScript, DOM, forms, screenshot preflight denial, and teardown. Every required check passed in every measured launch for both targets. + +The measurement aggregated RSS across the complete runtime-owned browser process tree from spawn through startup, CDP connection, workload, settle, and shutdown preparation. The Node coordinator was sampled and reported separately. + +Each measured launch included 10 explicit steady-state samples at 25 ms intervals plus boundary and continuous observations. The 30 MiB target retained 478 timestamped process-tree observations across its 20 measured launches, and the 25 MiB target retained 480, for 958 observations across both targets. + +The timing bounds were 15,000 ms for startup, 10,000 ms for an action, 5,000 ms for teardown, and 500 ms for settle. + +Energy was unavailable because the host did not provide a calibrated energy counter. CPU time is not reported as watts. + +The renderingPolicy value visual-actions-denied means that Cockroach Browser rejected screenshot capture during preflight. It does not assert that the selected engine disabled or omitted rendering. + +## 24. Benchmark results + +The 30 MiB target was 31,457,280 bytes. It passed. The per-launch peak distribution was: + +- Minimum: 28,893,184 bytes. +- Median: 29,347,840 bytes. +- p95: 29,569,024 bytes. +- Maximum: 29,622,272 bytes, or 28.25 MiB. +- Required capabilities: all passed. + +The 25 MiB target was 26,214,400 bytes. It failed because the owned browser process tree exceeded the target, while every required capability check still passed. The per-launch peak distribution was: + +- Minimum: 28,831,744 bytes. +- Median: 29,323,264 bytes. +- p95: 29,634,560 bytes. +- Maximum: 29,679,616 bytes, or approximately 28.30 MiB. +- Required capabilities: all passed. + +The passing artifact is docs/benchmarks/artifacts/obscura-0.2.1-constrained-non-visual-30mib-2026-09-03-rc1.json. + +Its SHA-256 is: + +```text +f90b31d6f5d5096300ac2722ed835db0483a76dc4d51ee85e86604a6634c0aa7 +``` + +The failing artifact is docs/benchmarks/artifacts/obscura-0.2.1-constrained-non-visual-25mib-2026-09-03-rc1.json. + +Its SHA-256 is: + +```text +581eb93577d6b52c71e02d7e0b71914f88acd0920a6e0e06925aae0a4575d2df +``` + +The [canonical proof record](./benchmarks/obscura-non-visual-2026-09-03.md) and the two immutable JSON artifacts are the source of truth for individual observations, coordinator measurements, timings, conformance, and teardown records. The September 2 five-run record is historical and superseded. + +## 25. Interpreting the result + +The supported external statement is: + +"The pinned Obscura 0.2.1 constrained non-visual fixture reached a maximum complete owned-browser-process-tree RSS of 29,622,272 bytes, or 28.25 MiB, across 20 measured launches and passed the 30 MiB target. A separately measured 25 MiB target failed with a 29,679,616-byte maximum." + +That statement should travel with the binary digest, host, workload, process-tree definition, sample count, conformance result, failing target, and coordinator exclusion. + +The result demonstrates that a compatible DOM-oriented lane can operate below a 30 MiB owned-browser-tree target for this fixture. It does not demonstrate a 28 MB product footprint. It does not make a promise about an arbitrary workload. Full engines normally use much more memory under realistic rendered workloads. + +The honest product formulation is: + +"Full browsers when fidelity matters. A measured 28.25 MiB non-visual lane when it does not." + +## 26. Competitive landscape + +The following landscape was checked against official public surfaces on 2026-09-03. Products change, so these descriptions should be revalidated before each external publication. + +[Playwright](https://playwright.dev/docs/browsers) supports Chromium, Firefox, and WebKit and supplies broad automation, testing, tracing, code generation, and isolation capabilities. [Playwright MCP](https://github.com/microsoft/playwright-mcp) provides an official MCP surface. Multi-engine control and MCP are therefore ecosystem parity, not exclusive Cockroach Browser claims. + +[Puppeteer](https://pptr.dev/guides/what-is-puppeteer) provides Chrome and Firefox automation using CDP and WebDriver BiDi surfaces. Cockroach Browser's Puppeteer, CDP, tracing, screenshot, PDF, and protocol access should be presented as interoperability and operator reach. + +[Selenium WebDriver](https://www.selenium.dev/documentation/webdriver/) is a long-established cross-browser standards-based automation interface. Cockroach Browser's WebDriver and Appium routes extend operator connectivity; they do not replace the Selenium ecosystem. + +[Browser Use](https://docs.browser-use.com/cloud/quickstart), [Stagehand](https://docs.stagehand.dev/v3/first-steps/introduction), and other agent-browser frameworks expose model-directed browser workflows. A built-in agent loop is table stakes. Cockroach Browser's position is the authority, capability, resource, and evidence boundary around the loop. + +[Browserbase](https://www.browserbase.com/), [Browserless](https://docs.browserless.io/), [Steel](https://docs.steel.dev/), and [Cloudflare Browser Run](https://developers.cloudflare.com/browser-run/) offer different combinations of managed browser capacity, automation protocols, observability, profiles, proxies, extraction, and agent integration. Cockroach Browser does not claim comparable managed global scale. Its near-term position is customer-owned execution, multi-engine routing, explicit authority, and reproducible evidence. + +[Obscura](https://github.com/h4ckf0r0day/obscura) is the upstream independent lightweight engine used in the measured lane. Its [0.2.1 release](https://github.com/h4ckf0r0day/obscura/releases/tag/v0.2.1) documents evolving rendering and automation behavior. Cockroach Browser claims the pinned adapter, policy, preflight, process ownership, conformance fixture, and independent measurement, not invention of Obscura. + +[Lightpanda](https://lightpanda.io/docs/) is an independent lightweight browser project with CDP-oriented automation. Its [published benchmark documentation](https://lightpanda.io/docs/core-concepts/benchmarks) covers broader workloads than the single Cockroach Browser fixture. Cockroach Browser does not compare these results directly because versions, hosts, workloads, concurrency, and memory definitions differ. + +Within this reviewed set, per-action engine manifests, memory that is explicitly non-authoritative, continuous process-tree governance, and build-linked proof with a published failing target form an uncommon combination. "Uncommon" is a dated positioning observation, not a universal exclusivity claim. + +## 27. Intended users and jobs + +The primary audience is engineers building AI-agent platforms or operational automation that must remain self-hosted, respect data-residency constraints, limit model authority, or retain inspectable evidence. + +Data extraction and crawling teams are a secondary audience when DOM-oriented jobs are constrained by memory or concurrency and can be routed away from a full rendering engine. + +QA and developer-tool teams are a secondary audience when they need full Chromium, Firefox, and WebKit fidelity plus an agent-facing governed layer. + +Regulated operations teams are a secondary audience when a workflow needs explicit approvals, bounded effects, ownership, and evidence retention. The software does not itself confer regulatory compliance. + +The initial wedge is not consumer desktop browsing, a turnkey global proxy or CAPTCHA service, or the cheapest hosted Chrome hour. + +The principal jobs are: + +- Let an agent inspect and operate a website without inheriting ambient profiles, credentials, origins, or machine authority. +- Select a lightweight DOM lane or a full-fidelity browser lane before launch. +- Reject unsupported work instead of silently downgrading or swapping engines. +- Reuse Playwright, Puppeteer, CDP, BiDi, WebDriver, Appium, MCP, and existing browser knowledge. +- Enforce finite RSS, CPU-time, duration, action, evidence, and byte budgets. +- Produce evidence and receipts that an operator can inspect. +- Run locally or in customer-controlled infrastructure with authenticated team access. +- Supply bounded cited context without turning retrieved history into browser authority. -Cockroach Browser gives an AI agent a real browser without silently transferring the whole machine's authority. The central design choice is not to remove browser capability, but to bind it to explicit sessions, finite budgets, evidence, and host-owned control. +## 28. Adoption and go-to-market + +The first product message should lead with the category and two-lane architecture, followed immediately by a runnable quickstart and the exact proof: + +"One governed browser runtime for AI agents. Route compatible DOM work through a measured 28.25 MiB Obscura lane, use Chromium, Firefox, or WebKit for full fidelity, and enforce origins, effects, approvals, resources, and evidence before the model acts." + +The next visible features should be engine preflight, fail-closed experimental opt-in, session authority, verified teardown, Playwright and Puppeteer interoperability, MCP and SDK access, evidence and receipts, Qarinah provenance, and customer-owned deployment. + +Distribution should use: + +- npm and the CLI for the primary installation path. +- Signed GitHub releases with checksums, provenance, and an SBOM. +- Pinned GHCR or Docker Hub images after container verification. +- An MCP registry listing and client-specific setup guides. +- Runnable quickstarts in TypeScript, Python, and Go. +- Templates for extraction, reviewed form submission, evidence-backed operations, and lightweight-to-full routing. +- GitHub releases, Discussions, issues, and a public compatibility board. +- A benchmark page that exposes raw artifacts and exact reproduction commands. +- A concise architecture article and a separate benchmark article derived from this paper. +- Show HN, Product Hunt, browser-automation communities, scraping communities, local-AI communities, agent-framework communities, and direct design-partner outreach after clean-host installation works. + +The first 30 days should reconcile version, capability count, benchmark scope, limitations, and release commands across package metadata, README, paper, site, and release assets. Clean installation, first session, proof verification, and uninstall should be repeatable. + +Days 31 through 60 should add same-host pinned comparisons, a real-page compatibility corpus, concurrency testing, a long soak, fault injection, security-negative tests, and integrations with at least three agent frameworks. + +Days 61 through 90 should publish the paper, raw results, known limitations, signed artifacts, and runnable demos together; recruit design partners with explicit permission for any public reference; and maintain an evidence and compatibility changelog. + +Useful operating metrics are clean-install success rate, median time to first verified session, benchmark reproduction rate, weekly active projects, four-week retained projects, task success by engine, orphan-process rate, resource-limit enforcement rate, and issue time to resolution. These are proposed metrics, not current adoption claims. + +## 29. Limitations and open risks + +The current lightweight proof covers one deterministic non-visual fixture on one Windows machine. It does not cover real-page compatibility, visual correctness, concurrency, long-running memory growth, energy, broad latency, or agent task success. + +Obscura is experimental. Its CDP and web-platform behavior is not assumed to match Chromium. Rendering, screenshots, PDF, frames, Shadow DOM, storage, downloads, and other declared experimental capabilities need version-pinned conformance before release claims. + +Lightpanda managed launch is intentionally unavailable until a complete engine-level or operating-system-level egress boundary covers relevant protocols and workers. + +The application network boundary does not contain WebRTC, STUN, TURN, UDP, WebTransport, QUIC, attached CDP, lightweight-engine WebSockets, or raw operator traffic. + +Process sampling can miss brief spikes and is not equivalent to cgroups, Job Objects, or another kernel boundary. Attached CDP and persistent headed contexts can make complete process ownership unavailable. + +The built-in queue is process-local and file-backed. It is not a distributed consensus or transaction system. BrowserWorkerPool routes capacity but does not create global scheduling correctness. + +The agent loop is finite and validated, but model output can still be wrong. Page content and retrieved context can attempt prompt injection. Runtime policy, exact approval, output bounds, citations, and fresh snapshots reduce authority and ambiguity; they do not make model reasoning correct. + +Evidence can be altered outside protected storage unless deployment controls preserve it. Hashes detect changes relative to a trusted head and artifact identity; they do not prevent deletion or compromise of every surrounding system. + +Browser automation may violate a site's rules or a user's authority even when the software can execute it. Operators must obtain authorization and respect applicable terms, privacy requirements, rate limits, and law. + +### 29.1 Windows SideBySide limitation + +On the Windows verification host used during release-candidate testing, the installed Playwright Firefox executable and the installed full Chrome executable failed before Cockroach Browser could establish browser control. Windows reported a SideBySide private-assembly resolution problem. The Firefox event identified a missing mozglue assembly version 1.0.0.0. The Chrome event identified a missing versioned assembly 151.0.7922.34. + +This is a host-specific packaging or assembly-resolution limitation, not evidence that Firefox or Chrome generally fails and not evidence of a Cockroach Browser policy failure. Playwright Chromium headless shell and the pinned Obscura path passed on the Windows host, while the full-engine and raw-platform suite passed on Linux or WSL. + +Windows private assemblies and the assembly search process are described in Microsoft's [About Private Assemblies](https://learn.microsoft.com/en-us/windows/win32/sbscs/about-private-assemblies-) and [Assembly Searching Sequence](https://learn.microsoft.com/en-us/windows/win32/sbscs/assembly-searching-sequence) documentation. + +The release should either resolve the local browser installation and rerun the affected Windows lanes, or publish the exact host limitation and supported Windows route. A passing Linux or WSL lane must not be relabeled as native Windows coverage. + +## 30. Reproducibility + +Verify the checked-in proof record, artifact hashes, narrative bindings, and current source and build identities with: + +```powershell +npm run verify:lightweight-proof +``` + +To produce new evidence, build the same reviewed source identity, obtain the exact Obscura binary whose SHA-256 is recorded above, and run each target independently. Replace the executable path with the verified local path. + +```powershell +npm run build +node scripts/benchmark-lightweight.mjs --executable C:/reviewed/obscura.exe --implementation obscura --sha256 5b609fb46bc00da79e450fb0fbd34bd442e565b1394f4af95433e0b341078221 --resource-profile constrained --target-mib 30 --warmup 1 --iterations 20 --resource-samples 10 --resource-sample-interval-ms 25 --settle-ms 500 --startup-timeout-ms 15000 --action-timeout-ms 10000 --teardown-timeout-ms 5000 --output output/resource-benchmarks/obscura-30mib-new.json +node scripts/benchmark-lightweight.mjs --executable C:/reviewed/obscura.exe --implementation obscura --sha256 5b609fb46bc00da79e450fb0fbd34bd442e565b1394f4af95433e0b341078221 --resource-profile constrained --target-mib 25 --warmup 1 --iterations 20 --resource-samples 10 --resource-sample-interval-ms 25 --settle-ms 500 --startup-timeout-ms 15000 --action-timeout-ms 10000 --teardown-timeout-ms 5000 --output output/resource-benchmarks/obscura-25mib-new.json +``` + +The failing 25 MiB command should write its JSON report and return a non-zero exit. Preserve that non-zero verdict. A new run is new evidence and must not overwrite the immutable September 3 artifacts. + +For release conformance, use clean pinned environments and retain the output from: + +```bash +npm ci +npm run typecheck +npm run build +npm test +npm run api-surface:check +npm run verify:lightweight-proof +npm run check:package +npm run check:site +npm audit --omit=dev +npm pack --dry-run --ignore-scripts +``` -This makes browser automation composable with crawling, memory, policy, approval, and workflow systems while preserving the distinct proof each layer provides. +Run real-browser tests on supported clean Windows, Linux, and macOS hosts. Record browser versions, operating-system build, CPU architecture, machine memory, Node version, command line, skips, failures, and artifact hashes. A release attestation should identify exact logs rather than reporting only a total. + +## 31. Evidence roadmap + +The current result is a rigorous scoped proof, not an overall performance or production-readiness verdict. Evidence should expand in this order. + +- Release identity: signed source, package, container, browser, harness, paper, SBOM, and provenance bindings. +- Correctness: per-engine bounded-action tests, parser properties, protocol conformance, and deterministic fixtures. +- Comparable performance: same-host browser-tree RSS or PSS, CPU, latency, throughput, and separately reported coordinator overhead against pinned baselines. +- Compatibility: at least 100 documented public pages plus deterministic SPA, frame, form, download, authentication, and challenge fixtures. +- Reliability: a 24-hour soak, thousands of launches, crash and hang classification, memory-growth slope, and zero unexplained owned-process orphans. +- Concurrency: 1, 10, 25, 50, and 100 sessions with admission behavior and saturation disclosed. +- Agent quality: public task success, cost, latency, step count, and failure taxonomy. +- Security negatives: SSRF, redirects, DNS rebinding, WebSockets, service workers, private networks, prompt injection, secret redaction, token scope, path traversal, persistence failure, and teardown failure. +- Platforms: Windows, Linux, macOS, Docker, and supported architectures with failures published beside passes. +- Independent review: third-party benchmark reproduction, threat-model review, and security assessment. + +Claims should expand only after the corresponding evidence exists. + +## 32. Conclusion + +Cockroach Browser is designed to make browser capability usable by AI agents without silently transferring ambient machine authority. It combines full browsers for fidelity, an explicitly experimental lightweight lane for compatible work, preflighted engine capabilities, bounded sessions, finite resources, structured observations, model integration, cited context, tenancy, and inspectable evidence. + +The most important measured result is also deliberately narrow: a pinned Obscura 0.2.1 non-visual fixture passed a 30 MiB complete owned-browser-process-tree target at a 29,622,272-byte maximum, while a separate 25 MiB target failed at 29,679,616 bytes. Publishing both outcomes defines the present boundary more accurately than a rounded product promise. + +The path to adoption is therefore evidence-led. Make setup fast, make engine selection explicit, make unsupported work fail before launch, make authority inspectable, make teardown uncertainty visible, and publish artifacts that others can verify. + +## Appendix A. Bounded action kinds + +The 66 bounded action kinds in 0.5.0-rc.1 are: + +- navigate +- back +- forward +- reload +- click +- doubleClick +- fill +- type +- press +- hover +- focus +- check +- uncheck +- select +- scroll +- drag +- mouse.move +- mouse.down +- mouse.up +- mouse.click +- keyboard.down +- keyboard.up +- keyboard.insertText +- upload +- download +- evaluate +- query.inspect +- emulation.set +- emulation.clear +- cache.clear +- console.clear +- network.clear +- wait +- challenge.resolve +- history.inspect +- capture.paired +- annotate.show +- annotate.clear +- clipboard.read +- clipboard.write +- network.inspect +- network.export +- network.route.add +- network.route.remove +- network.routes.list +- state.save +- state.load +- state.list +- state.delete +- screenshot +- pdf +- snapshot +- extract +- extract.structured +- cookies.read +- cookies.write +- storage.read +- storage.write +- tab.open +- tab.close +- tab.switch +- tab.lock +- tab.unlock +- tab.lock.status +- trace.start +- trace.stop + +## Appendix B. Claim controls + +Approved category wording: + +"A governed, local-first, multi-engine browser runtime for AI agents." + +Approved measured hero wording: + +"Full browsers when fidelity matters. A measured 28.25 MiB non-visual lane when it does not." + +Approved routing wording: + +"Use the lightweight lane for compatible DOM and JavaScript work, and Chromium, Firefox, or WebKit for full fidelity." + +Prohibited or unsupported wording includes "28 MB or less," "every browser feature in 28 MB," "the only," "the first," "no other browser," "faster than Chrome," "lighter than Lightpanda," "secure sandbox," "complete isolation," "all egress is contained," "works on every site," "unblockable," "bypasses CAPTCHAs," and "renderer disabled." + +Claims about operated regions, proxies, CAPTCHA services, stealth infrastructure, customer counts, compliance, reliability, savings, or a hosted fleet require current operational evidence and must not be inferred from adapter interfaces. + +## Appendix C. Release and deployment checklist + +- Bind the immutable release tag to the reviewed commit. +- Regenerate the source-derived capability inventory and confirm its counts. +- Run clean Node 22, 24, and 26 gates. +- Run full-engine browser tests on supported clean hosts. +- Resolve or disclose the Windows SideBySide limitation. +- Verify the canonical benchmark and retain both pass and fail artifacts. +- Inspect package contents from the packed tarball. +- Pin browser versions and record their digests where available. +- Generate checksums, provenance, signatures, and an SBOM. +- Build and scan the OCI image. +- Exercise loopback daemon authentication and remote TLS rejection paths. +- Exercise actor ownership, revocation, persistence failure, and admission ceilings. +- Exercise resource breach, telemetry loss, and unverified teardown paths. +- Exercise network-negative cases and document protocols requiring OS containment. +- Verify every external link and every local paper link. +- Render and visually inspect the paper without modifying the v1.1 artifacts. +- Publish limitations beside the quickstart and benchmark result. + +## Appendix D. Source and evidence map + +- Source repository: [github.com/AjnasNB/cockroach-browser](https://github.com/AjnasNB/cockroach-browser) +- Public documentation: [cockroachbrowser.com](https://cockroachbrowser.com) +- npm package: [npmjs.com/package/cockroach-browser](https://www.npmjs.com/package/cockroach-browser) +- Paper series DOI: [10.5281/zenodo.21701791](https://doi.org/10.5281/zenodo.21701791) +- Canonical lightweight proof: [docs/benchmarks/obscura-non-visual-2026-09-03.md](./benchmarks/obscura-non-visual-2026-09-03.md) +- Headless compatibility guide: [docs/headless-compatibility.md](./headless-compatibility.md) +- Resource-governance guide: [docs/resource-governance.md](./resource-governance.md) +- Market-positioning research: [docs/market-positioning.md](./market-positioning.md) +- Qarinah integration guide: [docs/qarinah.md](./qarinah.md) +- Deployment guide: [docs/deployment.md](./deployment.md) +- Security guide: [docs/security.md](./security.md) ## Acknowledgements -The author thanks Shahin Ahammed for contributions to product direction, use-case definition, positioning, and review of the manuscript. +The author thanks Shahin Ahammed for contributions to product direction, use-case definition, positioning, and review of the manuscript. Cockroach Browser builds on the work of browser-engine, automation, protocol, parsing, and open-source communities cited in this paper. ## References -1. Cockroach Browser source and documentation, annotated release `v0.3.0`, commit `76c1095a6066bfa0242d8ea4413b0643133c977c`. -2. Cockroach Browser technical-paper series, concept DOI [10.5281/zenodo.21701791](https://doi.org/10.5281/zenodo.21701791). -3. Model Context Protocol specification and tools documentation, [modelcontextprotocol.io](https://modelcontextprotocol.io/). -4. Microsoft, Playwright documentation, [playwright.dev](https://playwright.dev/). -5. Chrome DevTools Protocol, [chromedevtools.github.io/devtools-protocol](https://chromedevtools.github.io/devtools-protocol/). -6. WHATWG, HTML Living Standard, [html.spec.whatwg.org](https://html.spec.whatwg.org/). -7. WHATWG, URL Living Standard, [url.spec.whatwg.org](https://url.spec.whatwg.org/). -8. IETF, RFC 9110: HTTP Semantics, [rfc-editor.org/rfc/rfc9110](https://www.rfc-editor.org/rfc/rfc9110). -9. IETF, RFC 6454: The Web Origin Concept, [rfc-editor.org/rfc/rfc6454](https://www.rfc-editor.org/rfc/rfc6454). -10. Maqam source and documentation, [github.com/AjnasNB/maqam](https://github.com/AjnasNB/maqam). -11. Qarinah technical white paper and implementation, [qarinah.com](https://qarinah.com/). -12. Cockroach Crawler source and documentation, [cockroachcrawler.com](https://cockroachcrawler.com/). +1. Cockroach Browser source and documentation, [github.com/AjnasNB/cockroach-browser](https://github.com/AjnasNB/cockroach-browser). +2. Cockroach Browser technical-paper series, [concept DOI 10.5281/zenodo.21701791](https://doi.org/10.5281/zenodo.21701791). +3. Cockroach Browser canonical Obscura proof, [September 3, 2026 record](./benchmarks/obscura-non-visual-2026-09-03.md). +4. Microsoft, [Playwright browser documentation](https://playwright.dev/docs/browsers). +5. Chrome Browser Automation team, [Puppeteer introduction](https://pptr.dev/guides/what-is-puppeteer). +6. Selenium project, [WebDriver documentation](https://www.selenium.dev/documentation/webdriver/). +7. Microsoft, [Playwright MCP](https://github.com/microsoft/playwright-mcp). +8. Model Context Protocol, [specification and documentation](https://modelcontextprotocol.io/). +9. Chrome DevTools Protocol, [protocol documentation](https://chromedevtools.github.io/devtools-protocol/). +10. Obscura, [source repository](https://github.com/h4ckf0r0day/obscura) and [0.2.1 release](https://github.com/h4ckf0r0day/obscura/releases/tag/v0.2.1). +11. Lightpanda, [documentation](https://lightpanda.io/docs/) and [benchmark methodology](https://lightpanda.io/docs/core-concepts/benchmarks). +12. Browser Use, [cloud quickstart](https://docs.browser-use.com/cloud/quickstart). +13. Stagehand, [introduction](https://docs.stagehand.dev/v3/first-steps/introduction). +14. Browserless, [product documentation](https://docs.browserless.io/). +15. Steel, [product documentation](https://docs.steel.dev/). +16. Cloudflare, [Browser Run documentation](https://developers.cloudflare.com/browser-run/). +17. Microsoft, [About Private Assemblies](https://learn.microsoft.com/en-us/windows/win32/sbscs/about-private-assemblies-). +18. Microsoft, [Assembly Searching Sequence](https://learn.microsoft.com/en-us/windows/win32/sbscs/assembly-searching-sequence). +19. WHATWG, [HTML Living Standard](https://html.spec.whatwg.org/). +20. WHATWG, [URL Living Standard](https://url.spec.whatwg.org/). +21. IETF, [RFC 9110: HTTP Semantics](https://www.rfc-editor.org/rfc/rfc9110). +22. IETF, [RFC 6454: The Web Origin Concept](https://www.rfc-editor.org/rfc/rfc6454). +23. Maqam, [source and documentation](https://github.com/AjnasNB/maqam). +24. Qarinah, [technical documentation](https://qarinah.com/). +25. Cockroach Crawler, [source and documentation](https://cockroachcrawler.com/). diff --git a/package-lock.json b/package-lock.json index d9b9ddf..8456dbf 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,16 +1,18 @@ { "name": "cockroach-browser", - "version": "0.4.1", + "version": "0.5.0-rc.1", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "cockroach-browser", - "version": "0.4.1", + "version": "0.5.0-rc.1", "license": "AGPL-3.0-or-later", "dependencies": { "@modelcontextprotocol/sdk": "^1.17.5", "@playwright/test": "^1.62.1", + "ajv": "^8.20.0", + "parse5": "^8.0.1", "pixelmatch": "^7.1.0", "playwright-core": "^1.62.1", "pngjs": "^7.0.0", @@ -18,7 +20,9 @@ "zod": "^4.4.3" }, "bin": { - "cockroach-browser": "dist/cli.js" + "cockroach-browser": "dist/cli.js", + "cockroach-browser-codegen": "dist/codegen.js", + "cockroach-browser-test": "dist/test-runner.js" }, "devDependencies": { "@types/node": "^24.0.0", @@ -483,6 +487,18 @@ "node": ">= 0.8" } }, + "node_modules/entities": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-8.0.0.tgz", + "integrity": "sha512-zwfzJecQ/Uej6tusMqwAqU/6KL2XaB2VZ2Jg54Je6ahNBGNH6Ek6g3jjNCF0fG9EWQKGZNddNjU5F1ZQn/sBnA==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=20.19.0" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, "node_modules/es-define-property": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", @@ -627,9 +643,9 @@ "license": "MIT" }, "node_modules/fast-uri": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.5.tgz", - "integrity": "sha512-gHwA1O9LDIcKunMKhObS/HimwtehO1nPUECKAu5TpKgaO19fcWEl4bliWe1jWxVFvIXztJjjQ4L8XQ1EU9f7Jw==", + "version": "3.1.7", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.7.tgz", + "integrity": "sha512-dOvZVzjdZdz7phd9v6jCbwxrBW3fK6n8Rc0CtdmM4bumzMnxywBYhuph6J819RRw/ku+rLbelwfMunktuzVVHg==", "funding": [ { "type": "github", @@ -1031,6 +1047,18 @@ "wrappy": "1" } }, + "node_modules/parse5": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-8.0.1.tgz", + "integrity": "sha512-z1e/HMG90obSGeidlli3hj7cbocou0/wa5HacvI3ASx34PecNjNQeaHNo5WIZpWofN9kgkqV1q5YvXe3F0FoPw==", + "license": "MIT", + "dependencies": { + "entities": "^8.0.0" + }, + "funding": { + "url": "https://github.com/inikulin/parse5?sponsor=1" + } + }, "node_modules/parseurl": { "version": "1.3.3", "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", @@ -1150,9 +1178,9 @@ } }, "node_modules/qs": { - "version": "6.15.3", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.15.3.tgz", - "integrity": "sha512-O9gl3zCl5h5blw1KGUzQKhA5oUXSl8rwUIM5o0S3nCXMliSvy5Dzx7/DJcI+SwgICv+IneSZwhBh1oSyEHA71A==", + "version": "6.16.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.16.0.tgz", + "integrity": "sha512-h6fhOIaRrID2CbEY2fqs+7t+UXZo+MLAnU5gRIq85uFtdiUPCdsApMlHhXogKVM4HM2DVbIjGNTTYH2OcmP1vA==", "license": "BSD-3-Clause", "dependencies": { "es-define-property": "^1.0.1", diff --git a/package.json b/package.json index cfd1835..d6ad4ea 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "cockroach-browser", - "version": "0.4.1", + "version": "0.5.0-rc.1", "description": "Open-source local-first Chromium, Firefox, WebKit, Playwright, and Puppeteer automation for AI agents with semantic page snapshots, model-directed execution, MCP, SDKs, and verifiable evidence.", "license": "AGPL-3.0-or-later", "author": "Ajnas NB", @@ -82,7 +82,9 @@ "types": "./dist/fleet.d.ts", "default": "./dist/fleet.js" }, + "./schemas/action.schema.json": "./schemas/action.schema.json", "./schemas/browser-memory.schema.json": "./schemas/browser-memory.schema.json", + "./schemas/session.schema.json": "./schemas/session.schema.json", "./server.json": "./server.json" }, "files": [ @@ -108,12 +110,14 @@ "test:source": "npm test", "test:resource": "npm run build:test && node --test --test-reporter=spec .test-dist/test/resource-budget-e2e.test.js", "benchmark:resources": "npm run build && node scripts/benchmark-resources.mjs", + "benchmark:lightweight": "npm run build && node scripts/benchmark-lightweight.mjs", + "verify:lightweight-proof": "node scripts/verify-lightweight-proof.mjs", "typecheck": "tsc -p tsconfig.json --noEmit", "check:package": "node scripts/check-package.mjs", "check:site": "node scripts/check-site.mjs", "api-surface:build": "node scripts/build-api-surface.mjs", "api-surface:check": "node scripts/build-api-surface.mjs --check", - "check": "npm run typecheck && npm run build && npm test && npm run api-surface:check && npm run check:package && npm run check:site && npm audit --omit=dev && npm pack --dry-run --ignore-scripts", + "check": "npm run typecheck && npm run build && npm test && npm run api-surface:check && npm run verify:lightweight-proof && npm run check:package && npm run check:site && npm audit --omit=dev && npm pack --dry-run --ignore-scripts", "prepack": "npm run clean && npm run build && npm run check:package", "prepublishOnly": "npm run check" }, @@ -154,6 +158,8 @@ "dependencies": { "@modelcontextprotocol/sdk": "^1.17.5", "@playwright/test": "^1.62.1", + "ajv": "^8.20.0", + "parse5": "^8.0.1", "pixelmatch": "^7.1.0", "playwright-core": "^1.62.1", "pngjs": "^7.0.0", diff --git a/schemas/action.schema.json b/schemas/action.schema.json index 83cdcd8..e62e571 100644 --- a/schemas/action.schema.json +++ b/schemas/action.schema.json @@ -14,14 +14,19 @@ }, "purpose": { "type": "string", - "minLength": 3, - "maxLength": 1000 + "minLength": 1, + "maxLength": 500, + "pattern": "\\S" }, "tabId": { - "type": "string" + "type": "string", + "minLength": 1, + "maxLength": 128 }, "ref": { - "type": "string" + "type": "string", + "minLength": 1, + "maxLength": 256 }, "selector": { "type": "string", @@ -41,42 +46,68 @@ }, "url": { "type": "string", + "maxLength": 4096, "format": "uri", "pattern": "^https?://" }, "value": { - "type": "string" + "type": "string", + "maxLength": 262144 }, "valueRef": { "type": "string", + "maxLength": 256, "pattern": "^ref:" }, "dataRef": { "type": "string", + "maxLength": 256, "pattern": "^ref:" }, "values": { "type": "array", + "maxItems": 256, "items": { - "type": "string" + "type": "string", + "maxLength": 8192 } }, "key": { - "type": "string" + "type": "string", + "minLength": 1, + "maxLength": 100 }, "text": { - "type": "string" + "type": "string", + "maxLength": 262144 }, "expression": { - "type": "string" + "type": "string", + "minLength": 1, + "maxLength": 100000 + }, + "emulation": { + "$ref": "#/$defs/emulation" + }, + "query": { + "$ref": "#/$defs/query" + }, + "extraction": { + "$ref": "#/$defs/structuredExtractionLimits" }, "path": { - "type": "string" + "type": "string", + "minLength": 1, + "maxLength": 32768 }, "paths": { "type": "array", + "minItems": 1, + "maxItems": 32, "items": { - "type": "string" + "type": "string", + "minLength": 1, + "maxLength": 32768 } }, "timeoutMs": { @@ -99,16 +130,24 @@ "maximum": 100 }, "x": { - "type": "number" + "type": "number", + "minimum": 0, + "maximum": 100000 }, "y": { - "type": "number" + "type": "number", + "minimum": 0, + "maximum": 100000 }, "deltaX": { - "type": "number" + "type": "number", + "minimum": -100000, + "maximum": 100000 }, "deltaY": { - "type": "number" + "type": "number", + "minimum": -100000, + "maximum": 100000 }, "button": { "enum": [ @@ -128,7 +167,9 @@ "maximum": 100 }, "targetRef": { - "type": "string" + "type": "string", + "minLength": 1, + "maxLength": 256 }, "route": { "$ref": "#/$defs/networkRoute" @@ -137,6 +178,56 @@ "type": "string", "pattern": "^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$" }, + "lockOwner": { + "type": "string", + "minLength": 1, + "maxLength": 200 + }, + "lockTokenRef": { + "type": "string", + "pattern": "^ref:[A-Za-z0-9._:/-]{1,240}$" + }, + "lockTtlMs": { + "type": "integer", + "minimum": 1000, + "maximum": 86400000 + }, + "stateName": { + "type": "string", + "pattern": "^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$" + }, + "passphraseRef": { + "type": "string", + "pattern": "^ref:[A-Za-z0-9._:/-]{1,240}$" + }, + "outputFormat": { + "enum": [ + "json", + "ndjson", + "har" + ] + }, + "requireStable": { + "type": "boolean" + }, + "includeBounds": { + "type": "boolean" + }, + "method": { + "type": "string", + "minLength": 1, + "maxLength": 32 + }, + "status": { + "type": "integer", + "minimum": 0, + "maximum": 999 + }, + "resourceType": { + "type": "string", + "minLength": 1, + "maxLength": 64 + }, "limit": { "type": "integer", "minimum": 1, @@ -151,10 +242,133 @@ ] }, "approvalId": { - "type": "string" + "type": "string", + "minLength": 1, + "maxLength": 512 } }, + "allOf": [ + { + "if": { + "properties": { "kind": { "const": "extract.structured" } }, + "required": ["kind"] + }, + "then": { + "anyOf": [ + { "not": { "anyOf": [{ "required": ["ref"] }, { "required": ["selector"] }, { "required": ["xpath"] }, { "required": ["frame"] }] } }, + { "$ref": "#/$defs/exactTarget" } + ] + }, + "else": { + "not": { "required": ["extraction"] } + } + }, + { + "if": { "properties": { "kind": { "const": "navigate" } }, "required": ["kind"] }, + "then": { "required": ["url"] } + }, + { + "if": { + "properties": { + "kind": { + "enum": ["click", "doubleClick", "fill", "type", "hover", "focus", "check", "uncheck", "select", "download"] + } + }, + "required": ["kind"] + }, + "then": { "$ref": "#/$defs/exactTarget" } + }, + { + "if": { "properties": { "kind": { "const": "press" } }, "required": ["kind"] }, + "then": { "allOf": [{ "$ref": "#/$defs/exactTarget" }, { "required": ["key"] }] } + }, + { + "if": { "properties": { "kind": { "const": "drag" } }, "required": ["kind"] }, + "then": { "allOf": [{ "$ref": "#/$defs/exactTarget" }, { "required": ["targetRef"] }] } + }, + { + "if": { "properties": { "kind": { "enum": ["mouse.move", "mouse.click"] } }, "required": ["kind"] }, + "then": { "required": ["x", "y"] } + }, + { + "if": { "properties": { "kind": { "enum": ["keyboard.down", "keyboard.up"] } }, "required": ["kind"] }, + "then": { "required": ["key"] } + }, + { + "if": { "properties": { "kind": { "const": "keyboard.insertText" } }, "required": ["kind"] }, + "then": { "required": ["value"] } + }, + { + "if": { "properties": { "kind": { "const": "upload" } }, "required": ["kind"] }, + "then": { + "allOf": [ + { "$ref": "#/$defs/exactTarget" }, + { "oneOf": [{ "required": ["path"], "not": { "required": ["paths"] } }, { "required": ["paths"], "not": { "required": ["path"] } }] } + ] + } + }, + { + "if": { "properties": { "kind": { "const": "evaluate" } }, "required": ["kind"] }, + "then": { "required": ["expression"] } + }, + { + "if": { "properties": { "kind": { "const": "emulation.set" } }, "required": ["kind"] }, + "then": { "required": ["emulation"] } + }, + { + "if": { "properties": { "kind": { "const": "network.route.add" } }, "required": ["kind"] }, + "then": { "required": ["route"] } + }, + { + "if": { "properties": { "kind": { "const": "network.route.remove" } }, "required": ["kind"] }, + "then": { "required": ["routeId"] } + }, + { + "if": { "properties": { "kind": { "enum": ["cookies.write", "storage.write"] } }, "required": ["kind"] }, + "then": { "required": ["dataRef"] } + }, + { + "if": { "properties": { "kind": { "const": "clipboard.write" } }, "required": ["kind"] }, + "then": { "required": ["valueRef"] } + }, + { + "if": { "properties": { "kind": { "enum": ["state.save", "state.load"] } }, "required": ["kind"] }, + "then": { "required": ["stateName", "passphraseRef"] } + }, + { + "if": { "properties": { "kind": { "const": "state.delete" } }, "required": ["kind"] }, + "then": { "required": ["stateName"] } + }, + { + "if": { "properties": { "kind": { "const": "tab.switch" } }, "required": ["kind"] }, + "then": { "required": ["tabId"] } + }, + { + "if": { "properties": { "kind": { "const": "tab.lock" } }, "required": ["kind"] }, + "then": { "required": ["lockOwner", "lockTokenRef"] } + }, + { + "if": { "properties": { "kind": { "const": "tab.unlock" } }, "required": ["kind"] }, + "then": { "required": ["lockTokenRef"] } + } + ], "$defs": { + "exactTarget": { + "oneOf": [ + { + "required": ["ref"], + "not": { "anyOf": [{ "required": ["selector"] }, { "required": ["xpath"] }, { "required": ["frame"] }] } + }, + { + "required": ["selector"], + "not": { "anyOf": [{ "required": ["ref"] }, { "required": ["xpath"] }] } + }, + { + "required": ["xpath"], + "not": { "anyOf": [{ "required": ["ref"] }, { "required": ["selector"] }] } + } + ] + }, "actionKind": { "enum": [ "navigate", @@ -183,15 +397,34 @@ "upload", "download", "evaluate", + "query.inspect", + "emulation.set", + "emulation.clear", + "cache.clear", + "console.clear", + "network.clear", "wait", + "challenge.resolve", "history.inspect", + "capture.paired", + "annotate.show", + "annotate.clear", + "clipboard.read", + "clipboard.write", + "network.inspect", + "network.export", "network.route.add", "network.route.remove", "network.routes.list", + "state.save", + "state.load", + "state.list", + "state.delete", "screenshot", "pdf", "snapshot", "extract", + "extract.structured", "cookies.read", "cookies.write", "storage.read", @@ -199,10 +432,177 @@ "tab.open", "tab.close", "tab.switch", + "tab.lock", + "tab.unlock", + "tab.lock.status", "trace.start", "trace.stop" ] }, + "emulation": { + "type": "object", + "additionalProperties": false, + "properties": { + "viewport": { + "type": "object", + "additionalProperties": false, + "required": [ + "width", + "height" + ], + "properties": { + "width": { + "type": "integer", + "minimum": 320, + "maximum": 7680 + }, + "height": { + "type": "integer", + "minimum": 200, + "maximum": 4320 + } + } + }, + "colorScheme": { + "enum": [ + "light", + "dark", + "no-preference" + ] + }, + "reducedMotion": { + "enum": [ + "reduce", + "no-preference" + ] + }, + "forcedColors": { + "enum": [ + "active", + "none" + ] + }, + "media": { + "type": [ + "string", + "null" + ], + "enum": [ + "screen", + "print", + null + ] + }, + "offline": { + "type": "boolean" + }, + "geolocation": { + "oneOf": [ + { + "type": "null" + }, + { + "type": "object", + "additionalProperties": false, + "required": [ + "latitude", + "longitude" + ], + "properties": { + "latitude": { + "type": "number", + "minimum": -90, + "maximum": 90 + }, + "longitude": { + "type": "number", + "minimum": -180, + "maximum": 180 + }, + "accuracy": { + "type": "number", + "minimum": 0, + "maximum": 1000000 + } + } + } + ] + }, + "permissions": { + "type": "array", + "uniqueItems": true, + "items": { + "enum": [ + "geolocation", + "notifications" + ] + } + }, + "extraHTTPHeaders": { + "type": "object", + "additionalProperties": { + "type": "string", + "maxLength": 8192 + }, + "maxProperties": 64 + } + } + }, + "query": { + "type": "object", + "additionalProperties": false, + "properties": { + "properties": { + "type": "array", + "uniqueItems": true, + "items": { + "enum": [ + "text", + "html", + "attributes", + "box", + "value", + "checked", + "visible", + "enabled", + "count" + ] + } + }, + "attributeNames": { + "type": "array", + "maxItems": 64, + "uniqueItems": true, + "items": { + "type": "string", + "minLength": 1, + "maxLength": 128 + } + }, + "all": { + "type": "boolean" + } + } + }, + "structuredExtractionLimits": { + "type": "object", + "additionalProperties": false, + "properties": { + "maxTotalChars": { "type": "integer", "minimum": 0, "maximum": 2000000 }, + "maxTextChars": { "type": "integer", "minimum": 0, "maximum": 1000000 }, + "maxHtmlChars": { "type": "integer", "minimum": 0, "maximum": 1000000 }, + "maxMarkdownChars": { "type": "integer", "minimum": 0, "maximum": 1000000 }, + "maxLinks": { "type": "integer", "minimum": 0, "maximum": 2000 }, + "maxMetadataItems": { "type": "integer", "minimum": 0, "maximum": 256 }, + "maxJsonLdItems": { "type": "integer", "minimum": 0, "maximum": 256 }, + "maxJsonLdChars": { "type": "integer", "minimum": 0, "maximum": 1000000 }, + "maxTables": { "type": "integer", "minimum": 0, "maximum": 256 }, + "maxTableRows": { "type": "integer", "minimum": 0, "maximum": 2000 }, + "maxTableColumns": { "type": "integer", "minimum": 0, "maximum": 256 }, + "maxItemChars": { "type": "integer", "minimum": 0, "maximum": 16384 }, + "maxDomNodes": { "type": "integer", "minimum": 0, "maximum": 200000 } + } + }, "frameTarget": { "type": "object", "additionalProperties": false, diff --git a/schemas/browser-memory.schema.json b/schemas/browser-memory.schema.json index b9aa8f3..fa212d3 100644 --- a/schemas/browser-memory.schema.json +++ b/schemas/browser-memory.schema.json @@ -8,14 +8,14 @@ "schemaVersion", "type", "sessionId", - "purpose", + "purposeDigest", "timestamp", "evidenceIds", "metadata" ], "properties": { "schemaVersion": { - "const": "cockroach.browser-memory.v1" + "const": "cockroach.browser-memory.v2" }, "type": { "type": "string", @@ -28,9 +28,9 @@ "actor": { "type": "string" }, - "purpose": { + "purposeDigest": { "type": "string", - "minLength": 3 + "pattern": "^sha256:[a-f0-9]{64}$" }, "timestamp": { "type": "string", diff --git a/schemas/session.schema.json b/schemas/session.schema.json index f7a9944..6159d8f 100644 --- a/schemas/session.schema.json +++ b/schemas/session.schema.json @@ -16,8 +16,9 @@ }, "purpose": { "type": "string", - "minLength": 3, - "maxLength": 1000 + "minLength": 1, + "maxLength": 500, + "pattern": "\\S" }, "actor": { "type": "string", @@ -53,6 +54,9 @@ "minLength": 1, "maxLength": 128 }, + "browserProvider": { + "$ref": "#/$defs/browserProvider" + }, "locale": { "type": "string", "minLength": 2, @@ -94,6 +98,141 @@ } }, "$defs": { + "browserProvider": { + "oneOf": [ + { + "type": "object", + "additionalProperties": false, + "required": ["kind"], + "properties": { + "kind": { "const": "bundled" }, + "extensions": { "$ref": "#/$defs/browserExtensions" }, + "arguments": { "$ref": "#/$defs/browserArguments" }, + "persistentProfile": { "$ref": "#/$defs/persistentProfileName" } + } + }, + { + "type": "object", + "additionalProperties": false, + "required": ["kind"], + "properties": { + "kind": { "const": "system" }, + "channel": { "$ref": "#/$defs/browserChannel" }, + "extensions": { "$ref": "#/$defs/browserExtensions" }, + "arguments": { "$ref": "#/$defs/browserArguments" }, + "persistentProfile": { "$ref": "#/$defs/persistentProfileName" } + } + }, + { + "type": "object", + "additionalProperties": false, + "required": ["kind", "executablePath"], + "properties": { + "kind": { "const": "custom" }, + "executablePath": { "$ref": "#/$defs/browserExecutablePath" }, + "extensions": { "$ref": "#/$defs/browserExtensions" }, + "arguments": { "$ref": "#/$defs/browserArguments" }, + "persistentProfile": { "$ref": "#/$defs/persistentProfileName" } + } + }, + { + "type": "object", + "additionalProperties": false, + "required": ["kind", "cdpEndpoint"], + "properties": { + "kind": { "const": "cdp" }, + "cdpEndpoint": { "$ref": "#/$defs/cdpEndpoint" } + } + }, + { + "type": "object", + "additionalProperties": false, + "required": ["kind", "implementation", "executablePath", "allowExperimentalCapabilities"], + "properties": { + "kind": { "const": "lightweight" }, + "implementation": { "const": "obscura" }, + "executablePath": { "$ref": "#/$defs/browserExecutablePath" }, + "expectedSha256": { "$ref": "#/$defs/sha256" }, + "startupTimeoutMs": { "$ref": "#/$defs/lightweightStartupTimeout" }, + "rendering": { "enum": ["none", "native"] }, + "resourceProfile": { "enum": ["standard", "constrained"] }, + "allowExperimentalCapabilities": { "const": true } + } + }, + { + "type": "object", + "additionalProperties": false, + "required": ["kind", "implementation", "executablePath", "allowExperimentalCapabilities"], + "properties": { + "kind": { "const": "lightweight" }, + "implementation": { "const": "lightpanda" }, + "executablePath": { "$ref": "#/$defs/browserExecutablePath" }, + "expectedSha256": { "$ref": "#/$defs/sha256" }, + "startupTimeoutMs": { "$ref": "#/$defs/lightweightStartupTimeout" }, + "rendering": { "const": "none" }, + "resourceProfile": { "const": "standard" }, + "allowExperimentalCapabilities": { "const": true } + } + } + ] + }, + "browserChannel": { + "enum": [ + "chrome", + "chrome-beta", + "chrome-dev", + "chrome-canary", + "msedge", + "msedge-beta", + "msedge-dev", + "msedge-canary" + ] + }, + "browserExecutablePath": { + "type": "string", + "minLength": 1, + "maxLength": 32768 + }, + "cdpEndpoint": { + "type": "string", + "format": "uri", + "maxLength": 4096 + }, + "sha256": { + "type": "string", + "pattern": "^(?:sha256:)?[A-Fa-f0-9]{64}$" + }, + "lightweightStartupTimeout": { + "type": "integer", + "minimum": 250, + "maximum": 120000 + }, + "browserExtensions": { + "type": "array", + "maxItems": 16, + "uniqueItems": true, + "items": { + "type": "string", + "minLength": 1, + "maxLength": 32768 + } + }, + "browserArguments": { + "type": "array", + "maxItems": 64, + "items": { + "type": "string", + "minLength": 1, + "maxLength": 1024, + "pattern": "^(?!\\s*$)(?!\\s*--(?:[Rr][Ee][Mm][Oo][Tt][Ee]-[Dd][Ee][Bb][Uu][Gg][Gg][Ii][Nn][Gg]-(?:[Aa][Dd][Dd][Rr][Ee][Ss][Ss]|[Pp][Oo][Rr][Tt])|[Uu][Ss][Ee][Rr]-[Dd][Aa][Tt][Aa]-[Dd][Ii][Rr]|[Ll][Oo][Aa][Dd]-[Ee][Xx][Tt][Ee][Nn][Ss][Ii][Oo][Nn]|[Dd][Ii][Ss][Aa][Bb][Ll][Ee]-(?:[Ee][Xx][Tt][Ee][Nn][Ss][Ii][Oo][Nn][Ss]-[Ee][Xx][Cc][Ee][Pp][Tt]|[Ww][Ee][Bb]-[Ss][Ee][Cc][Uu][Rr][Ii][Tt][Yy])|[Nn][Oo]-[Ss][Aa][Nn][Dd][Bb][Oo][Xx])(?:=|\\s*$))[\\s\\S]+$" + } + }, + "persistentProfileName": { + "type": "string", + "minLength": 1, + "maxLength": 64, + "pattern": "^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$" + }, "policy": { "type": "object", "additionalProperties": false, diff --git a/scripts/benchmark-lightweight.mjs b/scripts/benchmark-lightweight.mjs new file mode 100644 index 0000000..9c72c5f --- /dev/null +++ b/scripts/benchmark-lightweight.mjs @@ -0,0 +1,450 @@ +import { createHash } from "node:crypto"; +import { execFile } from "node:child_process"; +import { createReadStream } from "node:fs"; +import { mkdir, readFile, readdir, stat, writeFile } from "node:fs/promises"; +import { arch, cpus, freemem, platform, release, totalmem } from "node:os"; +import { basename, dirname, isAbsolute, relative, resolve } from "node:path"; +import { fileURLToPath } from "node:url"; +import { promisify } from "node:util"; +import { chromium } from "playwright-core"; +import { ContinuousResourceSampler } from "../dist/continuous-resource-sampler.js"; +import { launchLightweightCdp } from "../dist/lightweight-cdp.js"; +import { sampleProcessTree } from "../dist/resource-usage.js"; + +const execute = promisify(execFile); +const MIB = 1024 ** 2; +const repositoryRoot = resolve(dirname(fileURLToPath(import.meta.url)), ".."); +const args = parseArguments(process.argv.slice(2)); +const executable = resolve(args.executable); +const executableInfo = await stat(executable); +if (!executableInfo.isFile()) throw new Error(`Not a regular file: ${executable}`); +const digest = await sha256(executable); +if (args.sha256 && digest !== args.sha256.toLowerCase().replace(/^sha256:/, "")) { + throw new Error(`SHA-256 mismatch: expected ${args.sha256}, got ${digest}`); +} +const executableVersion = await binaryVersion(executable); +const sourceIdentity = await collectSourceIdentity(args.output); + +const samples = []; +for (let index = 0; index < args.warmup + args.iterations; index += 1) { + const measured = index >= args.warmup; + process.stderr.write(`${measured ? "measure" : "warmup"} ${index + 1}/${args.warmup + args.iterations}\n`); + const sample = await benchmarkRun(); + if (measured) samples.push(sample); +} + +const targetBytes = args.targetMiB * MIB; +const requiredChecks = ["connect", "javascript", "dom", "forms", "screenshot", "teardown"]; +const requiredConformancePassed = samples.every((sample) => + requiredChecks.every((check) => sample.conformance[check]?.passed === true) +); +const everyRunWithinTarget = samples.every((sample) => sample.browserTree.peakRssBytes <= targetBytes); +const finalDigest = await sha256(executable); +if (finalDigest !== digest) throw new Error("The lightweight executable changed while the benchmark was running."); +const finalSourceIdentity = await collectSourceIdentity(args.output); +for (const key of ["gitCommit", "sourceFileCount", "sourceTreeSha256", "runtimeBuildFileCount", "runtimeBuildSha256", "benchmarkScriptSha256"]) { + if (sourceIdentity[key] !== finalSourceIdentity[key]) { + throw new Error(`Benchmark input changed while the run was in progress: sourceIdentity.${key}.`); + } +} +const report = { + schemaVersion: "cockroach.lightweight-browser-benchmark.v2", + generatedAt: new Date().toISOString(), + implementation: args.implementation, + resourceProfile: args.resourceProfile, + renderingPolicy: "visual-actions-denied", + executable: { + name: basename(executable), + bytes: executableInfo.size, + sha256: digest, + version: executableVersion + }, + sourceIdentity, + machine: { + platform: platform(), + architecture: arch(), + release: release(), + logicalCpuCount: cpus().length, + cpuModel: cpus()[0]?.model ?? "unavailable", + totalMemoryBytes: totalmem(), + freeMemoryBytesAtReport: freemem(), + node: process.version + }, + methodology: { + warmup: args.warmup, + iterations: args.iterations, + settleMs: args.settleMs, + startupTimeoutMs: args.startupTimeoutMs, + actionTimeoutMs: args.actionTimeoutMs, + teardownTimeoutMs: args.teardownTimeoutMs, + resourceSamples: args.resourceSamples, + resourceSampleIntervalMs: args.resourceSampleIntervalMs, + targetMiB: args.targetMiB, + targetDefinition: "Every measured owned-browser process-tree RSS sample is at or below the target and every required CDP/JavaScript/DOM/form/screenshot-preflight/teardown check passes.", + memoryDefinition: "Aggregate RSS for the complete owned browser process tree; the Node coordinator is disclosed separately and is not hidden in browser RSS.", + renderingControl: "Cockroach Browser declares a no-visual-action policy and rejects capture at preflight. This does not assert that the selected engine binary exposes or received a renderer-disable launch switch.", + workload: "One runtime-owned loopback CDP server, one non-visual data-URL document, JavaScript evaluation, DOM query, input, and click.", + requiredChecks, + optionalChecks: [], + energy: { available: false, reason: "No calibrated platform energy counter is available; CPU time is not watts." } + }, + verdict: { + passed: everyRunWithinTarget && requiredConformancePassed, + everyRunWithinTarget, + requiredConformancePassed, + targetBytes, + maxObservedBrowserTreeRssBytes: Math.max(...samples.map((sample) => sample.browserTree.peakRssBytes)) + }, + summary: { + peakBrowserTreeRssBytes: distribution(samples.map((sample) => sample.browserTree.peakRssBytes)), + browserCpuTimeMs: distribution(samples.map((sample) => sample.browserTree.cpuTimeMs)), + processCount: distribution(samples.map((sample) => sample.browserTree.processCount)), + launchMs: distribution(samples.map((sample) => sample.timings.launchMs)), + connectMs: distribution(samples.map((sample) => sample.timings.connectMs)), + workloadMs: distribution(samples.map((sample) => sample.timings.workloadMs)), + shutdownMs: distribution(samples.map((sample) => sample.timings.shutdownMs)), + coordinatorPeakRssBytes: distribution(samples.map((sample) => sample.coordinator.peakRssBytes)) + }, + samples +}; + +const output = resolve(args.output); +await mkdir(dirname(output), { recursive: true }); +await writeFile(output, `${JSON.stringify(report, null, 2)}\n`, "utf8"); +process.stdout.write(`${JSON.stringify({ ok: true, output, verdict: report.verdict, summary: report.summary }, null, 2)}\n`); +if (!report.verdict.passed) process.exitCode = 2; + +async function benchmarkRun() { + const timings = {}; + const coordinatorRss = [process.memoryUsage().rss]; + const coordinatorCpuBefore = process.cpuUsage(); + let managed; + let browser; + let peak; + let failure; + let operationError; + let samplingError; + let teardownError; + let resourceSampler; + const conformance = {}; + try { + managed = await timed(timings, "launchMs", () => launchLightweightCdp({ + executablePath: executable, + implementation: args.implementation, + expectedSha256: digest, + startupTimeoutMs: args.startupTimeoutMs, + rendering: "none", + resourceProfile: args.resourceProfile, + requiredActions: ["navigate", "evaluate", "query.inspect", "fill", "click"], + allowExperimentalCapabilities: true, + onSpawn: (pid) => { + resourceSampler = new ContinuousResourceSampler({ + intervalMs: args.resourceSampleIntervalMs, + sample: () => sampleProcessTree(pid), + onSample: () => coordinatorRss.push(process.memoryUsage().rss) + }); + resourceSampler.start(); + } + })); + browser = await timed(timings, "connectMs", () => chromium.connectOverCDP(managed.endpoint, { timeout: args.actionTimeoutMs })); + conformance.connect = passed(); + const context = browser.contexts()[0] ?? await browser.newContext(); + const page = context.pages()[0] ?? await context.newPage(); + await timed(timings, "workloadMs", async () => { + await page.goto(fixtureDataUrl(), { waitUntil: "domcontentloaded", timeout: args.actionTimeoutMs }); + const answer = await page.evaluate(() => 20 + 22); + conformance.javascript = answer === 42 ? passed() : failed(`Expected 42, received ${String(answer)}.`); + const heading = await page.locator("h1").textContent({ timeout: args.actionTimeoutMs }); + conformance.dom = heading === "Lightweight conformance" ? passed() : failed(`Unexpected heading: ${String(heading)}.`); + // The route declares no visual actionability contract. Exercise its + // explicit DOM-form semantics without pretending a coordinate-based + // pointer action occurred or that an engine renderer was disabled. + await page.locator("#name").evaluate((element, value) => { + const input = element; + input.value = value; + input.dispatchEvent(new Event("input", { bubbles: true })); + input.dispatchEvent(new Event("change", { bubbles: true })); + }, "Cockroach"); + await page.locator("#save").evaluate((element) => element.click()); + const saved = await page.locator("#save").getAttribute("data-saved", { timeout: args.actionTimeoutMs }); + conformance.forms = saved === "true" + ? passed({ actionability: "dom-non-visual" }) + : failed("The click handler did not run."); + const screenshotPreflight = managed.preflightActions(["screenshot"]); + conformance.screenshot = screenshotPreflight.ok + ? failed("The declared no-visual-action policy unexpectedly admitted screenshot capture.") + : passed({ supported: false, state: "unsupported", enforcement: "preflight" }); + }); + } catch (error) { + failure = errorMessage(error); + operationError = error; + for (const name of ["connect", "javascript", "dom", "forms"]) { + if (!conformance[name]) conformance[name] = failed(failure); + } + } finally { + if (resourceSampler) { + try { + if (managed?.running) { + await delay(args.settleMs); + for (let index = 0; index < args.resourceSamples; index += 1) { + await resourceSampler.sampleNow(); + if (index + 1 < args.resourceSamples) await delay(args.resourceSampleIntervalMs); + } + } + const observed = await resourceSampler.stop(); + peak = { + peakRssBytes: observed.peakRssBytes, + cpuTimeMs: observed.cpuTimeMs, + processCount: observed.processCount, + sampleCount: observed.sampleCount, + observations: observed.samples.map((sample) => ({ + sampledAt: sample.sampledAt, + rssBytes: sample.rssBytes, + cpuTimeMs: sample.cpuTimeMs, + processCount: sample.processCount + })) + }; + } catch (error) { + samplingError = error; + } + } + const shutdownStarted = performance.now(); + try { + await managed?.close(args.teardownTimeoutMs); + } catch (error) { + teardownError = error; + } + // The runtime-owned process tree is the authoritative lifecycle. Closing + // it first can make the CDP transport reject; that expected disconnect is + // not a teardown failure once the owned root is verified dead. + const transportDisposed = browser + ? await settlesWithin(browser.close(), args.teardownTimeoutMs) + : true; + if (!transportDisposed && !teardownError) { + teardownError = new Error(`The CDP transport did not settle within ${args.teardownTimeoutMs} milliseconds after process teardown.`); + } + timings.shutdownMs = performance.now() - shutdownStarted; + conformance.teardown = teardownError || managed?.running + ? failed(teardownError ? errorMessage(teardownError) : "The owned browser root process remained live after close.") + : passed({ rootProcessExited: true, transportDisposed: true }); + coordinatorRss.push(process.memoryUsage().rss); + } + if (operationError && !managed) throw operationError; + if (samplingError) throw samplingError; + if (!peak) throw new Error("No complete owned-process-tree resource sample was captured."); + const coordinatorCpu = process.cpuUsage(coordinatorCpuBefore); + return { + timings, + browserTree: peak, + coordinator: { + peakRssBytes: Math.max(...coordinatorRss), + cpuTimeMs: (coordinatorCpu.user + coordinatorCpu.system) / 1_000 + }, + conformance, + ...(failure ? { failure } : {}) + }; +} + +function fixtureDataUrl() { + const html = "Lightweight conformance

Lightweight conformance

"; + return `data:text/html;charset=utf-8,${encodeURIComponent(html)}`; +} + +function passed(details = {}) { return { passed: true, ...details }; } +function failed(reason) { return { passed: false, reason: String(reason).slice(0, 1_024) }; } +function errorMessage(error) { return error instanceof Error ? error.message : String(error); } + +async function timed(target, name, operation) { + const started = performance.now(); + try { return await operation(); } + finally { target[name] = performance.now() - started; } +} + +function distribution(values) { + const finite = values.filter(Number.isFinite).sort((a, b) => a - b); + if (!finite.length) return { median: null, p95: null, min: null, max: null }; + return { + median: percentile(finite, 0.5), + p95: percentile(finite, 0.95), + min: finite[0], + max: finite.at(-1) + }; +} + +function percentile(values, point) { + const index = Math.min(values.length - 1, Math.max(0, Math.ceil(values.length * point) - 1)); + return values[index]; +} + +async function sha256(path) { + const hash = createHash("sha256"); + for await (const chunk of createReadStream(path)) hash.update(chunk); + return hash.digest("hex"); +} + +async function collectSourceIdentity(outputPath) { + const commit = await gitOutput(repositoryRoot, ["rev-parse", "HEAD"]); + const status = await gitOutput(repositoryRoot, ["status", "--porcelain=v1", "--untracked-files=all"]); + const listed = (await gitOutput(repositoryRoot, ["ls-files", "-z", "--cached", "--others", "--exclude-standard"])) + .split("\0") + .filter(Boolean) + .map((path) => resolve(repositoryRoot, path)); + const excludedOutput = resolve(outputPath); + const generatedArtifactRoots = [ + resolve(repositoryRoot, "docs", "benchmarks", "artifacts"), + resolve(repositoryRoot, "output", "resource-benchmarks") + ]; + const sourceFiles = []; + for (const path of listed) { + if (path === excludedOutput) continue; + if (generatedArtifactRoots.some((root) => isBelow(root, path))) continue; + if (!isProofInput(repositoryRoot, path)) continue; + if ((await stat(path).catch(() => undefined))?.isFile()) sourceFiles.push(path); + } + const runtimeFiles = await filesBelow(resolve(repositoryRoot, "dist")); + return { + gitCommit: commit.trim(), + workingTreeDirty: Boolean(status.trim()), + sourceScope: "Runtime, schemas, benchmark/package/build configuration, and tests; generated documentation and benchmark artifacts are excluded.", + identityNormalization: "Relative paths use forward slashes and text line endings are normalized to LF before hashing.", + sourceFileCount: sourceFiles.length, + sourceTreeSha256: await sha256Files(repositoryRoot, sourceFiles), + runtimeBuildFileCount: runtimeFiles.length, + runtimeBuildSha256: await sha256Files(repositoryRoot, runtimeFiles), + benchmarkScriptSha256: await sha256CanonicalText(new URL(import.meta.url)) + }; +} + +function isProofInput(repositoryRoot, path) { + const candidate = relative(repositoryRoot, path).replaceAll("\\", "/"); + if (candidate.startsWith("src/") || candidate.startsWith("schemas/") || candidate.startsWith("scripts/") || candidate.startsWith("test/")) { + return true; + } + return candidate === "package.json" + || candidate === "package-lock.json" + || candidate === "server.json" + || /^tsconfig(?:\.[^.]+)?\.json$/.test(candidate); +} + +function isBelow(root, path) { + const candidate = relative(root, path); + return candidate !== "" && candidate !== ".." && !candidate.startsWith(`..${process.platform === "win32" ? "\\" : "/"}`) && !isAbsolute(candidate); +} + +async function gitOutput(root, arguments_) { + const result = await execute("git", ["-C", root, ...arguments_], { + encoding: "utf8", + timeout: 15_000, + windowsHide: true, + maxBuffer: 16 * 1024 * 1024 + }); + return String(result.stdout); +} + +async function filesBelow(root) { + const files = []; + const visit = async (directory) => { + for (const entry of await readdir(directory, { withFileTypes: true })) { + const path = resolve(directory, entry.name); + if (entry.isDirectory()) await visit(path); + else if (entry.isFile()) files.push(path); + } + }; + await visit(root); + return files.sort(); +} + +async function sha256Files(root, files) { + const hash = createHash("sha256"); + for (const path of [...files].sort()) { + hash.update(relative(root, path).replaceAll("\\", "/")); + hash.update("\0"); + hash.update((await readFile(path, "utf8")).replace(/\r\n?/g, "\n")); + hash.update("\0"); + } + return hash.digest("hex"); +} + +async function sha256CanonicalText(path) { + return createHash("sha256") + .update((await readFile(path, "utf8")).replace(/\r\n?/g, "\n")) + .digest("hex"); +} + +async function binaryVersion(path) { + for (const versionArgs of [["--version"], ["version"]]) { + try { + const result = await execute(path, versionArgs, { timeout: 5_000, windowsHide: true, maxBuffer: 64 * 1024 }); + const text = `${result.stdout}\n${result.stderr}`.trim(); + if (text) return text.slice(0, 2_048); + } catch { + // Try the next conventional version command. + } + } + return "unavailable"; +} + +function parseArguments(values) { + const executable = value(values, "--executable"); + if (!executable) throw new Error("--executable is required."); + const implementation = value(values, "--implementation") ?? "obscura"; + if (implementation !== "obscura" && implementation !== "lightpanda") { + throw new Error("--implementation must be obscura or lightpanda."); + } + const sha = value(values, "--sha256"); + if (sha && !/^(?:sha256:)?[a-f0-9]{64}$/i.test(sha)) throw new Error("--sha256 must contain 64 hexadecimal characters."); + const resourceProfile = value(values, "--resource-profile") ?? "standard"; + if (resourceProfile !== "standard" && resourceProfile !== "constrained") { + throw new Error("--resource-profile must be standard or constrained."); + } + if (implementation !== "obscura" && resourceProfile === "constrained") { + throw new Error("--resource-profile constrained is available only for Obscura."); + } + return { + executable, + implementation, + resourceProfile, + ...(sha ? { sha256: sha } : {}), + iterations: integer(values, "--iterations", 5, 1, 100), + warmup: integer(values, "--warmup", 1, 0, 20), + settleMs: integer(values, "--settle-ms", 500, 0, 60_000), + targetMiB: integer(values, "--target-mib", 30, 1, 32_768), + startupTimeoutMs: integer(values, "--startup-timeout-ms", 15_000, 250, 120_000), + actionTimeoutMs: integer(values, "--action-timeout-ms", 10_000, 250, 120_000), + teardownTimeoutMs: integer(values, "--teardown-timeout-ms", 5_000, 250, 120_000), + resourceSamples: integer(values, "--resource-samples", 3, 1, 20), + resourceSampleIntervalMs: integer(values, "--resource-sample-interval-ms", 100, 25, 10_000), + output: value(values, "--output") ?? `output/resource-benchmarks/lightweight-${implementation}-${resourceProfile}-${new Date().toISOString().replaceAll(":", "-")}.json` + }; +} + +function value(values, name) { + const index = values.indexOf(name); + return index >= 0 ? values[index + 1] : undefined; +} + +function integer(values, name, fallback, minimum, maximum) { + const raw = value(values, name); + if (raw === undefined) return fallback; + const parsed = Number(raw); + if (!Number.isSafeInteger(parsed) || parsed < minimum || parsed > maximum) { + throw new Error(`${name} must be an integer between ${minimum} and ${maximum}.`); + } + return parsed; +} + +function delay(ms) { return new Promise((settle) => setTimeout(settle, ms)); } + +function settlesWithin(promise, timeoutMs) { + return new Promise((settle) => { + let settled = false; + const finish = (value) => { + if (settled) return; + settled = true; + clearTimeout(timer); + settle(value); + }; + const timer = setTimeout(() => finish(false), timeoutMs); + Promise.resolve(promise).then(() => finish(true), () => finish(true)); + }); +} diff --git a/scripts/build-whitepaper-pdf.py b/scripts/build-whitepaper-pdf.py index 3953d4c..7b425f3 100644 --- a/scripts/build-whitepaper-pdf.py +++ b/scripts/build-whitepaper-pdf.py @@ -4,8 +4,12 @@ from __future__ import annotations import html +import hashlib +import json import re import shutil +import subprocess +import sys from pathlib import Path from reportlab import rl_config @@ -34,9 +38,16 @@ ROOT = Path(__file__).resolve().parents[1] SOURCE = ROOT / "docs" / "whitepaper.md" -OUTPUT = ROOT / "output" / "pdf" / "Cockroach-Browser-Technical-White-Paper-v1.1.pdf" +PAPER_VERSION = "1.2" +IMPLEMENTATION_VERSION = "0.5.0-rc.1" +PUBLICATION_DATE = "2026-09-03" +OUTPUT = ROOT / "output" / "pdf" / "Cockroach-Browser-Technical-White-Paper-v1.2.pdf" PUBLIC_COPY = ROOT / "docs" / OUTPUT.name SITE_COPY = ROOT / "site" / "paper" / OUTPUT.name +BUILD_MANIFEST = ROOT / "docs" / "Cockroach-Browser-Technical-White-Paper-v1.2-build.json" +CHECKSUM = ROOT / "docs" / "Cockroach-Browser-Technical-White-Paper-v1.2.sha256" +SITE_BUILD_MANIFEST = ROOT / "site" / "paper" / BUILD_MANIFEST.name +SITE_CHECKSUM = ROOT / "site" / "paper" / CHECKSUM.name LOGO = ROOT / "site" / "assets" / "logo.png" PAGE_WIDTH, PAGE_HEIGHT = A4 @@ -58,6 +69,9 @@ rl_config.invariant = 1 +FONT_FILES: list[Path] = [] + + def register_fonts() -> tuple[str, str, str, str]: choices = [ ("C:/Windows/Fonts/arial.ttf", "C:/Windows/Fonts/arialbd.ttf", @@ -68,6 +82,7 @@ def register_fonts() -> tuple[str, str, str, str]: for regular, bold, italic, mono in choices: paths = [Path(value) for value in (regular, bold, italic, mono)] if all(path.exists() for path in paths): + FONT_FILES.extend(paths) pdfmetrics.registerFont(TTFont("CBBody", str(paths[0]))) pdfmetrics.registerFont(TTFont("CBBody-Bold", str(paths[1]))) pdfmetrics.registerFont(TTFont("CBBody-Italic", str(paths[2]))) @@ -141,9 +156,9 @@ def __init__(self, filename: str): rightMargin=RIGHT, topMargin=TOP, bottomMargin=BOTTOM, - title="Cockroach Browser: A Local-First Browser Runtime for AI Agents", + title="Cockroach Browser: A Governed Multi-Engine Runtime for AI Agents", author="Ajnas N B", - subject="A local-first, authenticated browser runtime for AI agents", + subject="A governed multi-engine browser runtime for AI agents", creator="Cockroach Browser white-paper build", pageCompression=1, ) @@ -170,7 +185,7 @@ def cover_page(canvas, _doc): canvas.setFillColor(colors.HexColor("#91A49E")) canvas.setFont(MONO, 7.2) canvas.drawString(LEFT, 10 * mm, "COCKROACH BROWSER / TECHNICAL PAPER") - canvas.drawRightString(PAGE_WIDTH - RIGHT, 10 * mm, "AUGUST 2026") + canvas.drawRightString(PAGE_WIDTH - RIGHT, 10 * mm, "SEPTEMBER 3, 2026") canvas.restoreState() @staticmethod @@ -190,7 +205,7 @@ def body_page(canvas, doc): canvas.setStrokeColor(RULE) canvas.line(LEFT, 13 * mm, PAGE_WIDTH - RIGHT, 13 * mm) canvas.setFont(BODY, 7.2) - canvas.drawString(LEFT, 8 * mm, "Ajnas N B - Technical white paper v1.1") + canvas.drawString(LEFT, 8 * mm, "Ajnas N B - Technical white paper v1.2") canvas.drawRightString(PAGE_WIDTH - RIGHT, 8 * mm, str(doc.page)) canvas.restoreState() @@ -366,18 +381,18 @@ def cover_story() -> list: Spacer(1, 19 * mm), logo, Spacer(1, 10 * mm), - Paragraph("COCKROACH BROWSER / VERSION 0.3.0", STYLES["cover-kicker"]), - Paragraph("A local-first browser runtime for AI agents", STYLES["cover-title"]), + Paragraph("COCKROACH BROWSER / VERSION 0.5.0-RC.1", STYLES["cover-kicker"]), + Paragraph("A governed multi-engine runtime for AI agents", STYLES["cover-title"]), Paragraph( - "The browser runtime your AI agents can use without inheriting your whole machine.", + "Full browsers for fidelity. A verified lightweight lane for compatible non-visual work. Explicit authority for both.", STYLES["cover-subtitle"], ), HRFlowable(width="100%", thickness=0.8, color=colors.HexColor("#24433C")), Spacer(1, 8 * mm), Paragraph( "Author: Ajnas N B
" - "Paper version: 1.1
" - "Date: August 2026
" + "Paper version: 1.2
" + "Date: September 3, 2026
" "Concept DOI: 10.5281/zenodo.21701791
" "Software: AGPL-3.0-or-later
" "Paper: Creative Commons Attribution 4.0 International
" @@ -415,6 +430,13 @@ def toc_story() -> list: def build() -> None: + replace = "--replace" in sys.argv[1:] + authored = [OUTPUT, PUBLIC_COPY, SITE_COPY, BUILD_MANIFEST, CHECKSUM, + SITE_BUILD_MANIFEST, SITE_CHECKSUM] + existing = [path for path in authored if path.exists()] + if existing and not replace: + joined = ", ".join(str(path.relative_to(ROOT)) for path in existing) + raise FileExistsError(f"Refusing to overwrite v1.2 artifacts: {joined}. Pass --replace explicitly.") text = SOURCE.read_text(encoding="utf-8") start = text.find("## Abstract") if start < 0: @@ -422,9 +444,67 @@ def build() -> None: OUTPUT.parent.mkdir(parents=True, exist_ok=True) story = cover_story() + toc_story() + parse_markdown(text[start:]) PaperTemplate(str(OUTPUT)).multiBuild(story) + PUBLIC_COPY.parent.mkdir(parents=True, exist_ok=True) + SITE_COPY.parent.mkdir(parents=True, exist_ok=True) shutil.copyfile(OUTPUT, PUBLIC_COPY) shutil.copyfile(OUTPUT, SITE_COPY) - print(OUTPUT) + write_provenance() + print(json.dumps({ + "pdf": str(PUBLIC_COPY), + "sha256": sha256(PUBLIC_COPY), + "manifest": str(BUILD_MANIFEST), + }, indent=2)) + + +def sha256(path: Path) -> str: + digest = hashlib.sha256() + with path.open("rb") as handle: + for chunk in iter(lambda: handle.read(1024 * 1024), b""): + digest.update(chunk) + return digest.hexdigest() + + +def write_provenance() -> None: + git_commit = subprocess.run( + ["git", "rev-parse", "HEAD"], cwd=ROOT, check=True, + capture_output=True, text=True, + ).stdout.strip() + git_dirty = bool(subprocess.run( + ["git", "status", "--porcelain"], cwd=ROOT, check=True, + capture_output=True, text=True, + ).stdout.strip()) + inputs = [SOURCE, Path(__file__).resolve(), LOGO, *FONT_FILES] + pdf_digest = sha256(OUTPUT) + manifest = { + "schemaVersion": "cockroach.white-paper-build.v1", + "paperVersion": PAPER_VERSION, + "implementationVersion": IMPLEMENTATION_VERSION, + "publicationDate": PUBLICATION_DATE, + "gitBaseCommit": git_commit, + "workingTreeDirty": git_dirty, + "inputs": [ + { + "path": str(path.relative_to(ROOT)).replace("\\", "/") + if path.is_relative_to(ROOT) else str(path).replace("\\", "/"), + "bytes": path.stat().st_size, + "sha256": sha256(path), + } + for path in inputs + ], + "outputs": [ + { + "path": str(path.relative_to(ROOT)).replace("\\", "/"), + "bytes": path.stat().st_size, + "sha256": sha256(path), + } + for path in (OUTPUT, PUBLIC_COPY, SITE_COPY) + ], + "pdfSha256": pdf_digest, + } + BUILD_MANIFEST.write_text(json.dumps(manifest, indent=2) + "\n", encoding="utf-8") + CHECKSUM.write_text(f"{pdf_digest} {PUBLIC_COPY.name}\n", encoding="utf-8") + shutil.copyfile(BUILD_MANIFEST, SITE_BUILD_MANIFEST) + shutil.copyfile(CHECKSUM, SITE_CHECKSUM) if __name__ == "__main__": diff --git a/scripts/check-package.mjs b/scripts/check-package.mjs index 5b1083c..0881978 100644 --- a/scripts/check-package.mjs +++ b/scripts/check-package.mjs @@ -8,6 +8,7 @@ const root = resolve(dirname(fileURLToPath(import.meta.url)), ".."); const packageJson = await json("package.json"); const packageLock = await json("package-lock.json"); const server = await json("server.json"); +const publicSchemaFiles = ["action.schema.json", "browser-memory.schema.json", "session.schema.json"]; assert(packageJson.name === "cockroach-browser", "package name must be cockroach-browser"); assert(/^\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?$/.test(packageJson.version), "package version must be valid semver"); @@ -16,6 +17,10 @@ assert(packageJson.author === "Ajnas NB", "package author must identify Ajnas NB assert(packageJson.mcpName === "io.github.AjnasNB/cockroach-browser", "mcpName must match the authorized, case-sensitive MCP registry identity"); assert(packageJson.engines?.node === "^22.0.0 || ^24.0.0 || ^26.0.0", "Node support must remain 22, 24, and 26"); assert(packageLock.name === packageJson.name && packageLock.version === packageJson.version, "package-lock identity is stale"); +for (const schemaFile of publicSchemaFiles) { + const subpath = `./schemas/${schemaFile}`; + assert(packageJson.exports?.[subpath] === subpath, `package exports are missing ${subpath}`); +} assert(server.name === packageJson.mcpName, "server.json name must match package mcpName"); assert(server.version === packageJson.version, "server.json version must match package version"); assert( @@ -66,7 +71,9 @@ const requiredSourceFiles = [ "dist/integrations/qarinah.js", "dist/integrations/crawler.js", "dist/integrations/productloop.js", + "schemas/action.schema.json", "schemas/browser-memory.schema.json", + "schemas/session.schema.json", "docs/compatibility/browser-api-surface.json", "sdks/python/cockroach_browser/client.py", "sdks/java/src/main/java/io/cockroach/browser/Client.java", diff --git a/scripts/test-packed-consumer.mjs b/scripts/test-packed-consumer.mjs index f2e7ba2..0b87609 100644 --- a/scripts/test-packed-consumer.mjs +++ b/scripts/test-packed-consumer.mjs @@ -38,7 +38,9 @@ import { BrowserAgent } from "cockroach-browser/agent"; import { OpenAICompatibleModelGateway } from "cockroach-browser/model-gateway"; import { BrowserFleet, LocalBrowserFleetProvider } from "cockroach-browser/fleet"; import { createRawCdpSession } from "cockroach-browser/cdp"; +import actionSchema from "cockroach-browser/schemas/action.schema.json" with { type: "json" }; import memorySchema from "cockroach-browser/schemas/browser-memory.schema.json" with { type: "json" }; +import sessionSchema from "cockroach-browser/schemas/session.schema.json" with { type: "json" }; import server from "cockroach-browser/server.json" with { type: "json" }; if (CAPABILITIES.length < 73) throw new Error("operator capabilities missing"); if (typeof BrowserClient !== "function") throw new Error("client missing"); @@ -54,7 +56,9 @@ if (typeof playwrightTest !== "function" || typeof playwrightExpect !== "functio for (const exported of [BidiSession, WebDriverClient, BrowserAgent, OpenAICompatibleModelGateway, BrowserFleet, LocalBrowserFleetProvider, createRawCdpSession]) { if (typeof exported !== "function") throw new Error("expanded platform export missing"); } -if (memorySchema.properties?.schemaVersion?.const !== "cockroach.browser-memory.v1") throw new Error("memory schema mismatch"); +if (actionSchema.$id !== "https://cockroachbrowser.com/schemas/action.schema.json" || !actionSchema.$defs?.actionKind) throw new Error("action schema mismatch"); +if (memorySchema.properties?.schemaVersion?.const !== "cockroach.browser-memory.v2") throw new Error("memory schema mismatch"); +if (sessionSchema.$id !== "https://cockroachbrowser.com/schemas/session.schema.json" || !sessionSchema.properties?.browserProvider) throw new Error("session schema mismatch"); if (server.name !== "io.github.AjnasNB/cockroach-browser") throw new Error("MCP identity mismatch"); process.stdout.write(JSON.stringify({ ok: true, capabilities: CAPABILITIES.length }) + "\\n"); ` diff --git a/scripts/verify-lightweight-proof.mjs b/scripts/verify-lightweight-proof.mjs new file mode 100644 index 0000000..c033376 --- /dev/null +++ b/scripts/verify-lightweight-proof.mjs @@ -0,0 +1,338 @@ +import { createHash } from "node:crypto"; +import { execFile } from "node:child_process"; +import { createReadStream } from "node:fs"; +import { readFile, readdir, stat } from "node:fs/promises"; +import { dirname, isAbsolute, relative, resolve } from "node:path"; +import { fileURLToPath } from "node:url"; +import { promisify } from "node:util"; + +const execute = promisify(execFile); +const root = resolve(dirname(fileURLToPath(import.meta.url)), ".."); +const paths = [ + resolve(root, process.argv[2] ?? "docs/benchmarks/artifacts/obscura-0.2.1-constrained-non-visual-30mib-2026-09-03-rc1.json"), + resolve(root, process.argv[3] ?? "docs/benchmarks/artifacts/obscura-0.2.1-constrained-non-visual-25mib-2026-09-03-rc1.json") +]; +const proofRecordPath = resolve(root, "docs/benchmarks/obscura-non-visual-2026-09-03.md"); +const readmePath = resolve(root, "README.md"); +const reports = await Promise.all(paths.map(async (path) => JSON.parse(await readFile(path, "utf8")))); +const artifactDigests = await Promise.all(paths.map(sha256)); + +assertReport(reports[0], 30, true); +assertReport(reports[1], 25, undefined); +for (const key of [ + "gitCommit", + "workingTreeDirty", + "sourceScope", + "identityNormalization", + "sourceFileCount", + "sourceTreeSha256", + "runtimeBuildFileCount", + "runtimeBuildSha256", + "benchmarkScriptSha256" +]) { + assert(reports[0].sourceIdentity[key] === reports[1].sourceIdentity[key], `proof artifacts disagree on sourceIdentity.${key}`); +} +assert(reports[0].executable.sha256 === reports[1].executable.sha256, "proof artifacts used different binaries"); +for (const key of ["name", "bytes", "version"]) { + assert(reports[0].executable[key] === reports[1].executable[key], `proof artifacts disagree on executable.${key}`); +} +for (const key of ["platform", "architecture", "release", "logicalCpuCount", "cpuModel", "totalMemoryBytes", "node"]) { + assert(equalJson(reports[0].machine[key], reports[1].machine[key]), `proof artifacts were not measured on the same machine: machine.${key}`); +} +for (const key of [ + "warmup", + "iterations", + "settleMs", + "startupTimeoutMs", + "actionTimeoutMs", + "teardownTimeoutMs", + "resourceSamples", + "resourceSampleIntervalMs", + "targetDefinition", + "memoryDefinition", + "renderingControl", + "workload", + "requiredChecks", + "optionalChecks" +]) { + assert(equalJson(reports[0].methodology[key], reports[1].methodology[key]), `proof artifacts disagree on methodology.${key}`); +} + +const current = await collectCurrentIdentity(paths); +for (const key of [ + "sourceFileCount", + "sourceTreeSha256", + "runtimeBuildFileCount", + "runtimeBuildSha256", + "benchmarkScriptSha256" +]) { + assert(reports[0].sourceIdentity[key] === current[key], `proof artifact is stale: sourceIdentity.${key}`); +} +await assertNarrativeConsistency(reports, paths, artifactDigests, current); + +process.stdout.write(`${JSON.stringify({ + ok: true, + artifacts: paths.map((path, index) => ({ + path: relative(root, path).replaceAll("\\", "/"), + sha256: artifactDigests[index] + })), + sourceTreeSha256: current.sourceTreeSha256, + runtimeBuildSha256: current.runtimeBuildSha256, + outcomes: reports.map((report) => ({ + targetMiB: report.methodology.targetMiB, + passed: report.verdict.passed, + maxObservedBrowserTreeRssBytes: report.verdict.maxObservedBrowserTreeRssBytes + })) +}, null, 2)}\n`); + +function assertReport(report, targetMiB, requiredVerdict) { + assert(report?.schemaVersion === "cockroach.lightweight-browser-benchmark.v2", "unexpected benchmark schema"); + assert(report.implementation === "obscura", "proof must use Obscura"); + assert(report.resourceProfile === "constrained", "proof must use the constrained profile"); + assert(report.renderingPolicy === "visual-actions-denied", "proof must identify the visual-action policy without asserting renderer state"); + assert(report.executable?.name === "obscura.exe", "proof must disclose the binary name"); + assert(report.executable?.version === "obscura 0.2.1", "proof must use Obscura 0.2.1"); + assert(report.executable?.sha256 === "5b609fb46bc00da79e450fb0fbd34bd442e565b1394f4af95433e0b341078221", "proof must use the reviewed binary digest"); + assert(Number.isSafeInteger(report.executable?.bytes) && report.executable.bytes > 0, "proof must disclose a positive executable size"); + assert(Number.isFinite(Date.parse(report.generatedAt)), "proof generatedAt must be an ISO timestamp"); + assert(/^[a-f0-9]{40}$/.test(report.sourceIdentity?.gitCommit ?? ""), "proof must identify its Git base commit"); + assert(report.sourceIdentity?.workingTreeDirty === true || report.sourceIdentity?.workingTreeDirty === false, "proof must disclose working-tree state"); + assert(report.sourceIdentity?.identityNormalization === "Relative paths use forward slashes and text line endings are normalized to LF before hashing.", "proof must disclose its portable identity normalization"); + assert(/^[a-f0-9]{64}$/.test(report.sourceIdentity?.sourceTreeSha256 ?? ""), "proof must identify its source tree"); + assert(/^[a-f0-9]{64}$/.test(report.sourceIdentity?.runtimeBuildSha256 ?? ""), "proof must identify its runtime build"); + assert(/^[a-f0-9]{64}$/.test(report.sourceIdentity?.benchmarkScriptSha256 ?? ""), "proof must identify its benchmark harness"); + assert(typeof report.machine?.platform === "string" && report.machine.platform.length > 0, "proof must disclose its operating-system platform"); + assert(typeof report.machine?.architecture === "string" && report.machine.architecture.length > 0, "proof must disclose its CPU architecture"); + assert(typeof report.machine?.release === "string" && report.machine.release.length > 0, "proof must disclose its operating-system release"); + assert(typeof report.machine?.node === "string" && report.machine.node.length > 0, "proof must disclose its Node.js version"); + assert(report.methodology?.warmup === 1, "proof must include one warmup run"); + assert(report.methodology?.iterations === 20, "proof must include twenty measured runs"); + assert(report.methodology?.resourceSamples === 10, "proof must include ten explicit steady-state samples per run"); + assert(report.methodology?.resourceSampleIntervalMs === 25, "proof must sample at 25 ms intervals"); + assert(Number.isSafeInteger(report.methodology?.teardownTimeoutMs) && report.methodology.teardownTimeoutMs >= 250, "proof must disclose a bounded teardown timeout"); + assert(report.methodology?.targetMiB === targetMiB, `proof target must be ${targetMiB} MiB`); + assert(report.methodology?.renderingControl?.includes("does not assert"), "proof must disclaim unverified engine renderer state"); + assert(!/renderer (?:is|was|has been) disabled/i.test(report.methodology?.renderingControl ?? ""), "proof must not claim that the engine renderer was disabled"); + assert(report.methodology?.energy?.available === false, "proof must not invent energy measurements"); + assert(Array.isArray(report.samples) && report.samples.length === 20, "proof must retain all measured samples"); + const requiredChecks = ["connect", "javascript", "dom", "forms", "screenshot", "teardown"]; + assert(equalJson(report.methodology?.requiredChecks, requiredChecks), "proof requiredChecks must include visual-policy enforcement and teardown"); + assert(equalJson(report.methodology?.optionalChecks, []), "proof must not label a verdict-critical visual-policy check as optional"); + const targetBytes = targetMiB * 1024 ** 2; + let maximum = 0; + for (const [index, sample] of report.samples.entries()) { + const observations = sample?.browserTree?.observations; + assert(Array.isArray(observations) && observations.length >= 10, `sample ${index + 1} did not retain enough raw resource observations`); + assert(sample.browserTree.sampleCount === observations.length, `sample ${index + 1} sampleCount does not match retained observations`); + let samplePeakRss = 0; + let samplePeakCpu = 0; + let samplePeakProcesses = 0; + for (const [observationIndex, observation] of observations.entries()) { + assert(Number.isFinite(Date.parse(observation?.sampledAt)), `sample ${index + 1} observation ${observationIndex + 1} has no timestamp`); + assert(Number.isSafeInteger(observation?.rssBytes) && observation.rssBytes > 0, `sample ${index + 1} observation ${observationIndex + 1} has invalid RSS`); + assert(Number.isFinite(observation?.cpuTimeMs) && observation.cpuTimeMs >= 0, `sample ${index + 1} observation ${observationIndex + 1} has invalid CPU time`); + assert(Number.isSafeInteger(observation?.processCount) && observation.processCount >= 1, `sample ${index + 1} observation ${observationIndex + 1} has no owned browser process`); + samplePeakRss = Math.max(samplePeakRss, observation.rssBytes); + samplePeakCpu = Math.max(samplePeakCpu, observation.cpuTimeMs); + samplePeakProcesses = Math.max(samplePeakProcesses, observation.processCount); + } + assert(sample.browserTree.peakRssBytes === samplePeakRss, `sample ${index + 1} peak RSS is inconsistent with retained observations`); + assert(sample.browserTree.cpuTimeMs === samplePeakCpu, `sample ${index + 1} CPU time is inconsistent with retained observations`); + assert(sample.browserTree.processCount === samplePeakProcesses, `sample ${index + 1} process count is inconsistent with retained observations`); + for (const check of requiredChecks) { + assert(sample.conformance?.[check]?.passed === true, `sample ${index + 1} failed required check ${check}`); + } + assert(sample.conformance.teardown.rootProcessExited === true, `sample ${index + 1} did not verify root-process exit`); + assert(sample.conformance.teardown.transportDisposed === true, `sample ${index + 1} did not verify bounded CDP transport disposal`); + assert(sample.conformance?.screenshot?.passed === true && sample.conformance.screenshot.supported === false, `sample ${index + 1} did not enforce the visual-action preflight`); + for (const timing of ["launchMs", "connectMs", "workloadMs", "shutdownMs"]) { + assert(Number.isFinite(sample.timings?.[timing]) && sample.timings[timing] >= 0, `sample ${index + 1} has invalid ${timing}`); + } + assert(Number.isSafeInteger(sample.coordinator?.peakRssBytes) && sample.coordinator.peakRssBytes > 0, `sample ${index + 1} has invalid coordinator RSS`); + assert(Number.isFinite(sample.coordinator?.cpuTimeMs) && sample.coordinator.cpuTimeMs >= 0, `sample ${index + 1} has invalid coordinator CPU time`); + assert(!Object.hasOwn(sample, "failure"), `sample ${index + 1} retained an operation failure despite a passing conformance set`); + maximum = Math.max(maximum, samplePeakRss); + } + const everyRunWithinTarget = report.samples.every((sample) => + sample.browserTree.observations.every((observation) => observation.rssBytes <= targetBytes) + ); + assert(report.verdict?.targetBytes === targetBytes, "verdict targetBytes is inconsistent"); + assert(report.verdict?.maxObservedBrowserTreeRssBytes === maximum, "verdict maximum is inconsistent with retained samples"); + assert(report.verdict?.everyRunWithinTarget === everyRunWithinTarget, "memory verdict is inconsistent with retained samples"); + assert(report.verdict?.requiredConformancePassed === true, "required conformance verdict must pass"); + assert(report.verdict?.passed === everyRunWithinTarget, "combined verdict is inconsistent"); + if (requiredVerdict !== undefined) assert(report.verdict.passed === requiredVerdict, `the ${targetMiB} MiB proof has the wrong verdict`); + assertDistribution(report.summary?.peakBrowserTreeRssBytes, report.samples.map((sample) => sample.browserTree.peakRssBytes), "peak browser-tree RSS"); + assertDistribution(report.summary?.browserCpuTimeMs, report.samples.map((sample) => sample.browserTree.cpuTimeMs), "browser CPU time"); + assertDistribution(report.summary?.processCount, report.samples.map((sample) => sample.browserTree.processCount), "process count"); + assertDistribution(report.summary?.launchMs, report.samples.map((sample) => sample.timings.launchMs), "launch timing"); + assertDistribution(report.summary?.connectMs, report.samples.map((sample) => sample.timings.connectMs), "connect timing"); + assertDistribution(report.summary?.workloadMs, report.samples.map((sample) => sample.timings.workloadMs), "workload timing"); + assertDistribution(report.summary?.shutdownMs, report.samples.map((sample) => sample.timings.shutdownMs), "shutdown timing"); + assertDistribution(report.summary?.coordinatorPeakRssBytes, report.samples.map((sample) => sample.coordinator.peakRssBytes), "coordinator peak RSS"); +} + +async function assertNarrativeConsistency(reports, artifactPaths, artifactDigests, current) { + const [readme, proofRecord] = await Promise.all([ + readFile(readmePath, "utf8"), + readFile(proofRecordPath, "utf8") + ]); + const proofLink = "docs/benchmarks/obscura-non-visual-2026-09-03.md"; + assert(readme.replaceAll("\\", "/").includes(proofLink), "README must link to the current non-visual proof record"); + assert(readme.includes(reports[0].executable.sha256), "README must identify the reviewed Obscura digest"); + assert(readme.split(/\r?\n/).some((line) => /whole[- ]app/i.test(line) && /not|doesn't|does not/i.test(line)), "README must disclaim a whole-app memory guarantee"); + for (const report of reports) assertTargetNarrative(readme, report, reports, "README"); + + for (const [index, artifactPath] of artifactPaths.entries()) { + const artifact = relative(root, artifactPath).replaceAll("\\", "/"); + assert(proofRecord.replaceAll("\\", "/").includes(artifact), `proof record must name ${artifact}`); + assert(proofRecord.includes(artifactDigests[index]), `proof record must identify the SHA-256 of ${artifact}`); + } + for (const hash of [ + reports[0].executable.sha256, + current.sourceTreeSha256, + current.runtimeBuildSha256, + reports[0].sourceIdentity.benchmarkScriptSha256 + ]) { + assert(proofRecord.includes(hash), `proof record must bind narrative claims to ${hash}`); + } + assert(/does not assert[^\n]*renderer|renderer[^\n]*does not assert/i.test(proofRecord), "proof record must disclaim unverified renderer state"); + assert(proofRecord.split(/\r?\n/).some((line) => /coordinator/i.test(line) && /separate/i.test(line)), "proof record must disclose coordinator memory separately"); + for (const report of reports) assertTargetNarrative(proofRecord, report, reports, "proof record"); +} + +function assertTargetNarrative(text, report, allReports, label) { + const target = report.methodology.targetMiB; + const maximum = report.verdict.maxObservedBrowserTreeRssBytes; + const verdict = report.verdict.passed ? /pass(?:ed|es|ing)?/i : /fail(?:ed|s|ing)?/i; + const otherTargets = allReports.map((candidate) => candidate.methodology.targetMiB).filter((candidate) => candidate !== target); + const lines = text.split(/\r?\n/).filter((line) => + new RegExp(`${target}\\s*MiB`, "i").test(line) + && otherTargets.every((other) => !new RegExp(`${other}\\s*MiB`, "i").test(line)) + ); + assert(lines.length > 0, `${label} must name the ${target} MiB result`); + assert(lines.some((line) => containsNumber(line, maximum)), `${label} must state the exact ${target} MiB maximum of ${maximum} bytes`); + assert(lines.some((line) => verdict.test(line)), `${label} must state the honest ${target} MiB verdict`); +} + +function containsNumber(text, value) { + return new RegExp(`(?:^|\\D)${value}(?!\\d)`).test(text.replace(/[,_]/g, "")); +} + +function assertDistribution(actual, values, label) { + const expected = distribution(values); + assert(equalJson(actual, expected), `summary ${label} distribution is inconsistent with retained samples`); +} + +function distribution(values) { + const finite = values.filter(Number.isFinite).sort((left, right) => left - right); + if (finite.length === 0) return { median: null, p95: null, min: null, max: null }; + return { + median: percentile(finite, 0.5), + p95: percentile(finite, 0.95), + min: finite[0], + max: finite.at(-1) + }; +} + +function percentile(values, point) { + const index = Math.min(values.length - 1, Math.max(0, Math.ceil(values.length * point) - 1)); + return values[index]; +} + +function equalJson(left, right) { + return JSON.stringify(left) === JSON.stringify(right); +} + +async function collectCurrentIdentity(artifactPaths) { + const listed = (await gitOutput(["ls-files", "-z", "--cached", "--others", "--exclude-standard"])) + .split("\0") + .filter(Boolean) + .map((path) => resolve(root, path)); + const artifactRoots = [ + resolve(root, "docs", "benchmarks", "artifacts"), + resolve(root, "output", "resource-benchmarks") + ]; + const excluded = new Set(artifactPaths); + const sourceFiles = []; + for (const path of listed) { + if (excluded.has(path) || artifactRoots.some((artifactRoot) => isBelow(artifactRoot, path))) continue; + if (!isProofInput(path)) continue; + if ((await stat(path).catch(() => undefined))?.isFile()) sourceFiles.push(path); + } + const runtimeFiles = await filesBelow(resolve(root, "dist")); + return { + sourceFileCount: sourceFiles.length, + sourceTreeSha256: await sha256Files(sourceFiles), + runtimeBuildFileCount: runtimeFiles.length, + runtimeBuildSha256: await sha256Files(runtimeFiles), + benchmarkScriptSha256: await sha256CanonicalText(resolve(root, "scripts", "benchmark-lightweight.mjs")) + }; +} + +function isProofInput(path) { + const candidate = relative(root, path).replaceAll("\\", "/"); + return candidate.startsWith("src/") + || candidate.startsWith("schemas/") + || candidate.startsWith("scripts/") + || candidate.startsWith("test/") + || candidate === "package.json" + || candidate === "package-lock.json" + || candidate === "server.json" + || /^tsconfig(?:\.[^.]+)?\.json$/.test(candidate); +} + +function isBelow(parent, path) { + const candidate = relative(parent, path); + return candidate !== "" && candidate !== ".." && !candidate.startsWith(`..${process.platform === "win32" ? "\\" : "/"}`) && !isAbsolute(candidate); +} + +async function gitOutput(arguments_) { + const result = await execute("git", ["-C", root, ...arguments_], { + encoding: "utf8", + timeout: 15_000, + windowsHide: true, + maxBuffer: 16 * 1024 * 1024 + }); + return String(result.stdout); +} + +async function filesBelow(directory) { + const files = []; + const visit = async (current) => { + for (const entry of await readdir(current, { withFileTypes: true })) { + const path = resolve(current, entry.name); + if (entry.isDirectory()) await visit(path); + else if (entry.isFile()) files.push(path); + } + }; + await visit(directory); + return files.sort(); +} + +async function sha256(path) { + const hash = createHash("sha256"); + for await (const chunk of createReadStream(path)) hash.update(chunk); + return hash.digest("hex"); +} + +async function sha256Files(files) { + const hash = createHash("sha256"); + for (const path of [...files].sort()) { + hash.update(relative(root, path).replaceAll("\\", "/")); + hash.update("\0"); + hash.update((await readFile(path, "utf8")).replace(/\r\n?/g, "\n")); + hash.update("\0"); + } + return hash.digest("hex"); +} + +async function sha256CanonicalText(path) { + return createHash("sha256") + .update((await readFile(path, "utf8")).replace(/\r\n?/g, "\n")) + .digest("hex"); +} + +function assert(condition, message) { + if (!condition) throw new Error(message); +} diff --git a/sdks/dotnet/CockroachBrowser/CockroachBrowser.csproj b/sdks/dotnet/CockroachBrowser/CockroachBrowser.csproj index 7d2ad98..d64e696 100644 --- a/sdks/dotnet/CockroachBrowser/CockroachBrowser.csproj +++ b/sdks/dotnet/CockroachBrowser/CockroachBrowser.csproj @@ -4,6 +4,6 @@ enable enable CockroachBrowser - 0.4.1 + 0.5.0-rc.1 diff --git a/sdks/java/pom.xml b/sdks/java/pom.xml index 0f18ac5..dad28e1 100644 --- a/sdks/java/pom.xml +++ b/sdks/java/pom.xml @@ -2,6 +2,6 @@ 4.0.0 io.cockroach cockroach-browser - 0.4.1 + 0.5.0-rc.1 11 diff --git a/sdks/python/pyproject.toml b/sdks/python/pyproject.toml index f02584e..63d268f 100644 --- a/sdks/python/pyproject.toml +++ b/sdks/python/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "cockroach-browser" -version = "0.4.1" +version = "0.5.0rc1" description = "Python client for the authenticated Cockroach Browser daemon" requires-python = ">=3.10" license = { text = "AGPL-3.0-or-later" } diff --git a/sdks/ruby/cockroach-browser.gemspec b/sdks/ruby/cockroach-browser.gemspec index 4cffc4e..3d65283 100644 --- a/sdks/ruby/cockroach-browser.gemspec +++ b/sdks/ruby/cockroach-browser.gemspec @@ -1,6 +1,6 @@ Gem::Specification.new do |spec| spec.name = "cockroach-browser" - spec.version = "0.4.1" + spec.version = "0.5.0.rc.1" spec.summary = "Ruby client for the authenticated Cockroach Browser daemon" spec.license = "AGPL-3.0-or-later" spec.required_ruby_version = ">= 3.1" diff --git a/server.json b/server.json index f3d5291..0f3b8ad 100644 --- a/server.json +++ b/server.json @@ -6,12 +6,12 @@ "url": "https://github.com/AjnasNB/cockroach-browser", "source": "github" }, - "version": "0.4.1", + "version": "0.5.0-rc.1", "packages": [ { "registryType": "npm", "identifier": "cockroach-browser", - "version": "0.4.1", + "version": "0.5.0-rc.1", "transport": { "type": "stdio" }, diff --git a/site/404.html b/site/404.html index 2548609..81200f6 100644 --- a/site/404.html +++ b/site/404.html @@ -32,7 +32,7 @@ - + @@ -41,7 +41,7 @@ Cockroach Browser - 0.4.1 + 0.5.0-rc.1