Documentation
Documentation/Execution

Workers and execution

Worker connection, temporary workspaces, generated Playwright config, logs, steps, and failure handling.

workerswebsocketplaywrighttraceslogs
View Markdown
On this page

Workers are the execution capacity behind BernOps runs. The backend owns scheduling and persistence; workers own the local browser process and Playwright execution.

Worker connection

A worker connects to the backend WebSocket endpoint at ${ORCHESTRATOR_URL}/ws/worker with its enrolled WORKER_ID, display name, and individual workspace-bound credential. There is no pool-wide secret, and a personal coding-agent host is not worker capacity.

For a new worker, use Connect a worker: install the approved private bootstrap, then run bernops setup and choose 3 (automation worker) or 4 (both). The default menu choice is auth-only and installs no worker. Setup handles shared platform login, verifies a signed immutable SDK-capable worker release and real SDK/browser readiness, then requests separate workspace approval. No public download, source checkout build, manual credential creation, or preliminary bernops auth login is required. Platform login alone never enrolls a host or worker, and its OAuth session is not passed to the container. Setup requires a confirmed Ready or Busy worker connection, not just a running container or successful image check.

Target selection follows saved identity, then the journal, then the bootstrap default unless explicit. Local development uses ./dev.sh install-cli to activate the local-target bootstrap, followed by bare bernops setup; explicit --target local is also available. Native Secret Service is the default for local/cloud platform credentials; file storage is an explicit unencrypted choice for a new identity. Use --no-browser for remote approvals. The low-level bernops worker setup alternative is different: cloud default, no platform CLI authentication, and an operator-controlled source/image path rather than unified signed-release setup.

The backend tracks connected workers and claims an eligible worker in the run's workspace when capacity is available. If no matching worker or scope capacity is available, the run remains pending until the dispatcher can assign it. Inspect inventory from the worker cog or Workspace > Workers, and use the local management commands for status, start, stop, restart, and supervisor logs.

What the worker receives

For Playwright runs, the backend sends a RunTestsPayload containing:

FieldPurpose
test_filesFile names and source code to write into a temp directory.
base_urlFinal base URL used by the generated Playwright config.
env_varsProject runtime variables plus forced execution variables.
secret_env_keysVariable names whose values should be redacted.
project_idProject identifier exposed as BERNOPS_PROJECT_ID.

Temporary workspace

The worker creates a temp directory, writes every test file using a sanitized basename, links node_modules, and generates playwright.config.js. The generated config sets:

  • workers: 1
  • retries: 0
  • trace: 'on'
  • screenshot: 'on'
  • video: 'retain-on-failure'
  • Docker-safe Chromium launch arguments

The worker then starts npx playwright test --config=<temp config> from the worker root.

Environment construction

The child process environment is intentionally filtered. The worker keeps a small allowlist from its own environment, adds allowed payload variables, and forces BernOps variables like AB_LOG_URL, BERNOPS_RUN_ID, and BERNOPS_PROJECT_ID.

Payload variable names must pass the worker allowlist. Reserved names and prefixes are blocked so project variables cannot override process-critical values.

Logs, steps, and traces

The private Automation SDK can send acknowledged structured logs, nested steps, metrics, progress, and attachments through the authenticated worker-local bridge while the worker remains lifecycle owner.

New unified worker setup obtains an SDK-capable signed release with the exact private SDK already bundled and requires native_bridge_v1. Do not install or mount the SDK separately in a saved job. Existing legacy workers are not silently upgraded; image replacement alone does not expand their approved credential capabilities. See setup recovery for worker_sdk_upgrade_required and separately approved replacement workers.

BernOps installs a custom Playwright reporter. Reporter output is emitted with a structured prefix, parsed by the worker, redacted, and sent back to the backend as execution steps.

Plain stdout and stderr are also streamed as execution logs. After the process exits, the worker parses results.json, mines trace files, and includes useful failure context.

Why `test.step` matters

The reporter sees Playwright test steps. Good step names become readable run timelines, which makes debugging easier than reading raw logs only.

Failure handling

The worker reports a failed run when Playwright exits non-zero, when result parsing fails, or when the execution setup throws. The error message is redacted before it is sent back.

If no worker is connected, the run remains pending in the backend before a payload reaches any worker. Backend-side failures still move the run to error.

Terminal-result replay uses an in-memory outbox; it can recover across a connection interruption while the worker process survives, not after process loss. Worker enrollment persists separately. Scheduling still requires one active gateway; see current pool limitations.