feat(bidi): bind current Browser Session authority to BiDi planning - #316
feat(bidi): bind current Browser Session authority to BiDi planning#316seonghobae wants to merge 20 commits into
Conversation
|
Important Review skippedAuto reviews are disabled on base/target branches other than the default branch. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Advanced Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
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. Comment |
seonghobae
left a comment
There was a problem hiding this comment.
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
left a comment
There was a problem hiding this comment.
Exact-head review on b1c1648127defb89723606207033f5da5826c3ca finds two current blockers plus one recovery invariant that should be repaired in this slice rather than deferred.
-
Hosted CI
34489022703is RED for current source. Rust contracts job102911146862failstest_every_adr_is_indexed_once_with_its_file_status:0115-browser-session-webdriver-bidi-lifecycle-acl.mdexists but is absent from the second canonical ADR/documentation index. Production coverage job102911147207also fails exact enforcement after successful measurement/upload:lines=5804/5807,regions=7107/7108; artifact10157093093, digestsha256: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. -
More importantly,
WebDriverBidiLifecycleAdapter::create_disposable_contextstill loses protocol-specific recovery identity after a complete backend result. Duplicate remote-context detection returns onlyCreateFailedUncertain(Some(created.isolation)), discarding the already-known OriginWeavebrowsing_contextand opaque BiDiremote_context. Browser Session therefore retains onlyPartialCreationIsolation, 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. -
There is a second form of the same defect before Browser Session adoption. The adapter inserts a normal
bindingsentry 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 intobindings; Browser Session subsequently rejects the handle and entersRecoveryRequired. 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
left a comment
There was a problem hiding this comment.
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
left a comment
There was a problem hiding this comment.
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
left a comment
There was a problem hiding this comment.
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
left a comment
There was a problem hiding this comment.
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
left a comment
There was a problem hiding this comment.
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
left a comment
There was a problem hiding this comment.
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.
|
#317 current exact #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는 그대로 유지합니다. |
|
#317 exact #317 verified successor가 protocol-agnostic observed-lifecycle invalidation transition을 제공한 뒤에만 non-force restack하고, BiDi ACL에서 |
|
Navigation freshness prerequisite is now executable in stacked child #318 exact Keep #316 as protocol owner: after #317/#318 produces a verified successor, map reviewed |
|
#318 exact |
|
#318 exact #316 책임은 qualified BiDi lifecycle observation을 이 protocol-agnostic transition에 one-way mapping하는 것입니다. Duplicate delivery는 Browser Session이 invalidated 상태 동안 idempotent하게 흡수할 수 있지만, re-establish 뒤 늦게 도착한 과거 |
|
Dependency acceptance tightened by #318 exact |
|
Upstream Browser Session blocker added on #317 review Do not patch around this in the BiDi ACL. After the #317/#318 foundation repair, map qualified navigation observations into presentation invalidation, but route |
|
Upstream Browser Session RED is now #318 exact |
|
Upstream Browser Session acceptance update from #318 exact 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. |
|
Upstream #318 exact |
|
Upstream Browser Session contract advanced at #318 exact |
|
Upstream Browser Session handoff from #318 exact #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. |
|
Upstream #318 exact |
|
Upstream #318 exact |
|
Upstream #318 exact |
seonghobae
left a comment
There was a problem hiding this comment.
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
left a comment
There was a problem hiding this comment.
#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
left a comment
There was a problem hiding this comment.
#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
left a comment
There was a problem hiding this comment.
#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
left a comment
There was a problem hiding this comment.
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
left a comment
There was a problem hiding this comment.
#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
left a comment
There was a problem hiding this comment.
#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
left a comment
There was a problem hiding this comment.
#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
left a comment
There was a problem hiding this comment.
#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
left a comment
There was a problem hiding this comment.
#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
left a comment
There was a problem hiding this comment.
#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.
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. CI34518871994is 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 tocrates/originweave-bidi/src/lifecycle_acl.rs, and the security/lifecycle findings below remain unresolved.Security acceptance before Ready/merge:
WebDriverBidiPresentationCommandvocabulary. Durable command intent must not carry durable mutation authority; execution requires fresh Browser Session + exact lifecycle-adapter revalidation immediately before browser I/O.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.WebDriverBidiBrowsingContextremains adapter/ACL addressability, never reconstructed fromoriginweave-core::BrowsingContextIdand 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.