Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
788b55c
test(bidi): require reversible screen settings planning
seonghobae Sep 9, 2026
c75c37f
test(bidi): separate screen geometry from full screen surface
seonghobae Sep 9, 2026
68da86f
feat(bidi): plan reversible screen area overrides
seonghobae Sep 9, 2026
c28634f
refactor(bidi): keep screen-area intent exact
seonghobae Sep 9, 2026
a6d88d6
test(bidi): require exact screen-area value object
seonghobae Sep 9, 2026
a384fd4
docs(bidi): trace screen-area planning boundary
seonghobae Sep 9, 2026
2cc97ad
docs(adr): record reversible BiDi screen-area planning
seonghobae Sep 9, 2026
f507439
docs(doctoring): pin BiDi screen-area semantics
seonghobae Sep 9, 2026
a9c0fae
docs(changelog): record reversible BiDi screen-area planning
seonghobae Sep 9, 2026
e3b2b41
docs(doctoring): align BiDi screen-area evidence
seonghobae Sep 9, 2026
8f74471
test(bidi): fail on unmodeled available screen mutation
seonghobae Sep 9, 2026
11bc809
fix(bidi): isolate coupled screen-area override
seonghobae Sep 9, 2026
1904bea
docs(bidi): record available-screen coupling
seonghobae Sep 9, 2026
6cef413
docs(bidi): bind screen-area side effects
seonghobae Sep 9, 2026
c1effef
test(bidi): require explicit screen-area intent
seonghobae Sep 9, 2026
b7d82b2
docs(adr): isolate screen-area side effects
seonghobae Sep 9, 2026
b2da7e2
docs: doctor screen-area observable coupling
seonghobae Sep 9, 2026
3445a48
docs(changelog): bound screen-area partial intent
seonghobae Sep 9, 2026
eac7db0
test(bidi): close planner contract loophole
seonghobae Sep 9, 2026
be0c745
test(bidi): require screen-area ownership before mutation
seonghobae Sep 9, 2026
f6ad738
fix(bidi): withhold unowned screen-area mutation
seonghobae Sep 9, 2026
597108d
test(bidi): bind screen-area intent to ownership witness
seonghobae Sep 9, 2026
fa17e07
fix(bidi): gate screen-area commands on ownership witness
seonghobae Sep 9, 2026
c85a4bf
docs(bidi): single-source publication freshness
seonghobae Sep 9, 2026
4837309
docs(bidi): bind screen-area reset to owned lifecycle
seonghobae Sep 9, 2026
aee332c
docs(bidi): trace screen-area ownership authority
seonghobae Sep 9, 2026
47ab396
docs(adr): govern BiDi screen-area ownership witness
seonghobae Sep 9, 2026
7bb3105
docs(adr): index screen-area ownership decision
seonghobae Sep 9, 2026
8eb3340
docs(adr): discover screen-area ownership decision
seonghobae Sep 9, 2026
f1380ab
docs(adr): align screen ownership decision structure
seonghobae Sep 9, 2026
e5295a0
test(bidi): reject dead screen-area planners before ownership mint
seonghobae Sep 10, 2026
2fc2f64
fix(bidi): remove unreachable screen-area planner API
seonghobae Sep 10, 2026
bc3865d
docs(adr): remove dead planner from ownership decision
seonghobae Sep 10, 2026
35b95d9
docs(bidi): record fail-closed planner reachability
seonghobae Sep 10, 2026
43377c2
docs(trace): bind screen planner repair to executable Clippy RED
seonghobae Sep 10, 2026
af59acb
Merge #311: require ownership before screen-area mutation
seonghobae Sep 10, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
184 changes: 155 additions & 29 deletions crates/originweave-bidi/src/presentation_capabilities.rs
Original file line number Diff line number Diff line change
@@ -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;
Expand Down Expand Up @@ -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.
Expand All @@ -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.
Expand All @@ -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,
Expand All @@ -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,
Expand Down Expand Up @@ -153,23 +232,27 @@ 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
}

/// 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())
}
Expand Down Expand Up @@ -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());
Expand Down Expand Up @@ -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");

Expand Down
5 changes: 3 additions & 2 deletions docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Loading
Loading