Skip to content

feat(bidi): bind current Browser Session authority to BiDi planning - #316

Draft
seonghobae wants to merge 20 commits into
feat/privacy-presentation-identityfrom
feat/browser-session-bidi-acl
Draft

feat(bidi): bind current Browser Session authority to BiDi planning#316
seonghobae wants to merge 20 commits into
feat/privacy-presentation-identityfrom
feat/browser-session-bidi-acl

Conversation

@seonghobae

@seonghobae seonghobae commented Sep 10, 2026

Copy link
Copy Markdown
Contributor

Implements #314 as a stacked Browser Session→WebDriver BiDi ACL repair on #229 exact 6d87dff5dc572fbd74d06309d574a998f23cf02f.

Current exact source head is 8ca6c5a190d9ad2b4c7843d440e91f6070d681c2. This PR is intentionally Draft. CI 34518871994 is terminal RED: Rust contracts are now GREEN after indexing ADR 0115 in both canonical documentation indexes, including repository contracts, formatting, locked tests, strict Clippy, and rustdoc/API documentation; Production coverage still fails exact enforcement. The earlier coverage artifact isolated the remaining deficit to crates/originweave-bidi/src/lifecycle_acl.rs, and the security/lifecycle findings below remain unresolved.

Security acceptance before Ready/merge:

  • Browser Session remains the sole lifecycle/policy authority. Raw session/incarnation/context/isolation ids must not be sufficient to call remote create/destroy; lifecycle attempt/accept/reject/destroy must use non-forgeable aggregate-issued capability bound to the accepted lifecycle-port ownership. That prerequisite is now tracked by fix(browser-session): require aggregate-issued lifecycle request authority #317 from feat: add privacy-preserving presentation identity kernel #229; feat(bidi): bind current Browser Session authority to BiDi planning #316 must be non-force restacked onto its verified successor before adoption.
  • Remote creation is transactional: adapter keeps the complete protocol tuple pending, Browser Session validates domain ownership, only accepted completion promotes it to authorizing bindings, and rejection preserves the tuple only in non-authorizing quarantine/recovery evidence. Cross-adapter isolation/domain aliases and same-domain-key/different-remote redirects must fail closed.
  • Standard presentation apply/reset uses one canonical WebDriverBidiPresentationCommand vocabulary. Durable command intent must not carry durable mutation authority; execution requires fresh Browser Session + exact lifecycle-adapter revalidation immediately before browser I/O.
  • Local Rust lifetimes are not remote-liveness proof. browsingContext.contextDestroyed, BiDi session loss, or equivalent remote lifecycle change must reconcile into adapter evidence and Browser Session recovery/transport-liveness state before further mutation. Do not silently recreate/rebind a destroyed remote target. Command acknowledgement is not browser/page post-condition evidence.
  • Remote WebDriverBidiBrowsingContext remains adapter/ACL addressability, never reconstructed from originweave-core::BrowsingContextId and never promoted to Browser Session domain authority.

ADR 0115 remains Proposed and now records these blockers explicitly. Obtain fresh exact-head function/line/region/branch 100% GREEN after the causal code repair, then independent review, before ordinary adoption into #229. Screen-area mutation remains fail-closed until its separate incomplete presentation-surface contract is resolved.

No workflow/ruleset/secret changes, provider/model pin, sandbox weakening, runtime revision repin, force/destructive restack, self-approval, bypass, protected-main merge, tag or release.

@seonghobae
seonghobae marked this pull request as ready for review September 10, 2026 12:42
@coderabbitai

coderabbitai Bot commented Sep 10, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Advanced

Run ID: 49cd399c-6745-4ba2-8461-a92da55a44fb

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@seonghobae seonghobae left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Security finding on exact head 215338b77ddb3d828ebc95b796f5e8080000afde: the lifecycle binding key rejects only an exact (session, incarnation, isolation, domain context) collision. It does not reject a second backend result whose DisposableIsolationId and domain BrowsingContextId are both distinct but whose opaque WebDriverBidiBrowsingContext aliases an already-live binding. In that case Browser Session sees two apparently independent handles and mints two authorities, while both plans target the same remote browsing context. That violates the #314 invariant that exact lifecycle mapping must not redirect independently owned authority to another remote target. I added hostile RED duplicate_remote_context_is_rejected_before_second_authority_is_minted at this head. Minimum causal fix: fail closed before inserting/minting when a live binding already owns the same remote context; do not replace the existing binding or weaken Browser Session authority checks. Keep the W3C remote id as addressability, not authority. A separate follow-up should preserve full remote recovery evidence for any uncertain post-create result rather than discarding it.

@seonghobae seonghobae left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Exact-head review on b1c1648127defb89723606207033f5da5826c3ca finds two current blockers plus one recovery invariant that should be repaired in this slice rather than deferred.

  1. Hosted CI 34489022703 is RED for current source. Rust contracts job 102911146862 fails test_every_adr_is_indexed_once_with_its_file_status: 0115-browser-session-webdriver-bidi-lifecycle-acl.md exists but is absent from the second canonical ADR/documentation index. Production coverage job 102911147207 also fails exact enforcement after successful measurement/upload: lines=5804/5807, regions=7107/7108; artifact 10157093093, digest sha256:b6708e9c9f5c8368a761b48c2a69a8aced246e0556d7f2e98ee6bc7b39a36494. Do not transfer predecessor GREEN or weaken the 100% gate; update the canonical index and causally cover/remove the current uncovered production path(s), then obtain fresh exact-head GREEN.

  2. More importantly, WebDriverBidiLifecycleAdapter::create_disposable_context still loses protocol-specific recovery identity after a complete backend result. Duplicate remote-context detection returns only CreateFailedUncertain(Some(created.isolation)), discarding the already-known OriginWeave browsing_context and opaque BiDi remote_context. Browser Session therefore retains only PartialCreationIsolation, even though the adapter knew the complete (isolation, domain context, remote context) tuple. ADR 0115 already acknowledges this gap, but it is part of the security boundary: later reconciliation must not guess remote identity or speculatively destroy potentially foreign state.

  3. There is a second form of the same defect before Browser Session adoption. The adapter inserts a normal bindings entry before Browser Session checks whether the returned handle aliases an already-owned isolation or domain browsing-context id. A backend result with reused isolation + new domain context + new remote context, or reused domain context + new isolation + new remote context, can therefore be inserted into bindings; Browser Session subsequently rejects the handle and enters RecoveryRequired. The adapter is then left with a normal-map binding for a lifecycle handle the authority owner never adopted, while Browser Session recovery evidence cannot carry the BiDi remote id.

Required hostile REDs: exercise isolation-only and domain-context-only aliasing and prove rejected complete created tuples are retained in an adapter-local, non-authorizing quarantine/recovery store, distinct from accepted bindings; the duplicate-remote case should preserve the full known tuple there too. Normal authorization must never consult quarantine, and ambiguous tuples must not be auto-destroyed. Keep protocol strings out of Browser Session domain types; this belongs in the BiDi ACL/anti-corruption boundary. Only after these REDs, canonical index repair, and exact function/line/region/branch 100% GREEN should #316 be considered for ordinary adoption.

@seonghobae seonghobae left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Additional authority-boundary finding on exact b1c1648127defb89723606207033f5da5826c3ca: the new ACL protects presentation-plan projection, but the lifecycle adapter itself still exposes raw lifecycle mutation through the public DisposableContextPort contract.

WebDriverBidiLifecycleAdapter<B> publicly implements DisposableContextPort. Its public trait methods accept only BrowserSessionId, public BrowserSessionIncarnation, and (for destroy) DisposableContextHandle. A caller can obtain session.id() and session.incarnation(); DisposableContextHandle::new is public, and a retained PresentationMutationAuthority exposes its session/incarnation/isolation/browsing-context fields. Therefore code that has mutable access to the adapter can call the trait directly instead of going through BrowserSession::create_disposable_context / destroy_disposable_context.

That creates two authority bypasses: direct create can allocate remote browser state that Browser Session never records; direct destroy can remove the exact remote boundary while Browser Session still considers the context Active, because no aggregate transition occurs. In the destroy case the current binding key can be reconstructed from authority-visible values, so this is not merely a theoretical forged-id path. It contradicts the port rustdoc that raw driver identifiers cannot reconstruct cleanup authority and the #314 rule that the adapter is not policy/lifecycle authority.

Required hostile compile/runtime contract: demonstrate that ordinary downstream code possessing (session, retained authority, &mut WebDriverBidiLifecycleAdapter) cannot invoke remote create/destroy without a fresh non-constructible Browser Session-issued lifecycle command/capability. Minimal repair should make Browser Session the only issuer of the port request used for remote mutation (for example, opaque non-constructible create/destroy request values issued at the aggregate transition), while the adapter only consumes that request. Do not solve this with module convention, documentation, raw-id equality, or a public constructor. The request must also preserve incarnation/handle identity and be invalid after the relevant aggregate transition. This needs to precede real browser.createUserContext / browser.removeUserContext backend wiring; otherwise the first production backend would expose a direct browser lifecycle side door.

@seonghobae seonghobae left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Additional exact-head transaction-boundary finding on b1c1648127defb89723606207033f5da5826c3ca: fixing the current rejected-create recovery gap only by scanning each WebDriverBidiLifecycleAdapter.bindings map is insufficient, because BrowserSession::create_disposable_context<P: DisposableContextPort>(&mut self, port: &mut P) permits a different port/adapter instance on each call.

Hostile case: adapter A creates and Browser Session adopts (isolation=U, domain_context=C1, remote=R1). A distinct adapter B, with an empty local binding map, then creates either (U, C2, R2) or (U2, C1, R2). B cannot see A's local map, so its current one-shot DisposableContextPort::create_disposable_context can commit R2 to B's normal bindings and return success. Only afterward does the Browser Session aggregate detect the duplicate isolation/domain context and enter RecoveryRequired. The result is a normal adapter binding for browser state the aggregate never adopted. This remains possible even if each adapter adds perfect local alias scans.

Required RED: use one Browser Session with two distinct lifecycle adapter instances; adopt via A, then create an isolation-only alias and a domain-context-only alias via B. Prove the rejected B result never becomes an authorizing/normal binding and its complete protocol tuple remains available only as non-authorizing recovery evidence.

The causal repair needs an aggregate↔port transaction boundary, not another adapter-local equality check. The narrow options are: (a) make one lifecycle-port ownership identity an enforced invariant of the Browser Session before browser I/O, or preferably (b) split creation into attempt/candidate and aggregate accept/reject completion. In (b), the adapter keeps the complete remote tuple pending; Browser Session validates the returned domain handle against aggregate ownership; only a fresh non-constructible Browser Session-issued acceptance completes promotion into bindings; rejection moves/keeps the tuple in quarantine. Combine this with the lifecycle-capability side-door repair from review 5168479840, so neither attempt nor accept/destroy can be forged/replayed from raw IDs. Do not pass WebDriverBidiBrowsingContext into the Browser Session domain to achieve the handshake.

@seonghobae seonghobae left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Additional exact-head ACL finding: the public lifecycle-port side door can be composed with the per-adapter binding map to redirect a currently valid Browser Session authority to an unrelated remote BiDi context.

Concrete hostile case on this head: create/adopt (S,I,U,C)->R1 normally through adapter A and retain its valid PresentationMutationAuthority. Then construct a distinct adapter B whose backend returns the same domain lifecycle tuple (U,C) but a different remote context R2. Because DisposableContextPort is publicly callable with session.id() / session.incarnation() and WebDriverBidiCreatedContext::new is public, ordinary downstream code can invoke B's port directly, causing B to store (S,I,U,C)->R2 without any Browser Session transition. B.authorize_standard_presentation(&session, &authority, ...) then revalidates the authority successfully against the real BrowserSession and finds B's exact-key binding, so the resulting authorized plan targets R2, even though Browser Session adopted only A/R1.

This is stronger than merely creating untracked browser state: it defeats the stated target-binding ACL itself. Adapter-local duplicate checks cannot catch it because A and B have separate maps, and the aggregate currently has no lifecycle-port identity to distinguish them.

Required hostile RED: after legitimate adoption through A, seed B only through the currently public lifecycle port with the same (S,I,U,C) and a different remote id, then prove B cannot project the retained valid authority to R2. The causal repair should make lifecycle attempt/accept/destroy requests non-constructible and bind the accepted remote-address mapping to the same Browser Session lifecycle-port ownership identity (or an equivalent aggregate-issued adapter capability). A transaction handshake that is replayable across arbitrary adapter instances is insufficient. Keep WebDriverBidiBrowsingContext in the adapter ACL; do not move protocol strings into Browser Session. This should be repaired together with reviews 5168479840 and 5168832953, before real BiDi transport wiring.

@seonghobae seonghobae left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Additional DDD/API-boundary finding on exact b1c1648127defb89723606207033f5da5826c3ca: this slice currently creates a second presentation-command model instead of safely consuming the command/witness model that #314 explicitly exists to unlock.

presentation_capabilities.rs already owns the canonical BiDi command vocabulary and planner: WebDriverBidiPresentationOwnership, WebDriverBidiPresentationCommand, plan_standard_presentation_commands, and plan_standard_presentation_cleanup. #314 says the gap is the missing safe consuming path for that private ownership witness. But lifecycle_acl.rs defines parallel WebDriverBidiPresentationOperation, AuthorizedWebDriverBidiPresentationAction, and AuthorizedWebDriverBidiPresentationPlan, re-encoding SetViewport/SetTimezone/ResetViewport/ResetTimezone and their payloads. As a result, the new ACL does not actually mint/use the existing private WebDriverBidiPresentationOwnership, and the established command planner remains disconnected from the Browser Session authority path. A later transport would either need two command representations or silently choose one, which creates contract drift and violates the bounded-context/anti-corruption boundary rather than closing it.

Required RED/repair: prove the Browser Session ACL is the only non-public mint path into the existing presentation ownership/command contract, and that current/stale lifecycle semantics still hold there. Do not add a public ownership constructor. Prefer a crate-private mint/bridge owned by the ACL and refactor the lifetime-bound wrapper to carry/derive the canonical WebDriverBidiPresentationCommand semantics rather than duplicating its operation/payload schema. If the existing command type is too cloneable to preserve the required session lifetime, fix that ownership/lifetime contract at the canonical type boundary (or wrap it without exposing a cloneable stale capability); do not keep a second command vocabulary as the workaround. Add a repository contract that the standard apply/reset operation set has one canonical representation/planner. Screen-area remains separate and fail-closed.

This is independent of reviews 5168479840, 5168832953, and 5169424548; those lifecycle-port/cross-adapter findings still stand. Current CI 34489022703 is also still RED, so no merge/adoption claim transfers.

@seonghobae seonghobae left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Additional temporal-authority finding on exact b1c1648127defb89723606207033f5da5826c3ca: the canonical-command unification from review 5170088843 must not be implemented by simply minting WebDriverBidiPresentationOwnership and returning the existing owned WebDriverBidiPresentationCommand values.

presentation_capabilities.rs currently derives Clone for WebDriverBidiPresentationOwnership, and WebDriverBidiPresentationCommand is also owned + Clone. plan_standard_presentation_commands / plan_standard_presentation_cleanup clone that ownership into returned commands. If the new Browser Session ACL creates this witness after a current-authority check and then lets a bare canonical command escape, downstream code can retain/clone the command, release the ACL's &BrowserSession borrow, advance/destroy/lose/end the Browser Session, and later submit the still-owned command. The command then carries only an old addressability/ownership snapshot; its type no longer enforces the current lifecycle proof that #314 requires immediately before remote mutation. This is a TOCTOU/stale-capability regression even though the command vocabulary becomes canonical.

Required RED/contract: authorize a current context, derive the canonical SetViewport/SetTimezone (and cleanup) intents, then perform epoch advance or destroy/end and prove ordinary downstream code cannot execute the retained intent without a fresh live Browser Session revalidation. A compile-time lifetime test is preferred where practical; otherwise the only public execution boundary must revalidate current authority immediately before backend I/O and reject before exposing/submitting the remote target.

Minimal causal repair: keep one canonical WebDriverBidiPresentationCommand payload vocabulary, but separate durable command intent from ephemeral mutation authority. The ACL/transport bridge should hold a non-cloneable/lifetime-bound authorization wrapper (or equivalent borrow of Browser Session + adapter ownership) through execution, and mint/consume any private ownership witness only inside that live boundary. Do not make the private witness public, do not rely on callers to discard cloned commands, and do not weaken stale-authority checks. This is distinct from the lifecycle-port/cross-adapter/quarantine findings and should be solved in the same slice before real BiDi transport wiring.

@seonghobae
seonghobae marked this pull request as draft September 10, 2026 19:04

@seonghobae seonghobae left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This exact head confirms the #317 read-surface leak is also an ACL blocker here. authorize_standard_presentation(&self, session: &BrowserSession, authority: &PresentationMutationAuthority, ...) revalidates by calling the public session.presentation_authority(authority.browsing_context()). Because that same public method lets any caller holding the purported read-only &BrowserSession mint the matching current authority from a raw BrowsingContextId, the authority parameter is not a non-caller-constructible proof at this boundary. A caller with &BrowserSession + adapter access can mint the token and immediately satisfy this comparison.

Do not patch this independently around #317. Keep #316 Draft and non-force restack onto the verified Browser Session successor. Acceptance after restack must consume a purpose-bounded, non-caller-constructible fresh-validation borrow/guard from #317 at the I/O/planning boundary; retained PresentationMutationAuthority plus public snapshot lookup is insufficient. The resulting plan may remain lifetime-bound, but the proof used to create it must not be recoverable from the read-only aggregate projection.

Copy link
Copy Markdown
Contributor Author

#317 current exact f73cc5def267b99f43986cd3c504b86cb3d489d7에 review 5182010413이 추가됐습니다. Browser Session의 DisposableIsolationId가 WebDriver BiDi browser.UserContext를 one-to-one으로 소비하려면 >4096뿐 아니라 현재 parser가 거절하는 empty / surrounding-whitespace / control-containing text도 임의로 normalize/reject해서는 안 됩니다. W3C 2026-09-09 WD의 browser.UserContext = text와 user-context id 정의에는 그 lexical 제한이 없습니다.

#316은 별도 parser workaround를 만들지 말고 verified #317 successor를 non-force restack해 lossless browser-issued identity를 그대로 pending/accepted/quarantined 및 destroy/recovery key에 사용하십시오. 특정 Chromium qualification grammar가 더 좁다면 adapter qualification evidence로 다루되, browser가 이미 발급한 exact id를 trim/drop하거나 대체 id로 재구성하지 마십시오. 기존 transaction/remote-liveness blockers는 그대로 유지합니다.

Copy link
Copy Markdown
Contributor Author

#317 exact f73cc5def267b99f43986cd3c504b86cb3d489d7의 새 foundation review 5182471295#316 prerequisite에 추가합니다. 현재 #316 문서가 지적하듯 local Rust lifetime은 remote-liveness proof가 아니며, navigation freshness도 같습니다. Browser Session은 현재 explicit advance_context_epoch()만 제공하므로 실제 remote navigation/renderer replacement가 발생해도 caller가 별도 호출을 놓치면 retained pre-navigation authority가 current로 남을 수 있습니다.

#317 verified successor가 protocol-agnostic observed-lifecycle invalidation transition을 제공한 뒤에만 non-force restack하고, BiDi ACL에서 browsingContext.navigationStarted를 그 transition으로 매핑해 old authority가 presentation I/O 전에 fail closed인지 검증해 주세요. Renderer/document replacement는 Chromium/BiDi에서 실제 관측 가능한 qualified signal을 별도 근거로 선택하십시오. Event 자체가 mutation authority를 mint하면 안 되고, BiDi navigation id/event payload는 adapter evidence/correlation에만 남아야 합니다. 이 run에서는 #316 source를 수정하지 않습니다.

Copy link
Copy Markdown
Contributor Author

Navigation freshness prerequisite is now executable in stacked child #318 exact bcbd8f2ea5ad6fc4b25fd3d0cec9dfe84211aacc, based on #317 exact f73cc5def267b99f43986cd3c504b86cb3d489d7. The RED requires a Browser Session-owned record_observed_navigation(context) transition to invalidate pre-navigation PresentationMutationAuthority before any purpose-bounded adapter I/O; the observation itself must not mint authority.

Keep #316 as protocol owner: after #317/#318 produces a verified successor, map reviewed browsingContext.navigationStarted (and separately qualified renderer/document replacement evidence) one-way into that domain transition. Do not carry BiDi navigation ids/event payloads into Browser Session authority, and do not work around the prerequisite by manually calling advance_context_epoch from application code.

Copy link
Copy Markdown
Contributor Author

#318 exact ef9d0f4ac4bdb37d4b7a1b52949b30ac4bf4711c now fixes the review gap in the navigation-lifecycle RED and adds duplicate/foreign observation hostile cases. When #317 foundation produces a verified successor, this PR should map qualified browsingContext.navigationStarted observation one-way into a Browser Session non-minting invalidation transition. Repeated delivery while the context is already invalidated must be idempotent; an unknown/foreign context event must fail closed without touching unrelated authority or causing adapter I/O. Do not solve this by injecting BiDi navigation ids into mutation authority or by caller-driven advance_context_epoch.

Copy link
Copy Markdown
Contributor Author

#318 exact 1f3fa7de23ab60306ab9e1792d615f2496388e85가 Browser Session navigation freshness RED를 한 단계 더 좁혔습니다. Foundation은 이제 record_observed_navigation과 별도 bound-owner-only reestablish_presentation_authority를 요구하며, read-only BrowserSession projection이나 raw BrowsingContextId에서 fresh mutation authority를 mint하는 경로를 허용하지 않습니다. 첫 navigation 후 re-establish한 authority도 이후 두 번째 distinct navigation에서 adapter I/O 전에 stale 처리되어야 합니다.

#316 책임은 qualified BiDi lifecycle observation을 이 protocol-agnostic transition에 one-way mapping하는 것입니다. Duplicate delivery는 Browser Session이 invalidated 상태 동안 idempotent하게 흡수할 수 있지만, re-establish 뒤 늦게 도착한 과거 browsingContext.navigationStarted replay는 raw context만으로 later navigation과 구별할 수 없습니다. 따라서 adapter가 BiDi navigation/event correlation identity로 replay/dedup를 먼저 수행한 뒤 foundation transition을 호출해야 합니다. 그 protocol identity는 evidence/correlation이며 Browser Session mutation authority로 승격하지 않습니다. #317 verified successor 전에는 현재 exact 8ca6c5a190d9ad2b4c7843d440e91f6070d681c2를 restack하지 않습니다.

Copy link
Copy Markdown
Contributor Author

Dependency acceptance tightened by #318 exact 1b34a6a44afdac498ba7517582fbf3f6f4f9396a: once a qualified browsingContext.navigationStarted/renderer observation reaches Browser Session, neither stale operation execution nor generic advance_context_epoch may restore mutation capability. The adapter remains responsible for replay/dedup correlation before invoking the protocol-agnostic transition; only the bound Browser Session owner may explicitly re-establish authority. Please preserve this when non-force restacking after a verified #317 successor.

Copy link
Copy Markdown
Contributor Author

Upstream Browser Session blocker added on #317 review 5184580592: navigation invalidation must revoke presentation mutation authority without revoking the session's independent duty/capability to destroy its owned disposable user context. Current #317 destroy path still requires a current PresentationMutationAuthority, so re-establishing presentation authority merely to call browser.removeUserContext would invert the security boundary and failing to do so can strand remote ownership.

Do not patch around this in the BiDi ACL. After the #317/#318 foundation repair, map qualified navigation observations into presentation invalidation, but route browser.removeUserContext through the exact bound lifecycle owner / separate cleanup capability. Raw BiDi user-context or browsing-context identifiers remain addressability/evidence, never cleanup authority. Restack remains deferred until the verified upstream successor exists.

seonghobae commented Sep 12, 2026

Copy link
Copy Markdown
Contributor Author

Upstream Browser Session RED is now #318 exact bbe03978a1a6ff762a6fc4305323b8bcb7db9615. browser.removeUserContext/equivalent cleanup이 navigation-invalidated presentation authority와 분리되어 exact bound lifecycle owner로 실행되더라도, remote destruction이 실패·불확실하면 adapter ACL은 성공 처리/재bind/ordinary retry하지 말고 exact remote handle+epoch correlation을 Browser Session recovery evidence로 반환해야 합니다. 그 상태에서는 새 presentation authority를 다시 얻어 cleanup을 재시도하는 경로도 금지합니다. #317 verified successor 이후 non-force restack할 때 이 failure path를 BiDi lifecycle reconciliation과 연결하되 protocol identity는 recovery correlation일 뿐 mutation authority로 승격하지 마세요.

Copy link
Copy Markdown
Contributor Author

Upstream Browser Session acceptance update from #318 exact 5ff948126a24db4929541a55dbaabb24f97fc782: after a navigation-invalidated owned context enters RecoveryRequired because cleanup/destruction is unproven, any retained pre-navigation presentation authority must remain stale and fail before BiDi adapter I/O. Recovery must not restore/rebind a prior authority epoch merely because new authority issuance and ordinary cleanup retry are closed.

When #316 later restacks on the verified #317 successor, its BiDi execution path must therefore consume only a currently revalidated Browser Session authority; neither pending/recovery correlation nor retained command intent may revive an authority captured before navigation or failed cleanup. This does not change the current prerequisite/restack order.

Copy link
Copy Markdown
Contributor Author

Upstream #318 exact f936f8b8259e096bd4fb850756245cda99c583cc adds a transport-loss ordering RED that #316 should preserve when it later restacks: once Browser Session records transport loss, any already-buffered browsingContext.navigationStarted (or equivalent lifecycle observation) from that dead connection must be discarded/fail-closed before it can advance Browser Session presentation state. Adapter replay/correlation identity may prove event lineage, but it is not mutation authority; after connection/session liveness is lost, the old stream is no longer current-state truth. The Browser Session transition should return SessionNotActive, do zero adapter I/O, preserve recovery evidence unchanged, and retain pre-loss authority as stale.

Copy link
Copy Markdown
Contributor Author

Upstream Browser Session contract advanced at #318 exact 6bfd0d085145096756332f270e1fa5e803fdcbf3. BiDi consumption must preserve the same aggregate-trust ordering: once Browser Session is TransportLost, buffered/replayed navigation from that dead transport is non-authorizing and must not rewrite state/evidence; once unproven destruction enters RecoveryRequired, ordinary re-establishment/retry remains closed as SessionNotActive while exact recovery evidence is preserved. Do not map ContextNotOwned here as a recovery result because that would falsely imply lifecycle ownership was consumed. Keep replay identity/protocol correlation in this adapter, but do not let a late event or durable intent revive Browser Session authority. #316 remains correctly Draft until #317/#318 foundation adoption and non-force restack.

Copy link
Copy Markdown
Contributor Author

Upstream Browser Session handoff from #318 exact 989d069c186bbc05bd4785b8b87e86ffc6408f86: after unproven cleanup has entered RecoveryRequired, qualified BiDi navigation observations and their replays must remain non-authorizing and must not rewrite the aggregate into a different inactive state. The Browser Session RED now requires exact RecoveryRequired plus unchanged recovery evidence after each rejected late/replayed navigation, with zero adapter I/O.

#316 should therefore treat replay identity/correlation only as adapter evidence. It must not translate a rejected navigation into a new lifecycle state, rebuild authority, or infer ownership consumption from a downstream error. Restack remains blocked on the verified #317 successor; no source change requested here.

Copy link
Copy Markdown
Contributor Author

Upstream #318 exact c7117e9593940e8412b29b622db38ede0f551678 now proves an additional BiDi boundary: after Browser Session enters TransportLost or RecoveryRequired, even a foreign/raw BrowsingContextId navigation selector must be rejected as SessionNotActive before ownership lookup, with zero I/O and unchanged recovery evidence/state. #316 may own qualified BiDi event identity/replay correlation, but must not use late event context values to probe or reconstruct Browser Session ownership once aggregate transport/recovery trust is lost.

Copy link
Copy Markdown
Contributor Author

Upstream #318 exact fbfed17af630113af5aaec9d1af37790fae77a4e now makes Ended part of the same aggregate-trust-before-selector contract as TransportLost/RecoveryRequired. For the BiDi ACL, late/buffered navigation after Browser Session has normally ended must not be interpreted through historical remote/domain context mappings: formerly-owned and foreign raw selectors are both non-authorizing and should be rejected before ownership lookup or adapter I/O. Keep replay correlation as adapter evidence only; do not let a late browsingContext event reopen or reveal ended lifecycle ownership when #316 is eventually non-force restacked onto the verified Browser Session successor.

Copy link
Copy Markdown
Contributor Author

Upstream #318 exact e0407dd9314e9eface64cad10a0b03dda8e0b096 now covers a late-event case the BiDi adapter must not paper over: once #317 has proven lifecycle destruction and consumed a context’s ownership, a buffered navigation event for that context arriving while the Browser Session aggregate is still Active must be rejected as unowned/zero-I/O. BiDi replay identity or historical context mapping remains evidence only; it must not reopen observation, presentation, or lifecycle authority for the destroyed context. Keep this invariant when #316 is eventually non-force restacked onto the verified #317 successor.

@seonghobae seonghobae left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Navigation replay handoff from #318 exact 79e8db01f8ccf8297a2855dad9254d3db33a03f9: event qualification must preserve the Browser Session generation that was current when the BiDi observation was accepted for processing. Current W3C browsingContext.NavigationInfo includes context, navigation, timestamp, url, and userContext; use those protocol fields plus the exact lifecycle binding to deduplicate/correlate, then hand Browser Session the domain BrowserContextEpoch as non-authorizing provenance.

Do not defer raw BiDi events and later re-resolve only by domain/remote context id: after Browser Session explicitly re-establishes a new epoch, an old buffered navigationStarted replay could otherwise be misclassified as a new navigation and revoke current authority. Protocol navigation id remains adapter evidence, never Browser Session policy authority. After #317 successor exists, restack non-force and require: old-generation replay -> AuthorityMismatch/zero-I/O/current authority still usable; current-generation navigation -> deterministic invalidation.

@seonghobae seonghobae left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

#318 exact 8f1678f09017387c3f6d2d0aaedbaa83cbdd0489 adds a cross-context generation-confusion RED that #316 must preserve when mapping BiDi events. Protocol navigation identity/evidence must be bound to the exact owned (BrowsingContextId, BrowserContextEpoch) generation before invoking Browser Session. A current epoch from sibling context B must never be accepted as navigation provenance for context A even if both belong to the same BrowserSession; such substitution must remain zero-I/O and non-authorizing. Keep protocol navigation ids as adapter evidence, not a session-global authority or epoch selector.

@seonghobae seonghobae left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

#318 exact da9c8f1b3e9ec61a6f75d6e6e5058426ded07820 adds cross-session stale-event acceptance: a BiDi NavigationInfo from a prior Browser Session incarnation can carry raw session/context identifiers and an epoch value that numerically alias a newer aggregate. The ACL must retain the Browser Session incarnation from the lifecycle binding and correlate buffered/replayed navigation evidence to that exact incarnation before invoking the core observation transition. Protocol navigation ids remain adapter evidence; they do not mint or revoke Browser Session authority. A prior-incarnation event must fail closed with zero browser I/O and must not affect current presentation authority.

@seonghobae seonghobae left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

#318 exact da28f1d64df2c521a9537bf8a70155f75effa920 adds a Browser Session RED for navigation-start/re-establishment ordering. The adapter must not treat receipt of browsingContext.navigationStarted as sufficient evidence that presentation authority may be reissued. Published WebDriver BiDi 24 Aug 2026 defines separate navigationCommitted, fragmentNavigated, navigationAborted, and navigationFailed events.

For the #316 ACL: retain protocol navigation identity/replay qualification in this adapter, bind the event to the exact lifecycle-bound incarnation+context+domain epoch, and invoke Browser Session settlement only after a matching navigationCommitted or terminal same-document fragmentNavigated. Failed/aborted navigation must not silently reopen presentation authority. The W3C navigation id is correlation evidence only; deterministic authority remains in Browser Session. A delayed settlement for an older domain epoch must be dropped/fail closed rather than unlocking the current generation.

@seonghobae seonghobae left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Follow-up from #318 exact 56b3d41ee514ada9c9c02b01313c3996ef0b2f89: settlement correlation must reject both sibling-context epoch substitution and prior-session-incarnation ABA before the core can reopen re-establishment eligibility. The BiDi adapter should preserve the lifecycle-bound BrowserSessionIncarnation and domain context epoch alongside protocol navigation correlation, and only emit a settlement transition for that exact tuple. A stale/sibling protocol event must be dropped/fail closed without authorizing the current aggregate, even if external session/context ids and epoch numbers alias.

@seonghobae seonghobae left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

#318 exact b72ab18593f1fe0b622bccca4f1e39c254bb50d8 tightens the BiDi→Browser Session ACL. NavigationInfo.navigation and the domain (incarnation, context, epoch) tuple are correlation evidence only; neither may become settlement authority.

On an admitted navigationStarted, Browser Session should issue an opaque NavigationSettlementAuthority. This adapter may retain that witness alongside the exact W3C navigation id/replay state. Only a qualified matching navigationCommitted or terminal same-document fragmentNavigated should cause the adapter to present the same core-issued witness back to record_observed_navigation_settled. A raw tuple reconstruction, protocol navigation id by itself, sibling witness, or prior-session witness must fail closed before browser I/O. navigationFailed/navigationAborted must not silently reopen presentation authority.

Fresh W3C canonical provenance correction: the current published Working Draft is 9 September 2026 (WD-webdriver-bidi-20260909); the Editor’s Draft is a separate mutable source. This publication fact does not silently repin the runtime-qualified adapter revision.

@seonghobae seonghobae left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

#318 exposed a BiDi ACL/liveness handoff that needs to survive the later #317 restack. W3C WebDriver BiDi defines navigationAborted and navigationFailed as distinct terminal events with NavigationInfo, in addition to navigationCommitted. If #316 only returns the core-issued pending-navigation witness on commit/fragment and ignores abort/failure, Browser Session can remain navigation-pending indefinitely after a terminal negative event.

Adapter acceptance: retain the opaque Browser Session-issued witness alongside the exact W3C navigation correlation; on a matching navigationAborted or navigationFailed, return that same witness to the Browser Session negative-terminal transition with a typed non-authorizing outcome. Do not treat the protocol navigation id, raw context id, session id, or epoch as settlement authority. The terminal event must not itself restore presentation authority; explicit Browser Session re-establishment remains required. Stale/foreign/replayed terminal events must fail closed before browser I/O and must not consume a newer pending generation.

@seonghobae seonghobae left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

#318 exact c5e0cb445758747b57c117220ccfe13ebf9bd5de now requires terminal-outcome single assignment at the Browser Session boundary. #316 should still correlate/replay-qualify WebDriver BiDi navigation identity, but it must never cause one core-issued pending-navigation witness to be presented once as committed/fragment-settled and again as failed/aborted (or the reverse). If protocol/reconnect replay produces conflicting terminal evidence, the adapter may retain diagnostic provenance, but Browser Session must see at most one accepted terminal transition for that witness; a conflicting late terminal event must fail closed and must not mint/revoke authority or trigger browser I/O.

@seonghobae seonghobae left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

#318 exact 13454f971c0d8de7a290a08b0c39d01ecd3e57b7 adds an adapter-side acceptance constraint: a BiDi navigationStarted correlation may retain the core-issued pending witness only while the owning Browser Session aggregate remains Active. After transport loss, recovery-required transition, or normal end, buffered navigationCommitted / fragmentNavigated / navigationAborted / navigationFailed must not be able to spend that witness. Core will gate inactive state first; the adapter should also drop/quarantine stale protocol correlation when trust is lost rather than treating later terminal events as current authority. W3C navigation ids remain correlation evidence, never a replacement capability.

@seonghobae seonghobae left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

#318 exact a6ca33234eefdfcb6380363e60089940e01cf67b adds a consumer-side lifecycle condition: adapter-held protocol navigation id → core-issued NavigationSettlementAuthority correlation must be discarded/quarantined as soon as Browser Session reports proven destruction of that exact context, even when the overall Browser Session remains Active.

A buffered navigationCommitted, fragmentNavigated, navigationFailed, or navigationAborted after proven context destruction cannot be forwarded as if the pending witness were still spendable. Core acceptance now requires ContextNotOwned with zero adapter I/O for a pre-destroy witness. The WebDriver BiDi navigation id remains replay/correlation evidence only; it cannot reconstruct or prolong Browser Session lifecycle ownership.

When this PR is later non-force restacked onto the verified Browser Session successor, please cover the real adapter sequence navigationStarted → core witness → proven contextDestroyed/lifecycle destroy → buffered terminal event and require no authority resurrection or remote mutation.

@seonghobae seonghobae left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

#318 exact ea85d9f8ec60b87a70cc32ded7bfb02071a3c03f now covers overlapping navigation starts. #316 must use BiDi NavigationInfo.navigation only to prove that a later navigationStarted is a distinct protocol navigation rather than a replay; after that qualification it should ask Browser Session to supersede the prior pending generation and retain the new core-issued opaque witness. If terminal evidence for the superseded navigation arrives later, map it to the old witness so core rejects it; do not let an old abort/failure/commit reopen presentation authority while a newer navigation is still pending. W3C 9 Sep 2026 assigns a unique navigation id for a navigation, which is sufficient for adapter correlation but remains non-authorizing.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request priority: high

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant