diff --git a/CHANGELOG.md b/CHANGELOG.md index 10e5986f6..a317fc24e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,15 +4,16 @@ All notable changes to OriginWeave are documented in this file. The format follo ## [Unreleased] -- Made the reusable WebDriver BiDi presentation planner accept only viewport, DPR, and timezone inputs. It no longer accepts a complete presentation profile while leaving unsupported or lifecycle-unrestorable surfaces unapplied. +- Exposed WebDriver BiDi `emulation.setScreenSettingsOverride` as a separately explicit, context-scoped partial screen-area intent with matching reset. The protocol couples total and available screen areas to one rectangle, while the current presentation profile does not model `screen.availWidth` / `screen.availHeight`; the reusable profile-derived planner therefore remains viewport/DPR plus timezone rather than silently mutating an unmodelled page observable. - Refreshed the product-gap queue to 126 open pull requests (54 ready, 72 draft) after #190, #188, #185, #192, #182, #184, #115, #181, #116, #117, #118, #183, #114, #127, #112, #109, #186, #110, #108, #111, #174, and #113 were merged into their immediate stacked prerequisites. PRs #147, #146, #145, #144, #143, #142, #141, #139, #136, #132, #129, and #128 moved to ready after exact-head checks and thread review; these are queue-consolidation results, not protected-main shipment. ### Fixed +- Prevented the reusable profile-derived WebDriver BiDi planner from scheduling `setScreenSettingsOverride` from `ScreenMetrics` alone, because the standard operation also changes the page-observable available screen rectangle that the current presentation identity neither selects nor digest-binds. - Restored canonical Rust formatting for the WebDriver BiDi presentation cleanup assertion so exact-head contracts can execute the test, Clippy, and rustdoc gates. ### Added -- Added a version-pinned `originweave-bidi` presentation-capability boundary for the W3C WebDriver BiDi Working Draft published on 3 September 2026 (`https://www.w3.org/TR/2026/WD-webdriver-bidi-20260903/`). It depends inward on `originweave-fingerprint`, plans only the symmetrically restorable viewport/DPR and timezone commands for one bounded reusable browsing context, and fails first on the complete screen surface because standard BiDi cannot prove color depth or ordered languages. Reduced motion remains an expressible protocol capability but is not installed by the reusable plan because standard cleanup cannot selectively restore prior media state. No caller-mintable exclusive-reset type is exposed; a Browser Session owner must prove a disposable lifecycle or complete prior-state restoration. Planning performs no transport I/O or acknowledgement, cleanup, ownership, or page-observed evidence. Hardware concurrency and the complete Chromium platform/User-Agent Client Hints surface still require a separate versioned Chromium adapter. +- Added a version-pinned `originweave-bidi` presentation-capability boundary for the W3C WebDriver BiDi Working Draft published on 3 September 2026 (`https://www.w3.org/TR/2026/WD-webdriver-bidi-20260903/`). It depends inward on `originweave-fingerprint`, keeps the reusable plan limited to symmetrically restorable and explicitly modelled viewport/DPR and timezone commands, and exposes screen settings as a separate typed partial intent whose one rectangle controls both total and available screen area. Complete `PresentationSurface::Screen` admission still fails closed because available-screen geometry is unmodelled and color depth remains uncontrolled. Reduced motion remains an expressible protocol capability but is not installed by the reusable plan because standard cleanup cannot selectively restore prior media state. No caller-mintable exclusive-reset type is exposed; a Browser Session owner must prove a disposable lifecycle or complete prior-state restoration. Planning performs no transport I/O or acknowledgement, cleanup, ownership, or page-observed evidence. Hardware concurrency and the complete Chromium platform/User-Agent Client Hints surface still require a separate versioned Chromium adapter. - Added a bounded Rust presentation-identity kernel for explicit browser-visible profiles and credential-free replay digests, including control-safe mobile UA-CH model values; applying those profiles to Chromium and proving page-observed effects remain separate adapter and browser-E2E work. - Corrected the 2026-08-26 product-gap snapshot with current #229 presentation-identity evidence, stacked-only #205 integration evidence, current base/head pairs, the 126-PR queue count, explicit root-versus-child merge ordering, and the active GitHub counted-approval gate. diff --git a/crates/originweave-bidi/src/presentation_capabilities.rs b/crates/originweave-bidi/src/presentation_capabilities.rs index 70fdbcd0b..b47939e8a 100644 --- a/crates/originweave-bidi/src/presentation_capabilities.rs +++ b/crates/originweave-bidi/src/presentation_capabilities.rs @@ -1,8 +1,8 @@ use std::{error::Error, fmt}; use originweave_fingerprint::{ - DevicePixelRatio, PresentationError, PresentationSurface, PresentationTimeZone, ViewportBounds, - require_presentation_surfaces, + DevicePixelRatio, PresentationError, PresentationSurface, PresentationTimeZone, ScreenMetrics, + ViewportBounds, require_presentation_surfaces, }; const MAX_BROWSING_CONTEXT_BYTES: usize = 256; @@ -45,16 +45,86 @@ impl WebDriverBidiBrowsingContext { } } +/// Coupled total-and-available screen-area fields representable by +/// `emulation.setScreenSettingsOverride`. +/// +/// WebDriver BiDi applies one rectangle to both the web-exposed total screen area and available +/// screen area. Construction therefore remains an explicit partial capability: it projects width and +/// height from validated [`ScreenMetrics`] but does not claim that the presentation profile models the +/// resulting `screen.availWidth` / `screen.availHeight` observables or screen color depth. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct WebDriverBidiScreenArea { + width_px: u32, + height_px: u32, +} + +impl WebDriverBidiScreenArea { + /// Project the protocol-owned rectangle from validated presentation screen metrics. + /// + /// The returned value intentionally means that total and available screen areas will be coupled to + /// the same rectangle. It must not be inserted into a profile-derived reusable plan unless the + /// presentation schema has first modelled and authorized those available-area observables. + #[must_use] + pub const fn from_screen(screen: &ScreenMetrics) -> Self { + Self { + width_px: screen.width(), + height_px: screen.height(), + } + } + + /// Return the width applied to both total and available web-exposed screen areas. + #[must_use] + pub const fn width(&self) -> u32 { + self.width_px + } + + /// Return the height applied to both total and available web-exposed screen areas. + #[must_use] + pub const fn height(&self) -> u32 { + self.height_px + } +} + +/// Proof that Browser Session owns screen-settings mutation for one browsing context. +/// +/// This type intentionally has no public constructor. A remote-issued context identifier is identity, +/// not authority: WebDriver BiDi replaces the current screen-area override when setting a rectangle and +/// removes it when `screenArea` is null. A Browser Session integration may create this witness only +/// after it has established an exclusive/disposable context or an equivalent lifecycle that proves no +/// unrelated owner state can be overwritten or cleared. Until that integration exists, external +/// callers have neither a mint path nor a callable screen-area planner. +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct WebDriverBidiScreenAreaOwnership { + context: WebDriverBidiBrowsingContext, +} + +impl WebDriverBidiScreenAreaOwnership { + /// Return the exact browsing context covered by this ownership witness. + #[must_use] + pub const fn context(&self) -> &WebDriverBidiBrowsingContext { + &self.context + } +} + /// Typed standard-BiDi presentation command intent for one explicit browsing context. /// /// These values are inputs to a later transport owner. Constructing them does not send a command, /// prove an acknowledgement, establish Browser Session ownership, or establish page-observed state. -/// Presentation payloads retain the validated fingerprint value objects so a transport adapter cannot -/// bypass their bounds by constructing raw viewport, DPR, or time-zone values. This reusable-boundary -/// enum deliberately exposes no media-feature mutation command because this crate has no ownership or -/// snapshot witness that would make such mutation reversibly safe. +/// Presentation payloads retain validated value objects so a transport adapter cannot reopen raw +/// screen, viewport, DPR, or time-zone validation. Screen-area command vocabulary retains the opaque +/// Browser Session ownership witness because setting or clearing the context override is destructive to +/// any predecessor value. No public screen-area planner is exposed until Browser Session can mint that +/// witness. This reusable-boundary enum deliberately exposes no media-feature mutation command because +/// this crate has no ownership or snapshot witness that would make such mutation reversibly safe. #[derive(Debug, Clone, PartialEq, Eq)] pub enum WebDriverBidiPresentationCommand { + /// Set total and available web-exposed screen width and height together. + SetScreenArea { + /// Browser Session proof that this context's screen-settings lifecycle is exclusively owned. + ownership: WebDriverBidiScreenAreaOwnership, + /// Exact coupled standard-BiDi screen-area payload derived from validated screen metrics. + screen_area: WebDriverBidiScreenArea, + }, /// Set viewport dimensions and device-pixel ratio together. SetViewport { /// Exact target browsing context. @@ -71,6 +141,11 @@ pub enum WebDriverBidiPresentationCommand { /// Validated presentation time-zone identity. timezone: PresentationTimeZone, }, + /// Remove the coupled total-and-available screen-area override for the owned browsing context. + ResetScreenArea { + /// Browser Session proof that clearing this context cannot remove another owner's override. + ownership: WebDriverBidiScreenAreaOwnership, + }, /// Restore the implementation-defined viewport and remove the device-pixel-ratio override. ResetViewport { /// Exact target browsing context. @@ -86,13 +161,14 @@ pub enum WebDriverBidiPresentationCommand { /// Plan the reversible standard-BiDi presentation commands safe for a reusable browsing context. /// /// Viewport/device-pixel-ratio and time-zone state each have a non-destructive nullable reset in the -/// pinned Working Draft. Reduced motion remains an expressible protocol capability, but this reusable -/// planning boundary neither installs nor exposes a media-mutation command because `features: null` -/// clears the complete media-feature configuration rather than restoring only OriginWeave's prior -/// `prefers-reduced-motion` value. The explicit arguments make this a partial-plan API: it cannot be -/// mistaken for application of a complete [`originweave_fingerprint::PresentationProfile`]. A later -/// Browser Session-owned adapter may introduce reduced-motion application only after it can prove a -/// genuinely disposable lifecycle or a complete snapshot/restore path. +/// pinned Working Draft. The screen-settings override is excluded from this profile-derived plan even +/// though the protocol exposes a nullable reset because it also changes the unmodelled page-observable +/// available screen area and requires Browser Session ownership of the predecessor state. Reduced +/// motion remains an expressible protocol capability, but this reusable planning boundary neither +/// installs nor exposes a media-mutation command because `features: null` clears the complete +/// media-feature configuration rather than restoring only OriginWeave's prior `prefers-reduced-motion` +/// value. The explicit arguments make this a partial-plan API: it cannot be mistaken for application of +/// a complete [`originweave_fingerprint::PresentationProfile`]. #[must_use] pub fn plan_standard_presentation_commands( context: &WebDriverBidiBrowsingContext, @@ -113,12 +189,15 @@ pub fn plan_standard_presentation_commands( ] } -/// Plan cleanup that is non-destructive to unrelated media-feature overrides. +/// Plan cleanup that is non-destructive to unrelated presentation or media overrides. /// -/// The pinned Working Draft provides independently nullable reset paths for viewport/DPR and -/// time-zone state, so these two resets are safe to plan for a reusable browsing context. Media -/// cleanup is deliberately absent because `features: null` clears the complete media-feature -/// override configuration rather than selectively undoing `prefers-reduced-motion`. +/// The pinned Working Draft provides independently nullable context-scoped reset paths for viewport/DPR +/// and time-zone state, so these two resets are safe to plan for a reusable browsing context. Screen-area +/// command intent remains ownership-gated, but no callable screen-area cleanup planner exists until +/// Browser Session can mint the ownership witness; `screenArea: null` removes the current override +/// rather than restoring any predecessor. Media cleanup is absent because `features: null` clears the +/// complete media-feature override configuration rather than selectively undoing +/// `prefers-reduced-motion`. #[must_use] pub fn plan_standard_presentation_cleanup( context: &WebDriverBidiBrowsingContext, @@ -153,13 +232,15 @@ const WEBDRIVER_BIDI_PRESENTATION_SURFACES: [PresentationSurface; 4] = [ PresentationSurface::ReducedMotion, ]; -/// Return presentation surfaces expressible through the pinned standard BiDi contract. +/// Return complete presentation surfaces expressible through the pinned standard BiDi contract. /// -/// Complete screen and ordered-language surfaces, hardware concurrency, and the -/// Chromium platform/User-Agent Client Hints surface are intentionally absent. -/// Reduced motion is listed as protocol capability even though reusable application leaves media -/// state untouched until a Browser Session owner supplies a restorable lifecycle and corresponding -/// command authority. +/// The protocol can explicitly couple total and available screen width/height through +/// `emulation.setScreenSettingsOverride`, but OriginWeave's `Screen` surface also includes color depth +/// and the current profile does not model the available screen rectangle. `Screen` therefore remains +/// intentionally absent. Ordered-language surfaces, hardware concurrency, and the Chromium +/// platform/User-Agent Client Hints surface are also absent. Reduced motion is listed as protocol +/// capability even though reusable application leaves media state untouched until a Browser Session +/// owner supplies a restorable lifecycle and corresponding command authority. #[must_use] pub const fn webdriver_bidi_presentation_surfaces() -> &'static [PresentationSurface] { &WEBDRIVER_BIDI_PRESENTATION_SURFACES @@ -167,9 +248,11 @@ pub const fn webdriver_bidi_presentation_surfaces() -> &'static [PresentationSur /// Require the pinned standard BiDi capability set to satisfy the complete profile. /// -/// The current result is fail-closed with -/// `PresentationError::MissingSurface(PresentationSurface::Screen)`. -/// Callers must not translate that result into ambient-host fallback. +/// The current result remains fail-closed with +/// `PresentationError::MissingSurface(PresentationSurface::Screen)` because the dormant screen-area +/// command does not control color depth, additionally couples an available-screen observable absent +/// from the current profile, and cannot be materialized until Browser Session supplies ownership of the +/// screen-settings lifecycle. Callers must not translate that result into ambient-host fallback. pub fn require_complete_presentation_profile() -> Result<(), PresentationError> { require_presentation_surfaces(webdriver_bidi_presentation_surfaces()) } @@ -213,7 +296,50 @@ mod tests { } #[test] - fn reusable_standard_commands_bind_only_symmetrically_restorable_state() { + fn screen_area_command_shape_requires_the_same_ownership_witness() { + let profile = PresentationProfile::new( + ScreenMetrics::new(1920, 1080).expect("valid screen"), + ViewportBounds::new(1440, 900).expect("valid viewport"), + DevicePixelRatio::Quantized2, + 8, + PresentationTimeZone::Utc, + PresentationPlatform::MacOS, + vec!["en-US".to_owned()], + true, + ) + .expect("consistent profile"); + let context = + WebDriverBidiBrowsingContext::new("context-17").expect("bounded context identifier"); + let ownership = WebDriverBidiScreenAreaOwnership { + context: context.clone(), + }; + let screen_area = WebDriverBidiScreenArea::from_screen(profile.screen()); + let set_command = WebDriverBidiPresentationCommand::SetScreenArea { + ownership: ownership.clone(), + screen_area, + }; + let reset_command = WebDriverBidiPresentationCommand::ResetScreenArea { + ownership: ownership.clone(), + }; + + assert_eq!(ownership.context(), &context); + assert_eq!(screen_area.width(), 1920); + assert_eq!(screen_area.height(), 1080); + assert_eq!( + set_command, + WebDriverBidiPresentationCommand::SetScreenArea { + ownership: ownership.clone(), + screen_area, + } + ); + assert_eq!( + reset_command, + WebDriverBidiPresentationCommand::ResetScreenArea { ownership } + ); + } + + #[test] + fn reusable_standard_commands_bind_only_modelled_symmetrically_restorable_state() { let error = WebDriverBidiCommandError::InvalidBrowsingContext; assert_eq!(error.to_string(), "invalid WebDriver BiDi browsing context"); assert!(Error::source(&error).is_none()); @@ -264,7 +390,7 @@ mod tests { } #[test] - fn reusable_cleanup_does_not_clear_unrelated_media_feature_state() { + fn reusable_cleanup_does_not_clear_unrelated_screen_or_media_state() { let context = WebDriverBidiBrowsingContext::new("context-17").expect("bounded context identifier"); diff --git a/docs/README.md b/docs/README.md index 622fc7e99..fd2c19ec9 100644 --- a/docs/README.md +++ b/docs/README.md @@ -93,9 +93,10 @@ The second group exists only on this documentation branch until the branch integ ### Proposed decisions introduced by active feature work - [ADR 0016: BAP task lifecycle and state authority](adr/0016-bap-task-lifecycle-authority.md) +- [ADR 0113: WebDriver BiDi screen-area ownership witness](adr/0113-webdriver-bidi-screen-area-ownership.md) -ADR 0016 is owned by this active BAP lifecycle feature branch and remains Proposed. Its presence here makes the branch documentation graph complete without presenting the decision or implementation as protected-main truth before integration. +ADR 0016 is owned by the active BAP lifecycle feature branch. ADR 0113 is owned by the active WebDriver BiDi screen-area ownership successor. Their presence here makes the branch documentation graph complete without presenting either decision or implementation as protected-main truth before integration. -After protected-main integration, retain this subsection only when it is intentionally serving as historical provenance; otherwise protected-main reconciliation must remove it. In either case, integration alone does not change ADR 0016 from Proposed or assert implementation maturity. +After protected-main integration, retain this subsection only when it is intentionally serving as historical provenance; otherwise protected-main reconciliation must remove it. In either case, integration alone does not change ADR 0016 or ADR 0113 from Proposed or assert implementation maturity. See the [ADR index](adr/README.md) for status rules, required decision structure, supersession rules, and active feature ADRs. The index and each ADR's own status metadata must agree; a PR body, chat transcript, automation prompt, or stale issue reference cannot change ADR status. diff --git a/docs/adr/0107-browser-protocol-adapter-strategy.md b/docs/adr/0107-browser-protocol-adapter-strategy.md index 066c22942..491359110 100644 --- a/docs/adr/0107-browser-protocol-adapter-strategy.md +++ b/docs/adr/0107-browser-protocol-adapter-strategy.md @@ -46,7 +46,9 @@ The version boundary is explicit: the protected-main routing foundation and acti PR #293 was merged into PR #229 on 2026-09-09, so its `originweave-bidi` capability boundary is inherited by this parent rather than remaining a separate active stacked slice. The adapter remains runtime-qualified 3 September 2026 against the immutable WebDriver BiDi Working Draft URI `https://www.w3.org/TR/2026/WD-webdriver-bidi-20260903/`. W3C has since published the latest published 9 September 2026 Working Draft; publication freshness is recorded separately in `docs/traceability/webdriver-bidi-publication-current.md` and does not silently repin runtime compatibility. A newer runtime pin requires a dedicated compatibility/conformance change and pinned-browser evidence. -The inherited capability map delegates complete-profile admission to `originweave-fingerprint` and intentionally excludes `Screen`, `Languages`, `HardwareConcurrency`, and `Platform`: the standard screen command omits color depth, while the locale command cannot prove ordered language preferences. Standard BiDi alone must therefore return the kernel's first `MissingSurface(Screen)` result rather than accept ambient host values. The adapter can derive typed viewport/DPR and timezone command intents plus matching cleanup intents for one bounded reusable browsing-context identifier. Reduced motion remains an expressible protocol capability but is excluded from the reusable plan because the standard cannot selectively restore prior media state; no caller-mintable exclusive-reset type substitutes for Browser Session lifecycle evidence. Planning does not send a command, create an acknowledgement, apply or prove cleanup of a profile, or produce page-observed evidence. Those remain #292 follow-up work and require exact-head verification plus a version-pinned Chromium/CDP adapter for the remainder. +The inherited capability map delegates complete-profile admission to `originweave-fingerprint` and intentionally excludes `Screen`, `Languages`, `HardwareConcurrency`, and `Platform`. The standard screen-settings command omits color depth and, importantly, applies one rectangle to both the web-exposed total screen area and available screen area, while the current OriginWeave presentation profile does not model the available-screen rectangle. The locale command likewise cannot prove ordered language preferences. Standard BiDi alone must therefore return the kernel's first `MissingSurface(Screen)` result rather than accept ambient host values. + +PR #310 exposes the standard `emulation.setScreenSettingsOverride` operation as a separately explicit partial intent instead of inserting it into the reusable profile-derived plan. `WebDriverBidiScreenArea` projects validated width and height from `ScreenMetrics` and documents the protocol's total/available-area coupling; its matching reset is also explicit. The ordinary reusable-context plan remains viewport/DPR plus timezone while available-screen geometry is unmodelled. Reduced motion remains an expressible protocol capability but is excluded from the reusable plan because the standard cannot selectively restore prior media state; no caller-mintable exclusive-reset type substitutes for Browser Session lifecycle evidence. Planning does not send a command, create an acknowledgement, apply or prove cleanup of a profile, or produce page-observed evidence. Those remain #292 follow-up work and require exact-head verification plus a version-pinned Chromium/CDP adapter for the remainder. The detailed decision and acceptance boundary are recorded in `docs/traceability/webdriver-bidi-screen-area-planning.md`. ## Consequences @@ -60,7 +62,7 @@ Adapter negotiation failure disables only affected capabilities. Unsupported or Protocol validation occurs before messages influence policy. Tool/page-provided strings remain untrusted. Method and tool routing metadata is shape-bounded before correlation, preventing malformed or oversized untrusted routing strings from being reinterpreted through mismatch handling. Secret handles never become raw secret protocol payloads; only the separately authorized trusted broker-to-browser delivery path may materialize the value, and that value does not pass through MCP, WebMCP, BiDi observation, or model-visible CDP output. Adapter version/provenance is recorded for audit and incident reconstruction. -For presentation emulation, protocol availability is not presentation evidence. The adapter must bind its capability claim to an explicit protocol/browser revision, fail closed on missing required surfaces, clear every override that its presentation plan establishes before reuse is treated as clean, and later prove page-visible state after application and cleanup. Neither a protocol command acknowledgement nor an unobserved browser setting is sufficient evidence. +For presentation emulation, protocol availability is not presentation evidence. The adapter must bind its capability claim to an explicit protocol/browser revision, fail closed on missing required surfaces, and must not silently mutate a page-observable surface absent from the selected and digest-bound presentation identity. Every override actually applied must have owned cleanup before reuse is treated as clean, followed by page-visible post-cleanup observation. Neither a protocol command acknowledgement nor an unobserved browser setting is sufficient evidence. ## Tests and acceptance evidence @@ -68,7 +70,7 @@ Require version-negotiation tests, schema/property tests, malformed-message test For the protected-main `tools/call` foundation, acceptance includes deterministic method and tool-name bounds/syntax, exact header/body method and tool-name correlation only after both sides are bounded, explicit invalid-method/invalid-tool-name/unknown-tool rejection, one unambiguous tool-to-action registry, independent capability/risk expectations, route/action mismatch denial before ordinary policy evaluation, exact 100% owned-production coverage, and integrated review evidence from PR #168. For active PR #170, exact-current acceptance additionally requires bounded protocol metadata before cross-field comparison, required client-capabilities presence, bounded `tools/list` method correlation, rejection of unissued cursors, deterministic result/cache semantics, exact 100% owned-production coverage, and unchanged-head CI/security/review evidence. These checks do not substitute for complete transport or adapter conformance. -For the inherited PR #293 capability-boundary delta now carried by PR #229, acceptance requires the original regression proving the absence of an `originweave-bidi` bounded context on its predecessor, cleanup regressions that refuse to leave adapter-owned overrides behind, and exact-head Rust/Python/rustdoc/Clippy/coverage verification that the minimal adapter compiles and the runtime-qualified standard set fails with the canonical fingerprint-kernel missing-surface error. This is not acceptance of #292 as a whole. Real pinned-Chromium application, page-observed post-condition evidence, navigation/renderer/crash/cleanup behavior, and the Chromium-only CDP remainder still require realistic browser E2E. Publication of a newer Working Draft is not compatibility evidence and cannot by itself change this acceptance basis. +For the inherited PR #293 capability-boundary delta now carried by PR #229, acceptance requires the original regression proving the absence of an `originweave-bidi` bounded context on its predecessor, cleanup regressions that refuse to leave adapter-owned overrides behind, and exact-head Rust/Python/rustdoc/Clippy/coverage verification that the minimal adapter compiles and the runtime-qualified standard set fails with the canonical fingerprint-kernel missing-surface error. PR #310 additionally requires an explicit screen-area intent derived from validated `ScreenMetrics`, explicit total/available-area coupling semantics, a matching context-scoped reset, absence of color depth from the standard payload object, no automatic screen-area mutation in the reusable profile-derived plan while available-screen geometry is unmodelled, and continued `MissingSurface(Screen)` admission. This is not acceptance of #292 as a whole. Real pinned-Chromium application, page-observed post-condition evidence, navigation/renderer/crash/cleanup behavior, and the Chromium-only CDP remainder still require realistic browser E2E. Publication of a newer Working Draft is not compatibility evidence and cannot by itself change this acceptance basis. ## Migration and rollback @@ -76,7 +78,7 @@ Adapters are independently versioned and can be canaried. Clients migrate throug ## Open follow-ups -Define internal protocol versioning rules, complete MCP Streamable HTTP/request-metadata validation, MCP transport serialization, authenticated deployment, and MCP/WebMCP schema isolation. For presentation identity, implement the exact pinned Chromium/BiDi command path, a narrow version-pinned `originweave-cdp` capability owner for required non-BiDi surfaces, post-application and post-cleanup page observation, navigation/renderer invalidation, crash/cleanup behavior, and release compatibility evidence. +Define internal protocol versioning rules, complete MCP Streamable HTTP/request-metadata validation, MCP transport serialization, authenticated deployment, and MCP/WebMCP schema isolation. For presentation identity, decide and test the canonical available-screen-area model before any profile-derived `setScreenSettingsOverride` application, implement the exact pinned Chromium/BiDi command path, add a narrow version-pinned `originweave-cdp` capability owner for required non-BiDi surfaces, require post-application and post-cleanup page observation, navigation/renderer invalidation, crash/cleanup behavior, and release compatibility evidence. ## Supersession / reversal conditions @@ -100,4 +102,4 @@ World Wide Web Consortium. (2026, September 3). *WebDriver BiDi* [Working Draft; ## Related documents -See `docs/API_CONTRACT.md`, `docs/TRD.md`, `docs/doctoring.md`, `docs/doctoring/product-documentation-baseline.md`, `docs/traceability/README.md`, `docs/traceability/webdriver-bidi-publication-current.md`, and `docs/DATA_GOVERNANCE.md`. \ No newline at end of file +See `docs/API_CONTRACT.md`, `docs/TRD.md`, `docs/doctoring.md`, `docs/doctoring/product-documentation-baseline.md`, `docs/traceability/README.md`, `docs/traceability/webdriver-bidi-publication-current.md`, `docs/traceability/webdriver-bidi-screen-area-planning.md`, and `docs/DATA_GOVERNANCE.md`. diff --git a/docs/adr/0113-webdriver-bidi-screen-area-ownership.md b/docs/adr/0113-webdriver-bidi-screen-area-ownership.md new file mode 100644 index 000000000..be8eb089c --- /dev/null +++ b/docs/adr/0113-webdriver-bidi-screen-area-ownership.md @@ -0,0 +1,106 @@ +# ADR 0113: WebDriver BiDi screen-area ownership witness + +- **Status:** Proposed +- **Date:** 2026-09-10 +- **Supersedes:** none +- **Superseded by:** none +- **Refines:** ADR 0107 + +## Context + +ADR 0107 keeps WebDriver BiDi behind a versioned adapter and requires owned cleanup for presentation overrides. PR #310 exposed `emulation.setScreenSettingsOverride` as an explicit partial intent while correctly excluding it from the reusable profile-derived plan because one rectangle changes both total and available screen geometry. + +A second authority problem is independent of that schema gap. WebDriver BiDi stores the screen-area override against a browsing context. Setting a non-null rectangle replaces the target entry; sending `screenArea: null` removes the target entry. The standard does not restore a predecessor override. A `WebDriverBidiBrowsingContext` therefore identifies a mutation target but cannot prove that OriginWeave owns the state being replaced or cleared. + +The first ownership-witness implementation retained public explicit planner functions while intentionally exposing no Browser Session witness-mint path. Exact-head CI `34419810636` made that contradiction executable: Python repository contracts, formatting, and locked workspace tests passed, but strict Clippy rejected both planners as dead production code. Exact production coverage passed separately. A callable planner API with no legal production caller is not a deferred capability; it is unreachable surface area that obscures the lifecycle boundary. + +## Decision drivers + +- Preserve the useful typed WebDriver BiDi screen-area vocabulary without granting ambient mutation authority. +- Prevent a raw browsing-context identifier from authorizing replacement or removal of another owner's override. +- Keep cleanup evidence causal: ownership must exist before the destructive mutation, not be inferred from a later command acknowledgement. +- Do not suppress `dead_code` or retain unreachable public helpers merely to advertise a future capability. +- Keep the reusable profile-derived planner limited to observables represented by the profile and paired with safe cleanup semantics. +- Keep complete Screen admission fail-closed while available-screen geometry and color depth remain uncontrolled. + +## Assumptions and authority boundaries + +- Browser-domain and Browser Session lifecycle authority remain in OriginWeave. +- WebDriver BiDi remains an adapter; protocol addressability is not product authorization. +- The runtime-qualified 3 September 2026 Working Draft pin remains unchanged until a separate compatibility change proves a newer revision. +- `WebDriverBidiScreenArea` remains the typed width/height representation of the protocol's coupled total/available-area rectangle. +- This slice has no authoritative predecessor-state snapshot and does not invent one. +- A command acknowledgement is not page-observed application, ownership evidence, cleanup evidence, or restoration evidence. +- Screen-area mutation may become executable only after Browser Session proves an exclusive/disposable browsing context or an equivalent restoration-safe lifecycle. + +## Options considered + +1. **Keep context-only Set/Reset planners.** Rejected. Any caller able to supply a valid remote context identifier could replace or delete screen-settings state without proving ownership. +2. **Delete screen-area support.** Rejected. The standard capability is useful and can be represented without granting ambient mutation authority. +3. **Capture and restore an assumed predecessor value.** Rejected. This slice has no authoritative predecessor snapshot and the standard reset semantics remove the override rather than restore one. +4. **Treat a successful Set command as ownership proof.** Rejected. The Set can already have overwritten another owner's state; acknowledgement is too late to establish authorization. +5. **Keep public explicit planners that accept an opaque witness even though no production mint path exists.** Rejected by executable evidence. Exact-head strict Clippy identified both helpers as dead code; suppressing the warning would preserve an API that no legal caller can reach. +6. **Retain the typed command/witness vocabulary but expose no screen-area planner until Browser Session can mint the witness.** Selected. The protocol semantics remain represented, while executable authority appears only when the lifecycle owner supplies a reviewed mint transition and can consume the witness without reopening raw-context authority. + +## Decision + +`originweave-bidi` retains `WebDriverBidiScreenArea`, `WebDriverBidiScreenAreaOwnership`, and the typed `SetScreenArea` / `ResetScreenArea` command variants. Both variants carry the ownership witness rather than a raw `WebDriverBidiBrowsingContext`. + +`WebDriverBidiScreenAreaOwnership` contains the exact validated browsing context and intentionally exposes no public constructor in the adapter. Its context accessor preserves the target bound to the proof. A future Browser Session integration may mint the witness only after establishing an exclusive/disposable browsing context or an equivalent lifecycle guarantee that no unrelated screen override can be replaced or removed. + +Until that mint path exists, the adapter exposes no public explicit screen-area planner. This is deliberate fail-closed capability representation, not an incomplete helper API. When Browser Session adds the ownership transition, the planner/transport path must be introduced in the same reviewed slice so strict Clippy, repository contracts, runtime evidence, and lifecycle invalidation prove that the capability is actually reachable through the canonical owner. + +The standard reusable plan remains viewport/DPR plus timezone. Complete `PresentationSurface::Screen` remains unsupported because available-screen geometry is not represented by `ScreenMetrics` and color depth is not controlled by the standard operation. + +## Consequences + +The adapter preserves the protocol vocabulary needed for a future owned integration while ordinary context-aware callers cannot plan destructive screen-area mutation. The Browser Session owner now has a narrow future integration point instead of a context-only authorization escape hatch or dead public planner. + +The trade-off is deliberate: screen-area application cannot currently be materialized outside the module. Product code remains fail-closed until the lifecycle owner supplies a reviewed witness producer and a live consumer path. + +## Failure and degraded behavior + +If Browser Session cannot prove an exclusive/disposable lifecycle or equivalent restoration-safe ownership, no ownership witness is available and no screen-area Set/Reset plan is exposed to external callers. OriginWeave must not fall back to a raw context identifier, ambient browser state, an LLM decision, a command acknowledgement, best-effort cleanup, or a `dead_code` suppression. + +The reusable profile planner continues to omit screen-area mutation. Complete presentation-profile admission continues to return `MissingSurface(Screen)` because available-screen geometry is unmodelled and color depth is uncontrolled. + +## Security / privacy / governance impact + +A remote-issued context identifier is treated as untrusted addressing metadata rather than mutation authority. The ownership witness prevents adapters, MCP callers, LLM output, page content, or other context-aware code from acquiring screen-settings mutation merely by naming a valid browsing context. + +The witness must never be synthesized from command acknowledgement, ambient browser state, mutable external metadata, or a raw context identifier. If lifecycle ownership cannot be proven, screen-area mutation remains unavailable. + +No identity, egress, secret, policy, approval, or Context Fabric authority moves into the WebDriver BiDi adapter. The decision remains Proposed until policy-compliant protected-main review changes its lifecycle. + +## Tests and acceptance evidence + +The test-first successor to #310 initially over-constrained the repair by requiring deletion of all screen-area command intents. That was corrected: the useful protocol vocabulary remains, but the repository contract requires an opaque non-caller-mintable ownership type and requires both Set and Reset variants to carry it. After executable CI exposed the dead-helper contradiction, the contract was tightened to require that no public explicit screen-area planner exists before a Browser Session mint path does. + +Repository acceptance requires exact-head Python contracts, Rust formatting, locked workspace tests, strict Clippy, rustdoc/API documentation, and exact 100% owned-production function/line/region/branch coverage. The failing `34419810636` run is RED evidence, not acceptance. Browser acceptance remains separate and requires the pinned Chromium lane to prove application, page-observed post-condition, native interaction/outcome, owned cleanup or context destruction, and post-cleanup observation. Neither this ADR nor repository GREEN is browser GREEN. + +## Migration and rollback + +This active branch changes only the typed authority boundary. Existing callers must not be mechanically migrated by manufacturing a witness. There is intentionally no explicit public planner to call until the future Browser Session lifecycle owner creates the witness and the consuming path together. + +Rollback removes ADR 0113 and the ownership-witness change together with its contract tests. It must not restore context-only public Set/Reset authority or dead planner helpers without a separate reviewed decision, because either would reintroduce the authority or reachability defect. + +## Open follow-ups + +- Define the Browser Session aggregate transition that mints the witness only after exclusive/disposable-context establishment or equivalent ownership proof. +- Add the screen-area planner/transport consumer only in the same slice that makes the ownership witness legitimately mintable and reachable. +- Bind witness invalidation to context/session destruction and any lifecycle boundary that makes the proof stale. +- Bind runtime evidence to the exact ownership witness, Set command, page-observed post-condition, cleanup or context destruction, and post-cleanup observation. +- Decide in a separate schema change whether `PresentationProfile` should model available-screen geometry; do not infer it from total screen size. +- Continue #299/#292 real-Chromium acceptance independently of this repository-only authority contract. + +## Supersession / reversal conditions + +This ADR may be superseded if a later reviewed Browser Session design provides an equivalent non-forgeable capability with stronger lifetime semantics, or if a future WebDriver BiDi revision adds authoritative predecessor-state restoration that is separately compatibility-qualified. Publication of a newer draft alone is not sufficient. + +It is reversed only if OriginWeave removes the screen-area capability entirely or adopts another reviewed browser protocol boundary that provides equivalent ownership and cleanup guarantees. + +## References + +World Wide Web Consortium. (2026, September 3). *WebDriver BiDi* [Working Draft; runtime-qualified OriginWeave adapter pin]. https://www.w3.org/TR/2026/WD-webdriver-bidi-20260903/ + +Related repository evidence: ADR 0107, `docs/doctoring/webdriver-bidi-screen-area.md`, `docs/traceability/webdriver-bidi-screen-area-planning.md`, and `docs/traceability/webdriver-bidi-publication-current.md`. diff --git a/docs/adr/README.md b/docs/adr/README.md index a9fffa042..25aa31c0c 100644 --- a/docs/adr/README.md +++ b/docs/adr/README.md @@ -65,10 +65,11 @@ ADR 0013, ADR 0014, ADR 0110, ADR 0111, and ADR 0112 exist only on this document | ADR | Decision | Status | Governs | |---|---|---|---| | [0016](0016-bap-task-lifecycle-authority.md) | BAP task lifecycle and state authority | Proposed | BAP task states, transitions, recovery validation, transition sequencing, and authority separation | +| [0113](0113-webdriver-bidi-screen-area-ownership.md) | WebDriver BiDi screen-area ownership witness | Proposed | Browser Session-owned screen-settings mutation, destructive reset boundary, and fail-closed adapter authority | -ADR 0016 belongs to the active BAP lifecycle feature branch. Indexing it makes the branch documentation graph complete while preserving its Proposed lifecycle and active-PR, non-protected-main maturity. +ADR 0016 belongs to the active BAP lifecycle feature branch. ADR 0113 belongs to the active WebDriver BiDi screen-area ownership successor. Indexing them makes the branch documentation graph complete while preserving Proposed lifecycle and active-PR, non-protected-main maturity. -After protected-main integration, retain this subsection only when it is intentionally serving as historical provenance; otherwise protected-main reconciliation must remove it. In either case, integration alone does not change ADR 0016 from Proposed or assert implementation maturity. +After protected-main integration, retain this subsection only when it is intentionally serving as historical provenance; otherwise protected-main reconciliation must remove it. In either case, integration alone does not change ADR 0016 or ADR 0113 from Proposed or assert implementation maturity. Other active feature PRs may contain additional Proposed ADRs. Those files are not part of this canonical documentation line until integrated or deliberately reconciled here. Historical PR checks, stale branch state, or chat decisions never transfer ADR acceptance across a changed head. diff --git a/docs/doctoring/webdriver-bidi-screen-area.md b/docs/doctoring/webdriver-bidi-screen-area.md new file mode 100644 index 000000000..27b0d1ed1 --- /dev/null +++ b/docs/doctoring/webdriver-bidi-screen-area.md @@ -0,0 +1,19 @@ +# WebDriver BiDi screen-area doctoring + +The runtime-qualified protocol identity remains the W3C WebDriver BiDi Working Draft published 3 September 2026. Publication freshness is tracked separately in `docs/traceability/webdriver-bidi-publication-current.md` and does not by itself change OriginWeave's runtime pin. + +For one exact browsing context, `emulation.setScreenSettingsOverride` accepts `screenArea` as width/height or `null`. The W3C operation uses the same non-null rectangle for both the web-exposed total screen area and the web-exposed available screen area. When `screenArea` is `null`, the remote end removes that context from the screen-settings override map; the command does not restore any predecessor override value. + +That lifecycle matters independently of the profile schema. `ScreenMetrics(width, height, color_depth)` still does not model `screen.availWidth` or `screen.availHeight`, so the reusable profile-derived plan cannot silently apply the operation. A raw `WebDriverBidiBrowsingContext` also cannot authorize the separate explicit operation: replacing or removing the current override could mutate state installed by another owner. + +OriginWeave therefore keeps `WebDriverBidiScreenArea` and the `SetScreenArea` / `ResetScreenArea` command vocabulary behind an opaque `WebDriverBidiScreenAreaOwnership` witness. That witness has no public constructor in the adapter. A Browser Session integration may create it only after establishing an exclusive/disposable browsing context or an equivalent lifecycle proof that prevents replacement or removal of unrelated screen-settings state. Possession of a remote context identifier alone is not ownership evidence. + +The first witness implementation also retained two public explicit screen-area planner helpers even though no legal production path could mint the witness. Exact-head CI `34419810636` rejected both helpers under strict Clippy as dead code while repository contracts, formatting, workspace tests, and exact production coverage otherwise passed. OriginWeave does not suppress that finding. Until Browser Session introduces the reviewed witness-mint transition and a real consuming path, the adapter exposes no public explicit screen-area planner; the typed command vocabulary remains dormant and fail-closed. + +The standard operation also does **not** control color depth. `PresentationSurface::Screen` continues to fail closed with `MissingSurface(Screen)`: neither an owned screen-area command nor its command acknowledgement proves the complete Screen fingerprint surface. + +This evidence changes typed command authority only. It is not live WebDriver BiDi transport, command acknowledgement, page-observed state, browser cleanup proof, or complete Chromium presentation acceptance. Those remain separate Browser Session/runtime evidence, including post-reset observation and actual disposable-context destruction or equivalent restoration proof before a reusable boundary can be trusted again. + +## References + +World Wide Web Consortium. (2026, September 3). *WebDriver BiDi* [Working Draft; runtime-qualified OriginWeave adapter pin]. https://www.w3.org/TR/2026/WD-webdriver-bidi-20260903/ diff --git a/docs/traceability/webdriver-bidi-screen-area-planning.md b/docs/traceability/webdriver-bidi-screen-area-planning.md new file mode 100644 index 000000000..1ccbe8ea4 --- /dev/null +++ b/docs/traceability/webdriver-bidi-screen-area-planning.md @@ -0,0 +1,68 @@ +# WebDriver BiDi screen-area planning traceability + +## Problem + +The runtime-qualified WebDriver BiDi adapter plans reversible viewport/device-pixel-ratio and time-zone overrides, while the 3 September 2026 Working Draft also defines `emulation.setScreenSettingsOverride`. The screen operation is wider and more destructive than its width/height payload initially suggests. + +WebDriver BiDi applies one `screenArea` rectangle to both the web-exposed total screen area and the web-exposed available screen area. OriginWeave `ScreenMetrics` currently models width, height, and color depth, but not `screen.availWidth` or `screen.availHeight`. Automatically deriving the command from `ScreenMetrics` inside the reusable profile plan would therefore mutate a page-observable fingerprint surface that the profile neither selected nor digest-bound. Color depth remains independently uncontrolled. + +A second authority defect remains even when the operation is separated from the profile-derived plan. The standard stores one override per target browsing context. Setting a rectangle replaces that target's current override; `screenArea: null` removes the target from the override map. The standard does not restore a predecessor value. A validated browsing-context identifier therefore identifies where a mutation would occur but does not prove that OriginWeave owns the state being replaced or cleared. + +A third reachability defect became executable after the ownership witness was introduced. The adapter intentionally had no production mint path for `WebDriverBidiScreenAreaOwnership` but still retained public explicit screen-area planner helpers. Exact-head CI `34419810636` ran on a GitHub-hosted Ubuntu 24.04 runner: Python repository contracts, formatting, and locked workspace tests passed; exact production coverage passed; strict Clippy failed because both explicit planner functions were dead production code. Keeping those helpers with a lint waiver would advertise executable authority that the canonical Browser Session owner cannot yet provide. + +## Constraints + +- Keep browser-domain truth in OriginWeave; WebDriver BiDi remains an adapter, not policy authority. +- Preserve the runtime-qualified 3 September 2026 Working Draft pin. Publication freshness is owned separately by `webdriver-bidi-publication-current.md`. +- Reuse validated presentation value objects rather than reopen raw width/height validation in the adapter. +- Do not treat a browsing-context identifier as mutation authority. +- A reusable browsing context may automatically plan only observables represented by the explicit presentation contract and paired with non-destructive cleanup. +- Screen-area mutation requires an exclusive/disposable Browser Session context or equivalent ownership proof before the command can be materialized. +- Do not retain dead public planner helpers or suppress strict Clippy while the ownership mint path is absent. +- Do not add media-feature cleanup, ambient-host fallback, live protocol I/O, command-ACK success semantics, or Chromium-specific authority here. + +## Alternatives + +1. **Insert screen settings into the reusable profile-derived plan.** Rejected. The apply operation changes the currently unmodelled available-screen rectangle, and the nullable reset does not restore a predecessor override. +2. **Mark `PresentationSurface::Screen` supported after planning width/height.** Rejected because color depth remains page-observable and uncontrolled, and available-screen geometry is absent from the profile. +3. **Carry full `ScreenMetrics` in the command payload.** Rejected because the command would contain color depth, which the protocol operation does not apply, while still failing to name the available-screen side effect. +4. **Expose context-only explicit Set/Reset commands.** Rejected after review. A context identifier does not establish ownership; setting can replace another owner's override and resetting can erase it without restoration. +5. **Remove the standard capability entirely.** Rejected. The protocol operation is useful and can be represented safely without making it ambient authority. +6. **Keep public explicit planners that accept an opaque witness before any production witness-mint path exists.** Rejected by exact-head Clippy RED. No legal production caller can reach them, so they are dead API rather than useful capability. +7. **Retain the typed screen-area value, ownership witness, and Set/Reset command vocabulary, but expose no screen-area planner until Browser Session supplies the mint transition and consumer path.** Selected. Protocol semantics remain explicit while executable authority stays with the lifecycle owner. +8. **Expand `PresentationProfile` immediately with available-screen dimensions.** Deferred. That changes the canonical fingerprint schema, replay digest, consistency rules, fixtures, and buyer evidence and needs its own test-first change. + +## Decision + +`originweave-bidi` retains `WebDriverBidiScreenArea` as the validated width/height projection, retains opaque `WebDriverBidiScreenAreaOwnership`, and retains explicit `SetScreenArea` / `ResetScreenArea` command intent. Both command variants carry the ownership witness rather than a raw `WebDriverBidiBrowsingContext`. + +`WebDriverBidiScreenAreaOwnership` contains the exact validated browsing context but intentionally has no public constructor. The adapter therefore cannot mint its own proof from a context identifier. A future Browser Session integration may create the witness only after proving an exclusive/disposable lifecycle or an equivalent ownership transition. + +There is no public explicit screen-area planner while that mint path is absent. The planner/transport consumer must be introduced together with the reviewed Browser Session ownership transition so strict Clippy and runtime evidence prove a real canonical call path. No `allow(dead_code)`/`expect(dead_code)` exception is used. + +The ordinary `plan_standard_presentation_commands` and `plan_standard_presentation_cleanup` remain limited to viewport/DPR and time zone. The complete capability map continues to omit `PresentationSurface::Screen`, so `require_complete_presentation_profile()` still returns `MissingSurface(Screen)` until a reviewed owner models available-screen geometry, controls color depth, and proves the runtime application/cleanup lifecycle. + +## Evidence and acceptance + +PR #310 review identified two distinct findings. The first was the unmodelled available-screen side effect, repaired by keeping screen-area mutation out of the profile-derived reusable plan. The later exact-head review identified the ownership gap: a context-only `ResetScreenArea` could remove another owner's active override because `screenArea: null` deletes the target's override-map entry rather than restoring a prior value. + +The first #311 ownership-witness implementation then exposed a third, executable finding. Run `34419810636` on exact `f1380ab8e091964ccbdd576d933cf19d696c3791` assigned hosted runners and executed repository code. `Rust contracts` job `102692565837` passed Python contracts, formatting, and the complete locked workspace tests before strict Clippy rejected `plan_explicit_screen_area_override` and `plan_explicit_screen_area_cleanup` as dead code. `Production coverage` job `102692565938` passed measurement, diagnostics publication, and exact enforcement. This is a source RED, not a queue or coverage failure. + +The successor contract therefore requires: + +- `WebDriverBidiScreenArea` to remain the typed width/height representation derived from validated screen metrics; +- an opaque `WebDriverBidiScreenAreaOwnership` carrying the exact context with no public mint constructor in the adapter; +- `SetScreenArea` and `ResetScreenArea` to carry that ownership witness rather than a raw context identifier; +- no public explicit screen-area planner until the Browser Session ownership mint path and consuming integration exist; +- no screen-area mutation in the reusable profile-derived plan while available-screen geometry is unmodelled; +- no media-feature reset; +- no color-depth field in the screen-area value object; and +- continued fail-closed complete Screen admission. + +The initial successor RED briefly over-constrained the repair by requiring removal of all screen-area command intents. That remains unnecessary: the typed protocol vocabulary can stay dormant without exposing a callable dead planner or widening mutation authority. + +Hosted exact-head repository checks, 100% owned-production coverage, security checks, central required workflows, and realistic pinned-Chromium acceptance remain separate evidence. A command intent or acknowledgement is never substituted for apply → page-observed post-condition → interaction/outcome → owned cleanup/destruction → post-cleanup observation. + +## References + +World Wide Web Consortium. (2026, September 3). *WebDriver BiDi* [Working Draft; runtime-qualified OriginWeave adapter pin]. https://www.w3.org/TR/2026/WD-webdriver-bidi-20260903/ diff --git a/tests/test_webdriver_bidi_screen_settings_contract.py b/tests/test_webdriver_bidi_screen_settings_contract.py new file mode 100644 index 000000000..00300e2d9 --- /dev/null +++ b/tests/test_webdriver_bidi_screen_settings_contract.py @@ -0,0 +1,107 @@ +"""Repository contract for bounded standard-BiDi screen-area planning.""" + +from __future__ import annotations + +import pathlib +import unittest + +ROOT = pathlib.Path(__file__).resolve().parents[1] +SOURCE = ROOT / "crates/originweave-bidi/src/presentation_capabilities.rs" +FINGERPRINT_SOURCE = ROOT / "crates/originweave-fingerprint/src/lib.rs" + + +class WebDriverBiDiScreenSettingsContractTests(unittest.TestCase): + """Keep screen geometry typed without silently widening page-observable authority.""" + + def test_adapter_keeps_screen_area_typed_without_a_dead_external_planner(self) -> None: + """Dormant screen mutation stays typed but has no callable path before ownership can be minted.""" + text = SOURCE.read_text(encoding="utf-8") + + self.assertIn("ScreenMetrics", text) + self.assertIn("WebDriverBidiScreenArea", text) + self.assertIn("WebDriverBidiScreenAreaOwnership", text) + self.assertIn("SetScreenArea", text) + self.assertIn("ResetScreenArea", text) + self.assertNotIn("pub fn plan_explicit_screen_area_override", text) + self.assertNotIn("pub fn plan_explicit_screen_area_cleanup", text) + + def test_profile_derived_plan_cannot_silently_mutate_available_screen_area(self) -> None: + """A profile-derived reusable plan must not change an unmodelled page observable.""" + source = SOURCE.read_text(encoding="utf-8") + fingerprint = FINGERPRINT_SOURCE.read_text(encoding="utf-8") + screen_metrics = fingerprint.split("pub struct ScreenMetrics", maxsplit=1)[1] + screen_metrics = screen_metrics.split("impl ScreenMetrics", maxsplit=1)[0] + planner = source.split("pub fn plan_standard_presentation_commands", maxsplit=1)[1] + planner = planner.split("pub fn plan_standard_presentation_cleanup", maxsplit=1)[0] + cleanup = source.split("pub fn plan_standard_presentation_cleanup", maxsplit=1)[1] + cleanup = cleanup.split( + "pub const WEBDRIVER_BIDI_PRESENTATION_REVISION", maxsplit=1 + )[0] + + models_available_screen_area = ( + "available_width" in screen_metrics + and "available_height" in screen_metrics + ) + if models_available_screen_area: + return + + self.assertNotIn( + "SetScreenArea", + planner, + "WebDriver BiDi screen settings override also changes screen.availWidth/availHeight; " + "the reusable profile-derived plan must model those observables or keep the override " + "behind Browser Session ownership", + ) + self.assertNotIn( + "ResetScreenArea", + cleanup, + "generic reusable cleanup must not clear a screen override that the generic plan did " + "not own or install", + ) + + def test_screen_area_mutation_requires_non_mintable_browser_session_ownership(self) -> None: + """A context identifier alone cannot authorize replacing or clearing another owner's override.""" + text = SOURCE.read_text(encoding="utf-8") + ownership = text.split( + "pub struct WebDriverBidiScreenAreaOwnership", maxsplit=1 + )[1].split("pub enum WebDriverBidiPresentationCommand", maxsplit=1)[0] + set_variant = text.split("SetScreenArea {", maxsplit=1)[1].split("},", maxsplit=1)[0] + reset_variant = text.split("ResetScreenArea {", maxsplit=1)[1].split("},", maxsplit=1)[0] + + self.assertIn("context: WebDriverBidiBrowsingContext", ownership) + self.assertNotIn("pub context:", ownership) + self.assertNotIn("pub fn new(", ownership) + self.assertNotIn("pub fn from_", ownership) + self.assertIn("ownership: WebDriverBidiScreenAreaOwnership", set_variant) + self.assertNotIn("context: WebDriverBidiBrowsingContext", set_variant) + self.assertIn("ownership: WebDriverBidiScreenAreaOwnership", reset_variant) + self.assertNotIn("context: WebDriverBidiBrowsingContext", reset_variant) + self.assertNotIn("pub fn plan_explicit_screen_area_override", text) + self.assertNotIn("pub fn plan_explicit_screen_area_cleanup", text) + + def test_screen_surface_remains_fail_closed_until_complete_observables_are_controlled(self) -> None: + """Screen-area intent cannot satisfy the complete page-observable Screen contract.""" + text = SOURCE.read_text(encoding="utf-8") + surfaces = text.split( + "const WEBDRIVER_BIDI_PRESENTATION_SURFACES", maxsplit=1 + )[1].split("];", maxsplit=1)[0] + + self.assertNotIn("PresentationSurface::Screen", surfaces) + self.assertIn( + "PresentationError::MissingSurface(PresentationSurface::Screen)", + "".join(text.split()), + ) + + def test_screen_area_payload_does_not_carry_color_depth(self) -> None: + """The command intent must not imply authority over an unapplied screen observable.""" + text = SOURCE.read_text(encoding="utf-8") + screen_area = text.split("pub struct WebDriverBidiScreenArea", maxsplit=1)[1] + screen_area = screen_area.split("pub struct WebDriverBidiScreenAreaOwnership", maxsplit=1)[0] + + self.assertIn("width_px: u32", screen_area) + self.assertIn("height_px: u32", screen_area) + self.assertNotIn("color_depth", screen_area) + + +if __name__ == "__main__": + unittest.main()