From f0791e5ebb9c8f58c47e2c395ad2d290cd9d028e Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Wed, 9 Sep 2026 13:51:45 +0900 Subject: [PATCH 01/10] fix(bidi): make reusable application scope explicit --- AGENTS.md | 1 + CHANGELOG.md | 1 + CLAUDE.md | 1 + .../src/presentation_capabilities.rs | 32 ++++++++++++------- docs/product-technical-gap-baseline.md | 6 ++++ ...iver_bidi_presentation_adapter_contract.py | 13 ++++++++ 6 files changed, 42 insertions(+), 12 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index 5fd3863a5..7e8ac1995 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -60,6 +60,7 @@ The organization currently documents a **solo-maintainer** governance condition. - Keep Blink, V8, Skia, Viz, Dawn, Chromium sandboxing, Site Isolation, and Manifest V3 compatibility upstream-aligned. - Map browser presentation capabilities only when the protocol proves the complete canonical surface: width and height do not prove screen color depth, and one locale does not prove ordered languages. - Keep browser command planning distinct from execution evidence: a typed command intent bound to a validated context has not been sent, acknowledged, or observed by a page. +- A reusable presentation planner must accept only the explicitly restorable fields, never a complete `PresentationProfile` whose omitted surfaces could be mistaken for applied. - Do not assume browser/session teardown removed presentation overrides; model explicit cleanup for every override a presentation plan emits and require post-cleanup observation before reusing a browser boundary. - Pin protocol provenance to the immutable dated W3C TR URI; a mutable latest page or lagging index must not silently redefine the capability contract. - New product logic belongs in Rust control-plane modules behind narrow adapters. diff --git a/CHANGELOG.md b/CHANGELOG.md index 9feedfe76..3bb5f24fd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ 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. - 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. ### Added diff --git a/CLAUDE.md b/CLAUDE.md index ab08bc16f..ff26318b6 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -11,4 +11,5 @@ Additional constraints: - Do not merge logical origin, destination authorization, direct TCP peer proof, TLS service identity, proxy routing, or HTTP resource policy into one ambient authority. - Do not add hostname reconnect, proxy-environment inheritance, dangerous certificate-verifier hooks, Common Name fallback, TLS 0-RTT, key logging, or secret extraction to a production TLS path. - Keep changes bounded to one product gap and preserve modular crate boundaries. +- For partial browser-emulation plans, require only the named restorable fields; do not accept a complete profile unless every requested surface has an explicit application witness. - Never claim a test, benchmark, browser integration, TLS identity, GPU execution, release, or merge succeeded without current exact-head evidence. diff --git a/crates/originweave-bidi/src/presentation_capabilities.rs b/crates/originweave-bidi/src/presentation_capabilities.rs index 56f78ed8d..bd5b708b6 100644 --- a/crates/originweave-bidi/src/presentation_capabilities.rs +++ b/crates/originweave-bidi/src/presentation_capabilities.rs @@ -1,7 +1,8 @@ use std::{error::Error, fmt}; use originweave_fingerprint::{ - PresentationError, PresentationProfile, PresentationSurface, require_presentation_surfaces, + DevicePixelRatio, PresentationError, PresentationSurface, PresentationTimeZone, ViewportBounds, + require_presentation_surfaces, }; const MAX_BROWSING_CONTEXT_BYTES: usize = 256; @@ -96,23 +97,27 @@ pub enum WebDriverBidiPresentationCommand { /// pinned Working Draft. Reduced motion remains an expressible protocol capability, but the default /// reusable plan does not install it because `features: null` clears the complete media-feature /// configuration rather than restoring only OriginWeave's prior `prefers-reduced-motion` value. -/// A Browser Session owner must first bind media mutation to a genuinely disposable lifecycle or a -/// complete snapshot/restore path before constructing and sending `SetReducedMotion`. +/// The explicit arguments make this a partial-plan API: it cannot be mistaken for application of +/// a complete [`originweave_fingerprint::PresentationProfile`]. A Browser Session owner must first +/// bind media mutation to a genuinely disposable lifecycle or a complete snapshot/restore path +/// before constructing and sending `SetReducedMotion`. #[must_use] pub fn plan_standard_presentation_commands( context: &WebDriverBidiBrowsingContext, - profile: &PresentationProfile, + viewport: &ViewportBounds, + device_pixel_ratio: DevicePixelRatio, + timezone: PresentationTimeZone, ) -> [WebDriverBidiPresentationCommand; 2] { [ WebDriverBidiPresentationCommand::SetViewport { context: context.clone(), - width: profile.viewport().width(), - height: profile.viewport().height(), - device_pixel_ratio: profile.device_pixel_ratio().value(), + width: viewport.width(), + height: viewport.height(), + device_pixel_ratio: device_pixel_ratio.value(), }, WebDriverBidiPresentationCommand::SetTimezone { context: context.clone(), - timezone: profile.timezone().iana_name().to_owned(), + timezone: timezone.iana_name().to_owned(), }, ] } @@ -246,7 +251,12 @@ mod tests { assert_eq!(context.as_str(), "context-17"); assert_eq!( - plan_standard_presentation_commands(&context, &profile), + plan_standard_presentation_commands( + &context, + profile.viewport(), + profile.device_pixel_ratio(), + profile.timezone(), + ), [ WebDriverBidiPresentationCommand::SetViewport { context: context.clone(), @@ -283,9 +293,7 @@ mod tests { WebDriverBidiPresentationCommand::ResetViewport { context: context.clone(), }, - WebDriverBidiPresentationCommand::ResetTimezone { - context, - }, + WebDriverBidiPresentationCommand::ResetTimezone { context }, ] ); } diff --git a/docs/product-technical-gap-baseline.md b/docs/product-technical-gap-baseline.md index 8a702c75f..490e46014 100644 --- a/docs/product-technical-gap-baseline.md +++ b/docs/product-technical-gap-baseline.md @@ -2,6 +2,12 @@ This is a dated delivery baseline, not a substitute for the PRD, TRD, roadmap, architecture decisions, or live GitHub state. It keeps buyer-visible gaps, current issues, active pull-request evidence, and commercial completion tracks in one discoverable place. Protected `main` is the implementation boundary: code in an open pull request is not shipped behavior. +## Live continuity note: 2026-09-09 + +- Protected `main` was re-fetched at `87c4daa1830bac5a5228b6036752ad5633232085`. Issue #292 remains open; its buyer-visible acceptance is still pinned Chromium application followed by page-observed and post-cleanup evidence. +- Draft #293 (`476a8e09aa1aa7ab2e87cf7452a8ecfca47bf9c1`) is only the versioned standard-BiDi capability boundary. Its reusable command API previously accepted a complete profile despite planning only viewport/DPR and timezone. The active successor makes that partiality explicit at the type boundary; it is not Chromium runtime evidence or protected-main behavior. +- The next executable owner path remains the existing pinned-Chrome Agent Task lane, not a second browser runner: apply admitted overrides before navigation, read the controlled fixture's declared observations through bounded DOM endpoints, then prove explicit reset or owned-boundary destruction. Command acknowledgement and session teardown alone remain non-passing. + ## Observed snapshot: 2026-08-26 ### Protected-main truth diff --git a/tests/test_webdriver_bidi_presentation_adapter_contract.py b/tests/test_webdriver_bidi_presentation_adapter_contract.py index 174420d61..19dac7096 100644 --- a/tests/test_webdriver_bidi_presentation_adapter_contract.py +++ b/tests/test_webdriver_bidi_presentation_adapter_contract.py @@ -96,6 +96,19 @@ def test_reusable_apply_and_cleanup_do_not_mutate_unrestorable_media_state(self) )[0] self.assertNotIn("ResetMediaFeatures", standard_cleanup) + def test_reusable_plan_cannot_be_mistaken_for_complete_profile_application(self) -> None: + """The reusable planner must require the explicitly admitted fields only.""" + + source = ROOT / "crates/originweave-bidi/src/presentation_capabilities.rs" + text = source.read_text(encoding="utf-8") + standard_apply = text.split("pub fn plan_standard_presentation_commands", maxsplit=1)[1] + standard_apply = standard_apply.split(") ->", maxsplit=1)[0] + + self.assertNotIn("profile: &PresentationProfile", standard_apply) + self.assertIn("viewport: &ViewportBounds", standard_apply) + self.assertIn("device_pixel_ratio: DevicePixelRatio", standard_apply) + self.assertIn("timezone: PresentationTimeZone", standard_apply) + if __name__ == "__main__": unittest.main() From 2360033fbbfa849564745ae13e1d67a9eb806850 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Wed, 9 Sep 2026 13:57:02 +0900 Subject: [PATCH 02/10] docs(agents): record ready-check verification rule --- AGENTS.md | 1 + CLAUDE.md | 1 + 2 files changed, 2 insertions(+) diff --git a/AGENTS.md b/AGENTS.md index a6cf487e3..e4b33cd1f 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -61,6 +61,7 @@ The organization currently documents a **solo-maintainer** governance condition. - Map browser presentation capabilities only when the protocol proves the complete canonical surface: width and height do not prove screen color depth, and one locale does not prove ordered languages. - Keep browser command planning distinct from execution evidence: a typed command intent bound to a validated context has not been sent, acknowledged, or observed by a page. - A reusable presentation planner must accept only the explicitly restorable fields, never a complete `PresentationProfile` whose omitted surfaces could be mistaken for applied. +- Marking a draft Ready can enqueue a new exact-head run; do not merge from an earlier green result until that new run is terminal and re-fetched. - Do not assume browser/session teardown removed presentation overrides; model explicit cleanup for every override a presentation plan emits and require post-cleanup observation before reusing a browser boundary. - Pin protocol provenance to the immutable dated W3C TR URI; a mutable latest page or lagging index must not silently redefine the capability contract. - New product logic belongs in Rust control-plane modules behind narrow adapters. diff --git a/CLAUDE.md b/CLAUDE.md index e21b7e57d..380a6d9ed 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -13,4 +13,5 @@ Additional constraints: - Do not add hostname reconnect, proxy-environment inheritance, dangerous certificate-verifier hooks, Common Name fallback, TLS 0-RTT, key logging, or secret extraction to a production TLS path. - Keep changes bounded to one product gap and preserve modular crate boundaries. - For partial browser-emulation plans, require only the named restorable fields; do not accept a complete profile unless every requested surface has an explicit application witness. +- A Ready transition can replace an earlier green with a queued exact-head run; wait for its terminal result before merge. - Never claim a test, benchmark, browser integration, TLS identity, GPU execution, release, or merge succeeded without current exact-head evidence. From 5c3513fe056e3edc770dc1fd1bc34897fe66ab3d Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Wed, 9 Sep 2026 14:02:33 +0900 Subject: [PATCH 03/10] test(bidi): require validated command payload values --- ...river_bidi_presentation_adapter_contract.py | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/tests/test_webdriver_bidi_presentation_adapter_contract.py b/tests/test_webdriver_bidi_presentation_adapter_contract.py index 19dac7096..4300968bb 100644 --- a/tests/test_webdriver_bidi_presentation_adapter_contract.py +++ b/tests/test_webdriver_bidi_presentation_adapter_contract.py @@ -109,6 +109,24 @@ def test_reusable_plan_cannot_be_mistaken_for_complete_profile_application(self) self.assertIn("device_pixel_ratio: DevicePixelRatio", standard_apply) self.assertIn("timezone: PresentationTimeZone", standard_apply) + def test_public_command_intents_carry_validated_presentation_value_objects(self) -> None: + """Public command construction must not reopen validation already owned by the kernel.""" + + source = ROOT / "crates/originweave-bidi/src/presentation_capabilities.rs" + text = source.read_text(encoding="utf-8") + command_enum = text.split("pub enum WebDriverBidiPresentationCommand", maxsplit=1)[1] + command_enum = command_enum.split( + "pub fn plan_standard_presentation_commands", maxsplit=1 + )[0] + + self.assertIn("viewport: ViewportBounds", command_enum) + self.assertIn("device_pixel_ratio: DevicePixelRatio", command_enum) + self.assertIn("timezone: PresentationTimeZone", command_enum) + self.assertNotIn("width: u32", command_enum) + self.assertNotIn("height: u32", command_enum) + self.assertNotIn("device_pixel_ratio: f64", command_enum) + self.assertNotIn("timezone: String", command_enum) + if __name__ == "__main__": unittest.main() From 46abb40bef592181dcba0ec254b76c7e526e337c Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Wed, 9 Sep 2026 14:03:24 +0900 Subject: [PATCH 04/10] fix(bidi): retain validated command payload values --- .../src/presentation_capabilities.rs | 32 +++++++++---------- 1 file changed, 15 insertions(+), 17 deletions(-) diff --git a/crates/originweave-bidi/src/presentation_capabilities.rs b/crates/originweave-bidi/src/presentation_capabilities.rs index bd5b708b6..f3fb2fa5b 100644 --- a/crates/originweave-bidi/src/presentation_capabilities.rs +++ b/crates/originweave-bidi/src/presentation_capabilities.rs @@ -49,25 +49,25 @@ impl WebDriverBidiBrowsingContext { /// /// 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. -#[derive(Debug, Clone, PartialEq)] +/// 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. +#[derive(Debug, Clone, PartialEq, Eq)] pub enum WebDriverBidiPresentationCommand { /// Set viewport dimensions and device-pixel ratio together. SetViewport { /// Exact target browsing context. context: WebDriverBidiBrowsingContext, - /// CSS-pixel viewport width. - width: u32, - /// CSS-pixel viewport height. - height: u32, - /// Positive device-pixel ratio. - device_pixel_ratio: f64, + /// Validated viewport bounds from the presentation-identity kernel. + viewport: ViewportBounds, + /// Validated quantized device-pixel ratio from the presentation-identity kernel. + device_pixel_ratio: DevicePixelRatio, }, /// Set the named time zone. SetTimezone { /// Exact target browsing context. context: WebDriverBidiBrowsingContext, - /// IANA time-zone identifier. - timezone: String, + /// Validated presentation time-zone identity. + timezone: PresentationTimeZone, }, /// Set the reduced-motion media feature. /// @@ -111,13 +111,12 @@ pub fn plan_standard_presentation_commands( [ WebDriverBidiPresentationCommand::SetViewport { context: context.clone(), - width: viewport.width(), - height: viewport.height(), - device_pixel_ratio: device_pixel_ratio.value(), + viewport: *viewport, + device_pixel_ratio, }, WebDriverBidiPresentationCommand::SetTimezone { context: context.clone(), - timezone: timezone.iana_name().to_owned(), + timezone, }, ] } @@ -260,13 +259,12 @@ mod tests { [ WebDriverBidiPresentationCommand::SetViewport { context: context.clone(), - width: 1440, - height: 900, - device_pixel_ratio: 2.0, + viewport: *profile.viewport(), + device_pixel_ratio: profile.device_pixel_ratio(), }, WebDriverBidiPresentationCommand::SetTimezone { context: context.clone(), - timezone: "UTC".to_owned(), + timezone: profile.timezone(), }, ] ); From 0d36e8838221b2b43c6871a5768913afda3b00ee Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Wed, 9 Sep 2026 14:05:53 +0900 Subject: [PATCH 05/10] test(docs): distinguish BiDi planning from live transport --- ...bdriver_bidi_presentation_adapter_contract.py | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/tests/test_webdriver_bidi_presentation_adapter_contract.py b/tests/test_webdriver_bidi_presentation_adapter_contract.py index 4300968bb..9642ebeeb 100644 --- a/tests/test_webdriver_bidi_presentation_adapter_contract.py +++ b/tests/test_webdriver_bidi_presentation_adapter_contract.py @@ -127,6 +127,22 @@ def test_public_command_intents_carry_validated_presentation_value_objects(self) self.assertNotIn("device_pixel_ratio: f64", command_enum) self.assertNotIn("timezone: String", command_enum) + def test_top_level_docs_distinguish_planning_boundary_from_live_bidi_transport(self) -> None: + """Active-branch planning code must not be documented as either absent or live transport.""" + + readme = (ROOT / "README.md").read_text(encoding="utf-8") + roadmap = (ROOT / "docs/product-roadmap.md").read_text(encoding="utf-8") + + self.assertIn("`originweave-bidi` capability and command-planning boundary", readme) + self.assertIn("live WebDriver BiDi transport remains planned", readme) + self.assertNotIn( + "Chromium, WebDriver BiDi, CDP, complete MCP, HTTP, proxy, WARC, and persistent provenance adapters are planned but not yet shipped", + readme, + ) + self.assertIn("live WebDriver BiDi transport", roadmap) + self.assertIn("version-pinned capability and command-planning boundary", roadmap) + self.assertNotIn("- WebDriver BiDi adapter behind a versioned interface;", roadmap) + if __name__ == "__main__": unittest.main() From 6e07a4d920629514d745425b40642b22ef556ff5 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Wed, 9 Sep 2026 14:06:27 +0900 Subject: [PATCH 06/10] docs: distinguish BiDi planning from live transport --- README.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 0942976cf..06d893d54 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ OriginWeave is a Chromium-compatible, Rust-first control plane for governed AI agents on the web. It is designed to let an agent observe, extract, and act without turning untrusted page content into authority, exposing secrets to a model, connecting to an unapproved network destination, accepting an unauthenticated web service, or losing the evidence required to explain what happened. -> Project status: pre-alpha. The current protected repository contains independently reusable safety, resolved-destination, direct TCP peer-binding, authenticated TLS service-identity, and bounded MCP `2026-07-28` stateless `tools/call` routing/policy foundations. Chromium, WebDriver BiDi, CDP, complete MCP, HTTP, proxy, WARC, and persistent provenance adapters are planned but not yet shipped. Active PR #170 implements only conservative `tools/list` discovery metadata on top of the protected-main MCP catalog; it remains non-shipped active-PR evidence and does not make the complete MCP adapter available. +> Project status: pre-alpha. The current protected repository contains independently reusable safety, resolved-destination, direct TCP peer-binding, authenticated TLS service-identity, and bounded MCP `2026-07-28` stateless `tools/call` routing/policy foundations. Live Chromium control, CDP, complete MCP, HTTP, proxy, WARC, and persistent provenance adapters are planned but not yet shipped. This active branch adds an `originweave-bidi` capability and command-planning boundary for a pinned standard revision; live WebDriver BiDi transport remains planned, and open-PR code is not protected-main shipment. Active PR #170 implements only conservative `tools/list` discovery metadata on top of the protected-main MCP catalog; it remains non-shipped active-PR evidence and does not make the complete MCP adapter available. ## Why OriginWeave @@ -37,6 +37,7 @@ The repository is organized as independently consumable Rust crates: - `originweave-destination`: address classification, explicit destination policy, origin-bound DNS snapshots, connection pinning, rebinding detection, and redirect reauthorization. - `originweave-network`: direct-only, single-use TCP connection plans that bind an approved canonical address to the exact operating-system peer and emit credential-free evidence. - `originweave-tls`: single-use WebPKI handshakes over an existing verified TCP stream, with RFC 9525 DNS/IP identity, explicit roots and time, TLS 1.2/1.3, bounded ALPN and certificate evidence, and no reconnect or verifier bypass. +- `originweave-bidi`: active-branch, version-pinned capability and command-planning boundary for validated reusable viewport/DPR and timezone intents. It performs no live protocol transport and does not turn command construction into acknowledgement or page-observed evidence. - `originweave-resource`: task-level RAM, VRAM, thread, and frame-time budgets with cumulative mitigation plans. - `originweave-evidence`: universally value-redacted network evidence and source-bound provenance records. @@ -111,4 +112,4 @@ Read [AGENTS.md](AGENTS.md), [CONTRIBUTING.md](CONTRIBUTING.md), and [SECURITY.m ## License -Apache License 2.0. See [LICENSE](LICENSE). \ No newline at end of file +Apache License 2.0. See [LICENSE](LICENSE). From 82f2e20ed8aa47eb40c7098ce01fdcca1b2be870 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Wed, 9 Sep 2026 14:06:54 +0900 Subject: [PATCH 07/10] docs(roadmap): split BiDi planning from transport --- docs/product-roadmap.md | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/docs/product-roadmap.md b/docs/product-roadmap.md index 1e6e32ba9..7cf8fcdc4 100644 --- a/docs/product-roadmap.md +++ b/docs/product-roadmap.md @@ -73,10 +73,17 @@ Delivered document-node authority foundation: - deterministic rejection of cross-session, cross-context, cross-origin, or stale-document node reuse before a future browser adapter performs an action; - reusable core contracts without Chromium, WebDriver, selector, script-execution, network, storage, or secret dependencies. +Active-branch WebDriver BiDi foundation: + +- a version-pinned capability and command-planning boundary in `originweave-bidi` for the 3 September 2026 W3C Working Draft; +- fail-closed distinction between the complete canonical presentation profile and the standard surfaces BiDi can express; +- reusable viewport/DPR and timezone intents built only from validated presentation value objects; +- no live protocol transport, acknowledgement, page-observed application, Browser Session ownership, or cleanup proof is claimed by the planning boundary. + Remaining vertical-slice work: - launch and terminate ephemeral Chromium user contexts; -- WebDriver BiDi adapter behind a versioned interface; +- live WebDriver BiDi transport that consumes the version-pinned capability and command-planning boundary, including serialization, request/response correlation, page-observed post-conditions, and cleanup observation; - session-scoped translation from external protocol identifiers to collision-free internal browser-session, browsing-context, document-epoch, and node identities; - navigation and accessibility-tree observation; - typed `navigate`, `observe`, `query`, and `click` actions; From 3bd7b2a911fddcd6771c46568fb5e44d3c3412f8 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Wed, 9 Sep 2026 14:28:33 +0900 Subject: [PATCH 08/10] test(bidi): reject unowned reduced-motion command authority --- tests/test_bidi_media_authority_contract.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 tests/test_bidi_media_authority_contract.py diff --git a/tests/test_bidi_media_authority_contract.py b/tests/test_bidi_media_authority_contract.py new file mode 100644 index 000000000..2a0b9a7b4 --- /dev/null +++ b/tests/test_bidi_media_authority_contract.py @@ -0,0 +1,14 @@ +from pathlib import Path + + +SOURCE = Path("crates/originweave-bidi/src/presentation_capabilities.rs") + + +def test_reduced_motion_capability_does_not_mint_unowned_command() -> None: + source = SOURCE.read_text(encoding="utf-8") + command_enum = source.split("pub enum WebDriverBidiPresentationCommand {", 1)[1].split( + "/// Plan the reversible standard-BiDi presentation commands", 1 + )[0] + + assert "PresentationSurface::ReducedMotion" in source + assert "SetReducedMotion" not in command_enum From 369add64ea285497e9fa3f706ba85ba205adff80 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Wed, 9 Sep 2026 14:29:07 +0900 Subject: [PATCH 09/10] fix(bidi): remove unowned media mutation command --- .../src/presentation_capabilities.rs | 45 ++++++------------- 1 file changed, 14 insertions(+), 31 deletions(-) diff --git a/crates/originweave-bidi/src/presentation_capabilities.rs b/crates/originweave-bidi/src/presentation_capabilities.rs index f3fb2fa5b..70fdbcd0b 100644 --- a/crates/originweave-bidi/src/presentation_capabilities.rs +++ b/crates/originweave-bidi/src/presentation_capabilities.rs @@ -50,7 +50,9 @@ impl WebDriverBidiBrowsingContext { /// 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. +/// 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. #[derive(Debug, Clone, PartialEq, Eq)] pub enum WebDriverBidiPresentationCommand { /// Set viewport dimensions and device-pixel ratio together. @@ -69,16 +71,6 @@ pub enum WebDriverBidiPresentationCommand { /// Validated presentation time-zone identity. timezone: PresentationTimeZone, }, - /// Set the reduced-motion media feature. - /// - /// The pinned standard can express this command, but it is intentionally excluded from the - /// reusable default plan because standard media cleanup cannot selectively restore prior state. - SetReducedMotion { - /// Exact target browsing context. - context: WebDriverBidiBrowsingContext, - /// Whether `prefers-reduced-motion` is `reduce`. - reduce: bool, - }, /// Restore the implementation-defined viewport and remove the device-pixel-ratio override. ResetViewport { /// Exact target browsing context. @@ -94,13 +86,13 @@ 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 the default -/// reusable plan does not install it 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 Browser Session owner must first -/// bind media mutation to a genuinely disposable lifecycle or a complete snapshot/restore path -/// before constructing and sending `SetReducedMotion`. +/// 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. #[must_use] pub fn plan_standard_presentation_commands( context: &WebDriverBidiBrowsingContext, @@ -165,8 +157,9 @@ const WEBDRIVER_BIDI_PRESENTATION_SURFACES: [PresentationSurface; 4] = [ /// /// 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 default application leaves -/// media state untouched until a Browser Session owner supplies a restorable lifecycle. +/// 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 @@ -263,21 +256,11 @@ mod tests { device_pixel_ratio: profile.device_pixel_ratio(), }, WebDriverBidiPresentationCommand::SetTimezone { - context: context.clone(), + context, timezone: profile.timezone(), }, ] ); - assert_eq!( - WebDriverBidiPresentationCommand::SetReducedMotion { - context: context.clone(), - reduce: profile.reduced_motion(), - }, - WebDriverBidiPresentationCommand::SetReducedMotion { - context, - reduce: true, - } - ); } #[test] From 5be095915b445c3198ad085aa2044b691d97c6fc Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Wed, 9 Sep 2026 17:17:50 +0900 Subject: [PATCH 10/10] test(bidi): align media authority contract --- AGENTS.md | 1 + CLAUDE.md | 1 + tests/test_webdriver_bidi_presentation_adapter_contract.py | 6 ++++-- 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index e4b33cd1f..89d095611 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -61,6 +61,7 @@ The organization currently documents a **solo-maintainer** governance condition. - Map browser presentation capabilities only when the protocol proves the complete canonical surface: width and height do not prove screen color depth, and one locale does not prove ordered languages. - Keep browser command planning distinct from execution evidence: a typed command intent bound to a validated context has not been sent, acknowledged, or observed by a page. - A reusable presentation planner must accept only the explicitly restorable fields, never a complete `PresentationProfile` whose omitted surfaces could be mistaken for applied. +- When a protocol capability remains discoverable but its unsafe reusable command is removed, update every source-contract assertion to require capability presence and command absence together. - Marking a draft Ready can enqueue a new exact-head run; do not merge from an earlier green result until that new run is terminal and re-fetched. - Do not assume browser/session teardown removed presentation overrides; model explicit cleanup for every override a presentation plan emits and require post-cleanup observation before reusing a browser boundary. - Pin protocol provenance to the immutable dated W3C TR URI; a mutable latest page or lagging index must not silently redefine the capability contract. diff --git a/CLAUDE.md b/CLAUDE.md index 380a6d9ed..ec1e50548 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -13,5 +13,6 @@ Additional constraints: - Do not add hostname reconnect, proxy-environment inheritance, dangerous certificate-verifier hooks, Common Name fallback, TLS 0-RTT, key logging, or secret extraction to a production TLS path. - Keep changes bounded to one product gap and preserve modular crate boundaries. - For partial browser-emulation plans, require only the named restorable fields; do not accept a complete profile unless every requested surface has an explicit application witness. +- A discoverable protocol capability does not justify exposing an unsafe reusable command; contract tests must assert both facts. - A Ready transition can replace an earlier green with a queued exact-head run; wait for its terminal result before merge. - Never claim a test, benchmark, browser integration, TLS identity, GPU execution, release, or merge succeeded without current exact-head evidence. diff --git a/tests/test_webdriver_bidi_presentation_adapter_contract.py b/tests/test_webdriver_bidi_presentation_adapter_contract.py index 9642ebeeb..61d1ad9c2 100644 --- a/tests/test_webdriver_bidi_presentation_adapter_contract.py +++ b/tests/test_webdriver_bidi_presentation_adapter_contract.py @@ -54,7 +54,8 @@ def test_2026_09_03_bidi_capabilities_fail_closed_for_complete_profile(self) -> self.assertIn("ResetViewport", text) self.assertIn("SetTimezone", text) self.assertIn("ResetTimezone", text) - self.assertIn("SetReducedMotion", text) + self.assertIn("PresentationSurface::ReducedMotion", text) + self.assertNotIn("SetReducedMotion", text) def test_presentation_documentation_tracks_published_wd_and_cleanup_symmetry(self) -> None: """Architecture, changelog, and doctoring must describe the same pinned adapter contract.""" @@ -82,7 +83,8 @@ def test_reusable_apply_and_cleanup_do_not_mutate_unrestorable_media_state(self) self.assertNotIn("plan_exclusive_presentation_media_cleanup", text) self.assertIn("plan_standard_presentation_commands", text) self.assertIn("plan_standard_presentation_cleanup", text) - self.assertIn("SetReducedMotion", text) + self.assertIn("PresentationSurface::ReducedMotion", text) + self.assertNotIn("SetReducedMotion", text) standard_apply = text.split("pub fn plan_standard_presentation_commands", maxsplit=1)[1] standard_apply = standard_apply.split(