Skip to content

Hosted runs record measured screen geometry instead of guessed viewports - #287

Merged
danielgwilson merged 3 commits into
mainfrom
fix/observer-status-semantics
Jul 31, 2026
Merged

Hosted runs record measured screen geometry instead of guessed viewports#287
danielgwilson merged 3 commits into
mainfrom
fix/observer-status-semantics

Conversation

@danielgwilson

Copy link
Copy Markdown
Owner

Anyone reading a hosted-browser run bundle today sees a stream.viewport that was copied from the requested device preset, never measured. On the E2B desktop route the requested size actually picks the virtual screen; the browser's real CSS viewport is smaller because chrome occupies space, and a failed lookup silently became a "measured" claim. Evidence consumers (Observer, verify, downstream analysis) could not tell an honest measurement from a guess.

Why: run bundles are the product's source of truth. A geometry field that might be fabricated poisons every claim built on top of it.

Fix: hosted-browser runs now record requested virtual screen, verified X screen, measured browser outer-window bounds, and measured CSS viewport (innerWidth/innerHeight/DPR) as separate facts. stream.viewport is written only from a real CDP measurement; when measurement is unavailable the field is absent and the bundle carries an explicit warning instead of a copy of the request. Verify fails closed on a forged stream/geometry mismatch. The Observer renders full screenshots without viewport-aspect cropping, and lane status wording no longer overstates dry-run lanes. Sequential shared-world seats terminate the previous seat's browser when a turn ends, so a prior role's live session cannot mutate the shared plane during a later role's turn.

Done when:

  • A live chromium lane's bundle carries browserWindow (source cdp) and a measured stream.viewport distinct from the requested screen.
  • A Firefox or unmeasurable lane omits stream.viewport and records a warning, never a substituted size.
  • A screen-size mismatch on a concurrent lane records requested vs verified as separate facts and the run proceeds; single-lane and fan-out routes keep failing closed.
  • Historical bundles with legacy stream.viewport still verify and render without migration.
  • Each sequential seat's browser is terminated at turn end (bounded, warning on failure).

Not in this PR: any npm release (separately gated); the replacement README hero capture; live-E2B rebaselines of the CDP timing heuristics (reasoned, not executed, on the deterministic substrate).


Details for the implementer:

  • Contract statement: docs/contracts/run-bundle.md; honest-fidelity language updated in README.md and docs/goals/current.md.
  • Core seams: src/cua-actor-lab.ts (window finder keyed on browser family with identity as refinement; ChromeCdpEndpoint with cached-port, DevToolsActivePort re-read at observe time, then legacy 9222 fallback; CDP page attribution pinned targetId / lane target URL / single-page; teardown capture merged final-else-launch with Set-deduped warnings), src/shared-world-lab.ts (same restructure for sequential seats + per-seat browser termination by recorded PID with profile-dir pkill fallback), src/concurrent-shared-world-lab.ts (screenMismatchPolicy: "record-evidence" for independent evidence seats), src/run.ts (geometry schema + fail-closed stream/geometry consistency), src/oss-meta-lab.ts (dry-run bundles carry requested screen only, no fabricated viewport), src/device-presets.ts, src/observer-assets.ts.
  • Conscious breaking change (pre-1.0): root-exported buildCuaBundle drops the deviceScaleFactor input; it existed only to fabricate stream.viewport from the requested size, which this contract forbids. Callers now pass desktopRoute/desktopGeometry. Flag for the next version bump's release notes.
  • Disclosed capability regressions (honest fallbacks): the find_named_window title-match fallback is removed, and unknown-family launches (desktop.open non-http route) skip window lookup; those routes stream the full desktop with a warning instead of a window-scoped stream.
  • tests/golden/labs/oss.json regenerated via the capture path: dry-run bundles now emit desktopGeometry.screen.requested instead of a fabricated viewport. Timestamp/git-state leaves are masked by lab-golden.test.ts.
  • Gates: full suite 1061 passed / 10 skipped / 0 failed (includes 7 new tests for seat termination and CDP port resolution); tsc --noEmit clean; build clean; git diff --check clean; public-surface scan ok (base scan locally; the denylist-augmented variant runs in CI).

🤖 Generated with Claude Code

danielgwilson and others added 3 commits July 16, 2026 03:18
Hosted-browser bundles previously copied the requested device preset into
stream.viewport, so a guess was indistinguishable from a measurement. Runs
now record requested virtual screen, verified X screen, measured browser
outer-window bounds, and measured CSS viewport as separate facts.
stream.viewport is written only from a real CDP measurement; unmeasurable
lanes omit the field and carry an explicit warning. Verify fails closed on
a forged stream/geometry mismatch. Concurrent lanes record a screen-size
mismatch as evidence and proceed; single-lane and fan-out routes keep
failing closed. Sequential shared-world seats terminate the previous
seat's browser at turn end (bounded, warning on failure) and chromium
observers re-resolve the CDP port at observe time.

Conscious pre-1.0 breaking change: root-exported buildCuaBundle drops the
deviceScaleFactor input; it existed only to fabricate stream.viewport
from the requested size, which this contract forbids.

Full suite 1061 passed / 10 skipped; typecheck, build, diff --check, and
public-surface scan clean.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The regenerated fixture from the feature commit embedded that worktree's
ambient git state (18 unstaged files). Recaptured from the committed tree
so the recorded leaves are clean. Masked by lab-golden.test.ts either way.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@danielgwilson
danielgwilson merged commit 7759f21 into main Jul 31, 2026
3 checks passed
@danielgwilson
danielgwilson deleted the fix/observer-status-semantics branch July 31, 2026 23:53
danielgwilson added a commit that referenced this pull request Aug 1, 2026
Ships the checksum-pinned drawDB four-persona study composite
(docs/assets/humanish-drawdb-hero.png, SHA-256 0a61840b...) in the npm
payload alongside the existing synthetic hero, and bumps the package and
doc version pins to 0.16.0. The README switch to the new hero follows in
a separate PR once the version-pinned unpkg URL exists.

0.16.0 is a minor bump because #287 consciously changed the root-exported
buildCuaBundle input surface: deviceScaleFactor is gone; callers pass
desktopRoute/desktopGeometry. stream.viewport is now measured-only.

The image comes from live run drawdb-study-20260801-wide-05: four
computer-use personas driving a commit-pinned local drawDB checkout
(9d334355), 4/4 passed, verify 15/15, all sandboxes torn down by exact
id. drawDB is the public application studied, not an adopter or endorser.

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant