Skip to content

fix(browser-session): require aggregate-issued lifecycle request authority - #317

Draft
seonghobae wants to merge 95 commits into
feat/privacy-presentation-identityfrom
fix/browser-session-lifecycle-request-capability
Draft

seonghobae wants to merge 95 commits into
feat/privacy-presentation-identityfrom
fix/browser-session-lifecycle-request-capability

Conversation

@seonghobae

@seonghobae seonghobae commented Sep 10, 2026

Copy link
Copy Markdown
Contributor

Prerequisite Browser Session lifecycle-authority repair for #312 and #314/#316, stacked on #229 exact 6d87dff5dc572fbd74d06309d574a998f23cf02f.

Current exact head: 12c2d03b875c5a12fd8ba4225dc51c8a064d1d6b. The PR is open / Draft / mergeable. A prior Ready probe created exact-head CI but never received a runner; the PR has since returned to Draft. This is not a merge-ready claim.

Repaired on this lineage

  • BrowserSession::bind_lifecycle_port consumes one concrete lifecycle adapter into BoundBrowserSession<P>; no public raw-port accessor or alternate-port lifecycle path exists.
  • DisposableContextCreateRecoveryEvidence preserves aggregate-issued create attempt_epoch, disposition and complete candidate identity without collapsing a previously accepted same-valued owner into later candidate evidence.
  • Destroy/operation requests retain exact already-validated BrowserContextEpoch; stale authority fails before adapter I/O.
  • Recovery/transport-loss evidence remains non-authorizing; failed finish(&mut self) retains the same owner; Drop performs no browser I/O.
  • BoundBrowserSession::into_recovery(self) performs one-way same-adapter custody only from RecoveryRequired|TransportLost. BoundBrowserSessionRecovery<P> exposes state and exact non-authorizing evidence but no raw adapter, inner Browser Session, create, presentation-authority, epoch-advance, destroy, authorized-operation or ordinary finish path; negative capabilities are pinned by rustdoc compile_fail contracts.
  • Proven destruction removes only the live hot command-authority record after exact authority validation and adapter-proven success. Failed destruction keeps the record Uncertain with exact UnprovenDestruction { context, context_epoch }. A 258-generation same-handle hostile fixture requires monotonic epochs and predecessor rejection before lifecycle I/O.
  • DisposableIsolationId no longer invents a Browser Session lexical grammar for browser-issued WebDriver BiDi browser.UserContext. Protocol text is preserved exactly for empty, surrounding-whitespace, control-containing, ordinary and >4096-byte strings. The legacy result/error shape remains source-compatible but does not normalize or reject protocol text at this domain boundary.
  • The read-only aggregate projection is now non-authorizing: BrowserSession::presentation_authority is module-private, while BoundBrowserSession::presentation_authority remains the explicit lifecycle-owner capability path. A crate-level compile_fail contract proves external code holding only bound.browser_session() cannot mint authority from a raw BrowsingContextId.

Standards boundary

The immutable W3C WebDriver BiDi Working Draft dated 9 September 2026 defines browser.UserContext = text and uses that value as the input to browser.removeUserContext. Browser Session therefore preserves the browser-issued lifecycle address instead of adding trim/control/length grammar. Runtime-specific qualification remains a versioned adapter/deployment concern and does not mutate the protocol identity after remote creation.

#316 owns ADR 0115 (docs/adr/0115-browser-session-webdriver-bidi-lifecycle-acl.md). #317 owns Proposed ADR 0116 (docs/adr/0116-browser-session-recovery-custody-and-hot-ownership.md); #316 source and ADR 0115 remain untouched.

Review and verification state

Fresh CodeRabbit focused review completed against exact 12c2d03b875c5a12fd8ba4225dc51c8a064d1d6b. It verified that the prior P1 is repaired: the aggregate authority lookup is module-private, the explicit bound-owner lookup remains public, the read-only projection cannot call the private method, the crate-level compile_fail pins that negative capability, and no public conversion/raw-port/recovery path reopens it. It also verified the browser.UserContext lossless representation repair remains intact and git diff --check is clean. This is static source review only, not execution evidence.

Exact-head CI run 34897432671 is now terminal cancelled. Rust contracts 104154928312 and Production coverage 104154928552 both ended cancelled with steps=null; neither ever executed. Later Draft-policy CI 34900948264 is skipped. The runner-admission pattern remains handed to canonical owner ContextualWisdomLab/.github#712. OriginWeave is not weakening runs-on, synthesizing success, blind-rerunning, or bypassing gates.

No runner-backed repository-contract/rustfmt/locked-test/strict-Clippy/rustdoc/API-doc/production-coverage/Chromium-E2E GREEN is claimed for this exact head.

Remaining owner path

No force-push, destructive rebase, self-approval, bypass, gate weakening, workflow/ruleset/secret change, protected-main merge, PR close, tag, publish or release was performed.

@coderabbitai

coderabbitai Bot commented Sep 10, 2026

Copy link
Copy Markdown

Review Change StackReview Change Stack

📝 Walkthrough

Walkthrough

BoundBrowserSession이 구체적인 포트를 소유하고 생성 완료, 권한 작업, 복구 증거 및 종료 처리를 관리합니다. 생성·삭제 요청은 epoch 정보를 포함합니다. 통합 테스트와 권한 설계 문서가 새 계약을 반영합니다.

Changes

라이프사이클 포트 바인딩

Layer / File(s) Summary
요청 계약과 바운드 세션 API
crates/originweave-browser-session/src/lib.rs
생성·삭제 요청에 epoch를 추가했습니다. 생성 completion과 AuthorizedContextOperationPort를 추가했습니다. bind_lifecycle_port가 aggregate와 포트를 BoundBrowserSession에 결합합니다.
생성 완료와 복구 증거
crates/originweave-browser-session/src/lib.rs, crates/originweave-browser-session/tests/*
생성 성공과 중복 생성을 Accepted 또는 Rejected completion으로 처리합니다. completion 실패, 삭제 실패, 형제 컨텍스트, transport loss에 대한 복구 증거를 기록합니다.
포트 소유권과 종료 처리
crates/originweave-browser-session/src/lib.rs, crates/originweave-browser-session/tests/bound_session_abandonment.rs
공개 raw port accessor를 제거했습니다. finish()와 redacted Debug를 추가했습니다. unresolved ownership 상태의 Drop은 browser I/O 없이 abandonment counter를 증가시킵니다.
라이프사이클 동작 통합 검증
crates/originweave-browser-session/tests/*
바운드 포트 호출, completion, epoch, incarnation, 권한 검증, 복구 증거, transport loss, finish 및 Debug redaction을 검증하도록 테스트를 확장했습니다.
권한 설계 문서와 계약 검증
docs/adr/0114-browser-session-disposable-context-authority.md, docs/traceability/browser-session-lifecycle-authority.md, docs/uml/browser-session-lifecycle-authority.md, tests/test_browser_session_lifecycle_contract.py
ADR, 추적성 문서, UML 및 계약 테스트를 새 바운드 포트, completion, 권한 작업, 복구 및 abandonment 계약에 맞게 갱신했습니다.

Priority: ➖ Normal

Estimated code review effort: 4 (Complex) | ~60 minutes

Change: Bug fix

Sequence Diagram(s)

sequenceDiagram
  participant Caller
  participant BoundBrowserSession
  participant DisposableContextPort
  Caller->>BoundBrowserSession: bind_lifecycle_port(port)
  Caller->>BoundBrowserSession: create_disposable_context()
  BoundBrowserSession->>DisposableContextPort: create_disposable_context(request)
  BoundBrowserSession->>DisposableContextPort: complete_disposable_context_creation(completion)
  Caller->>BoundBrowserSession: execute_authorized_context_operation(authority, operation)
  BoundBrowserSession->>DisposableContextPort: execute_authorized_context_operation(request)
  Caller->>BoundBrowserSession: finish()
  BoundBrowserSession->>DisposableContextPort: destroy_disposable_context(request)
Loading

Merge Risk: 🟠 High · up to f56ef

The change reworks browser-session lifecycle ownership so that a session's adapter is bound once and cannot be swapped, which is a solid improvement. However, the test suite for this crate does not currently build (a pattern no longer matches the changed recovery-evidence shape), and a new identity-length test asserts behavior the parser does not yet provide, so the build and test workflow is blocked. An operability counter can also over-report abandoned sessions, and the standards references in the documentation and contract test disagree with each other. These should be fixed before merging.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 51.97% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 127 functions across 14 files. (4 skipped… Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed 제목은 aggregate-issued lifecycle request authority를 요구하도록 변경한 핵심 내용을 정확하고 간결하게 설명합니다.
Full details: Docstring Coverage

Explanation

Docstring coverage is 51.97% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 127 functions across 14 files. (4 skipped: 4 unsupported.)

  • Fix all pre-merge checks with AI
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/browser-session-lifecycle-request-capability

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 marked this pull request as draft September 10, 2026 19:22
@seonghobae
seonghobae marked this pull request as ready for review September 10, 2026 19:27
@seonghobae
seonghobae marked this pull request as draft September 10, 2026 19:33

@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 security finding on 97e0a4d875166ad733e78c1d3f213454ee615f01: the new lifecycle request is non-caller-constructible, but its port ownership is still self-asserted through public DisposableContextPortId::new(u64) plus DisposableContextPort::port_id(). Two distinct adapter instances can both report port_id=101; after A is bound, Browser Session's equality check will accept B as the same port. More strongly, A can relay the borrowed aggregate-issued create/destroy request to B, and B can satisfy the same scalar equality and reach remote lifecycle I/O even though B was never the aggregate-approved adapter instance. The current hostile test only uses 101 vs 102, so it proves mismatch rejection but not non-forgeable adapter ownership.

Required RED before this prerequisite can be GREEN: bind adapter A with id 101, then use distinct adapter B also claiming id 101; B must be rejected before create/destroy I/O and must not be able to consume/replay A's request. Do not repair this by documenting port-id uniqueness or randomizing a public scalar. The binding itself needs a non-caller-constructible Browser Session-approved/linear port capability or equivalent identity that a second adapter cannot self-select or replay. Keep remote BiDi identifiers outside Browser Session domain truth.

Separately, current hosted CI 34520503299 is still RED at rustfmt before tests/Clippy/rustdoc, and coverage measurement also fails; fix that operational RED without weakening gates after the authority model is corrected.

@seonghobae
seonghobae marked this pull request as ready for review September 10, 2026 19:37
@seonghobae
seonghobae marked this pull request as draft September 10, 2026 19:47

Copy link
Copy Markdown
Contributor Author

Exact-head RED evidence for 9caf9bbe4228c443b7d5a4279831765a6a38765a is now causal and reproducible. CI 34522121442 reached the Browser Session integration suite after repairing the pre-existing lifecycle-port test-signature drift. All earlier Browser Session unit/integration cases passed; only the new same-id/different-adapter hostile tests failed.

Coverage job 103021942177 observed:

  • distinct_port_with_same_claimed_id_cannot_create: actual Ok(PresentationMutationAuthority { browser_session: 17, isolation: "spoofed-isolation", browsing_context: 42, context_epoch: 2, ... }), expected Err(LifecyclePortMismatch).
  • distinct_port_with_same_claimed_id_cannot_destroy: actual Ok(()), expected Err(LifecyclePortMismatch).

So a distinct adapter B that self-reports the already-bound scalar port id can both reach create I/O and reach destroy I/O. This falsifies the current claim that aggregate-issued request + DisposableContextPortId binds lifecycle mutation to one adapter instance. Keep this RED until the port ownership is non-forgeable/non-replayable; do not weaken or delete the hostile fixture.

Repository status on this exact head is independently RED at cargo fmt --check as well. Rustfmt diagnostics artifact 10170079709, digest sha256:982aee39ad1383379288ff82575a950d0d72cbee6b2c28b8de62d15d10734068, remains available. Production coverage cannot proceed to exact enforcement while the intentional hostile tests fail. A separate nightly diagnostic also flags AtomicU64::fetch_update as deprecated in favor of try_update; treat that as root-fix work only after compatibility with the pinned stable toolchain is verified, not as a warning suppression exercise.

@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 security finding on 9caf9bbe4228c443b7d5a4279831765a6a38765a: the current port-binding preflight still cannot guarantee the stated “reject before lifecycle I/O” boundary because bind_lifecycle_port / require_bound_lifecycle_port call the public trait method DisposableContextPort::port_id(&self) before issuing or validating the aggregate request. Rust &self is not a purity guarantee: an implementation can use interior mutability, a socket/client handle, atomics, or delegated state and perform remote side effects from port_id(). The rustdoc requirement that port_id be side-effect-free is therefore a convention, not an enforced security invariant. The same problem remains even after replacing the scalar id with a harder-to-forge value if Browser Session must first invoke an arbitrary adapter callback to discover/validate that value.

Required RED: use a hostile port whose identity/preflight callback records a lifecycle side effect, then exercise (a) a first create attempt and (b) a mismatched destroy/create attempt that Browser Session is expected to reject. Both must show zero adapter/browser mutation before an aggregate-issued authority exists and is validated. Do not satisfy this by documenting port_id() purity or by testing only the reviewed WebDriver adapter implementation.

Causal direction: remove authority-sensitive identity discovery from an arbitrary preflight callback. Establish the lifecycle-port binding through a non-caller-constructible composition/binding capability (or a session-owned/linear port wrapper) before lifecycle mutation, then invoke the adapter only under the already validated create/destroy request. If DisposableContextPort remains externally implementable, the contract must not rely on &self implying side-effect freedom. Preserve the current same-id/different-adapter hostile RED and the requirement that requests cannot be replayed into a different bound adapter. Keep remote BiDi identifiers outside Browser Session domain truth.

@seonghobae
seonghobae marked this pull request as ready for review September 10, 2026 20:09
@seonghobae
seonghobae marked this pull request as draft September 10, 2026 20:13
@seonghobae
seonghobae marked this pull request as ready for review September 10, 2026 21:14

Copy link
Copy Markdown
Contributor Author

@coderabbitai review

Please review exact head 9cde981899950b900698a17e7fa739af59f6bb4f, especially the security/API claim that consuming BrowserSession + P into BoundBrowserSession<P> removes pre-authority adapter callbacks and ordinary distinct-adapter substitution/replay without moving protocol-specific BiDi ownership into the Browser Session domain.

@coderabbitai

coderabbitai Bot commented Sep 10, 2026

Copy link
Copy Markdown
🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '%s\n' '== Exact revision and PR diff summary =='
git rev-parse HEAD
git status --short
git diff --stat 6d87dff5dc572fbd74d06309d574a998f23cf02f..HEAD
git diff --name-only 6d87dff5dc572fbd74d06309d574a998f23cf02f..HEAD

printf '%s\n' '== Candidate lifecycle files =='
fd -t f -e rs . | while IFS= read -r file; do
  if rg -q -i 'BrowserSession|BoundBrowserSession|DisposableContextPort|bind_lifecycle_port|DisposableContext(Create|Destroy)Request' "$file"; then
    printf '%s\n' "$file"
  fi
done

printf '%s\n' '== Rust API outline for candidate files =='
fd -t f -e rs . | while IFS= read -r file; do
  if rg -q -i 'BrowserSession|BoundBrowserSession|DisposableContextPort|bind_lifecycle_port|DisposableContext(Create|Destroy)Request' "$file"; then
    ast-grep outline "$file" --items all --type struct,trait,impl,function,method --match 'BrowserSession|BoundBrowserSession|DisposableContextPort|bind_lifecycle_port|DisposableContextCreateRequest|DisposableContextDestroyRequest' || true
  fi
done

printf '%s\n' '== Lifecycle declarations, calls, and port-ID remnants =='
rg -n -C 4 --type rust \
  '(\bbind_lifecycle_port\s*\(|\b(create|destroy)_(disposable_)?context\s*\(|\bDisposableContextPort\b|\bBoundBrowserSession\b|\bDisposableContextPortId\b|\bport_id\s*\()' \
  .

printf '%s\n' '== Browser Session BiDi/protocol-specific references =='
rg -n -i -C 3 --type rust \
  '(bidi|webdriver|pending|quarantin|remote.liveness|remote_liveness|accepted)' \
  . \
  -g '!**/test/**' -g '!**/tests/**' || true

printf '%s\n' '== Documentation and external fixtures relevant to binding =='
fd -t f . | while IFS= read -r file; do
  if rg -q -i 'BoundBrowserSession|bind_lifecycle_port|DisposableContextPortId|distinct.adapter|hostile|linear.lifecycle|ADR.?.?0114' "$file"; then
    printf '%s\n' "$file"
  fi
done

Length of output: 147304


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '%s\n' '== Browser Session public types and lifecycle implementation =='
sed -n '1,720p' crates/originweave-browser-session/src/lib.rs

printf '%s\n' '== Focused external hostile tests =='
for file in \
  crates/originweave-browser-session/tests/lifecycle_port_authority.rs \
  crates/originweave-browser-session/tests/lifecycle_port_preflight_side_effect.rs \
  crates/originweave-browser-session/tests/lifecycle_port_same_id_spoof.rs \
  crates/originweave-browser-session/tests/sequential_incarnation_reuse.rs \
  crates/originweave-browser-session/tests/destroy_failure_requires_recovery.rs \
  tests/test_browser_session_lifecycle_contract.py
do
  printf '\n== %s ==\n' "$file"
  cat -n "$file"
done

printf '%s\n' '== Browser Session dependency boundary =='
browser_manifest="$(fd -t f '^Cargo\.toml$' crates/originweave-browser-session | head -n 1)"
printf '%s\n' "$browser_manifest"
cat -n "$browser_manifest"
printf '%s\n' '== Direct Browser Session crate imports =='
rg -n '^\s*use\s+originweave_[A-Za-z0-9_]+' crates/originweave-browser-session/src crates/originweave-browser-session/tests

printf '%s\n' '== Public mutable extraction and lifecycle helper reachability =='
rg -n -C 3 --type rust \
  '(pub(\([^)]*\))?\s+(fn|struct|enum|trait)|fn\s+(create|destroy)_disposable_context_with_port|fn\s+(into_|as_mut|lifecycle_port_mut)|&mut\s+(self|P)|->\s*&mut)' \
  crates/originweave-browser-session/src/lib.rs

printf '%s\n' '== Focused PR diff for implementation and contracts =='
git diff --find-renames --find-copies --unified=50 \
  6d87dff5dc572fbd74d06309d574a998f23cf02f..HEAD -- \
  crates/originweave-browser-session/src/lib.rs \
  tests/test_browser_session_lifecycle_contract.py \
  docs/adr/0114-browser-session-disposable-context-authority.md \
  docs/traceability/browser-session-lifecycle-authority.md \
  docs/uml/browser-session-lifecycle-authority.md

printf '%s\n' '== Changed documents =='
for file in \
  docs/adr/0114-browser-session-disposable-context-authority.md \
  docs/traceability/browser-session-lifecycle-authority.md \
  docs/uml/browser-session-lifecycle-authority.md
do
  printf '\n== %s ==\n' "$file"
  cat -n "$file"
done

Length of output: 215228


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '%s\n' '== Repository-wide lifecycle API references =='
rg -n -C 3 --type rust \
  '(\bcreate_disposable_context\s*\(|\bdestroy_disposable_context\s*\(|\bbind_lifecycle_port\s*\(|\blifecycle_port_mut\b|\binto_(inner|port|browser_session)\b|\bDisposableContextPortId\b|\bport_id\s*\()' \
  .

printf '%s\n' '== Public methods on BrowserSession and BoundBrowserSession =='
rg -n -U --type rust \
  'impl(?:<[^>]+>)?\s+(?:BrowserSession|BoundBrowserSession(?:<[^>]+>)?)\s*\{(?s:.*?)^\}' \
  crates/originweave-browser-session/src/lib.rs || true
rg -n -C 2 --type rust \
  'pub\s+(?:const\s+)?fn\s+(?:create_disposable_context|destroy_disposable_context|bind_lifecycle_port|lifecycle_port|browser_session|into_[A-Za-z_]+|[A-Za-z_]+_mut)\s*\(' \
  crates/originweave-browser-session/src/lib.rs

printf '%s\n' '== Trait implementation locations =='
rg -n -C 2 --type rust 'impl\s+(?:[A-Za-z0-9_<>:]+\s+for\s+)?DisposableContextPort\b' .

printf '%s\n' '== Patch whitespace integrity =='
git diff --check 6d87dff5dc572fbd74d06309d574a998f23cf02f..HEAD

printf '%s\n' '== Exact-head PR checks and review state =='
gh pr view 317 --repo ContextualWisdomLab/OriginWeave \
  --json number,headRefOid,isDraft,mergeStateStatus,reviewDecision,statusCheckRollup \
  --jq '{number, headRefOid, isDraft, mergeStateStatus, reviewDecision, checks: [.statusCheckRollup[] | {name, status, conclusion, workflowName}]}'

Length of output: 68284


@seonghobae Review complete for exact head 9cde981899950b900698a17e7fa739af59f6bb4f.

I found no code-level issue in the requested security/API change.

  • BrowserSession::bind_lifecycle_port(self, port) only moves the aggregate and port into private BoundBrowserSession<P> fields. It invokes no adapter callback.
  • The public lifecycle methods exist on BoundBrowserSession<P>. They accept no replacement port.
  • Repository-wide inspection found no DisposableContextPortId, port_id(), mutable port accessor, extraction method, or alternate public lifecycle entry point.
  • Create validates Active state and reserves the epoch before it calls the bound port.
  • Destroy validates state, session, incarnation, isolation, context, and epoch before it calls the bound port.
  • The request types have private fields and no public constructors.
  • The external hostile fixtures cover the pre-authority callback case, ordinary distinct-adapter substitution, sequential-incarnation replay, and recovery behavior.
  • originweave-browser-session depends only on originweave-core. It does not import or depend on the BiDi crate. The BiDi-specific pending → accepted/quarantined state and remote-liveness responsibility remain outside the Browser Session domain.

The structural binding prevents ordinary callers from substituting or replaying a different adapter after binding. It cannot prevent a malicious trusted DisposableContextPort implementation from internally forwarding an authorized call. ADR 0114 states this TCB boundary.

Exact-head verification is not complete. GitHub reports Rust contracts and Production coverage as queued, and the PR state is UNSTABLE. I cannot determine readiness or approve the PR until the required exact-head checks are terminal and GREEN.

✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@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.

Merge-blocking integration finding on exact 9cde981899950b900698a17e7fa739af59f6bb4f: the linear BoundBrowserSession<P> closes caller-side port substitution, but the lifecycle contract still has no aggregate→adapter completion phase for creation, and DisposableContextCreateRequest is not unique per create attempt. Every create request in one Browser Session incarnation exposes only the same (BrowserSessionId, BrowserSessionIncarnation) pair, even though BrowserSession already reserves a distinct context epoch before remote I/O. After port.create_disposable_context(&request) returns, the aggregate may accept the handle or reject it as duplicate/ambiguous and enter RecoveryRequired, but DisposableContextPort receives no accept/reject completion carrying a per-attempt identity. A BiDi adapter implementing #316 therefore cannot deterministically move the exact remote tuple from pending→accepted or pending→quarantine without inventing adapter-local correlation/authority, keying only by the same session/incarnation, relying on call order, or prematurely authorizing before Browser Session validation.

Required RED before adoption: one bound port produces at least two creation candidates in the same Browser Session incarnation, retains them as separate pending protocol tuples, and Browser Session accepts one while rejecting the other (e.g. duplicate domain context/isolation). The adapter must promote only the accepted candidate and quarantine exactly the rejected candidate; neither candidate may collide/overwrite because their create requests are indistinguishable. No remote tuple may become authorizing before aggregate acceptance.

Causal repair should extend the Browser Session-owned transaction boundary, not move BiDi ids into this domain: mint a non-caller-constructible per-attempt lifecycle identity/capability (the already-reserved epoch is a natural candidate if its semantics fit), pass it in the create request, and provide an aggregate-issued accept/reject completion that the exact bound port consumes. The adapter keeps protocol tuples pending until that completion. Preserve structural port binding and the existing no-preflight/same-id hostile tests. #316 remains the owner of remote BiDi pending/accepted/quarantine data; #317 should only provide the domain transaction identity/completion contract it needs.

Copy link
Copy Markdown
Contributor Author

@coderabbitai review

Please review exact head 77819f1236d295f9cca2c5eced72cac9b812b78f, focused on the code-current Browser Session doctoring and repository contract added after the already-reviewed production slice:

  • docs/adr/0115-browser-session-recovery-custody-and-hot-ownership.md
  • docs/adr/README.md
  • docs/traceability/browser-session-lifecycle-authority.md
  • docs/uml/browser-session-lifecycle-authority.md
  • tests/test_browser_session_lifecycle_contract.py

Verify these against src/recovery.rs, recovery_owner_handoff.rs, and proven_destroy_releases_hot_ownership.rs. In particular, check that recovery custody cannot regain ordinary Browser Session authority, proven destruction retires only hot command-authority state, failed destruction preserves uncertain ownership/evidence, same-raw-id reuse cannot revive predecessor authority, and protocol-specific recovery remains #316-owned. Do not treat active-PR documentation as protected-main shipment.

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

coderabbitai Bot commented Sep 14, 2026

Copy link
Copy Markdown

Tip

For best results, initiate chat on the files or code changes.

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

expected='77819f1236d295f9cca2c5eced72cac9b812b78f'
actual="$(git rev-parse HEAD)"
printf '== revision ==\nexpected=%s\nactual=%s\n' "$expected" "$actual"
git status --short
printf '\n== exact-head changed files ==\n'
git diff --name-status "${expected}^" "$expected"

printf '\n== focused file outlines ==\n'
for file in \
  crates/originweave-browser-session/src/recovery.rs \
  crates/originweave-browser-session/src/browser_session.rs \
  crates/originweave-browser-session/tests/recovery_owner_handoff.rs \
  crates/originweave-browser-session/tests/proven_destroy_releases_hot_ownership.rs
do
  printf '\n== %s ==\n' "$file"
  ast-grep outline "$file" --items all --type struct,enum,trait,impl,function,method \
    --match 'BoundBrowserSessionRecovery|BoundBrowserSession|BrowserSession|Recovery|destroy|context|authority|end' || true
done

printf '\n== exact documentation and contract patch ==\n'
git diff --find-renames --unified=80 "${expected}^" "$expected" -- \
  docs/adr/0115-browser-session-recovery-custody-and-hot-ownership.md \
  docs/adr/README.md \
  docs/traceability/browser-session-lifecycle-authority.md \
  docs/uml/browser-session-lifecycle-authority.md \
  tests/test_browser_session_lifecycle_contract.py

printf '\n== requested documentation and repository contract ==\n'
for file in \
  docs/adr/0115-browser-session-recovery-custody-and-hot-ownership.md \
  docs/adr/README.md \
  docs/traceability/browser-session-lifecycle-authority.md \
  docs/uml/browser-session-lifecycle-authority.md \
  tests/test_browser_session_lifecycle_contract.py
do
  printf '\n== %s ==\n' "$file"
  cat -n "$file"
done

printf '\n== recovery custody implementation and authority escape search ==\n'
rg -n -C 14 --type rust \
  '(struct\s+BoundBrowserSessionRecovery|impl.*BoundBrowserSessionRecovery|fn\s+into_recovery|pub(\([^)]*\))?\s+(?:const\s+)?fn|browser_session|lifecycle_port|into_(inner|port|browser_session)|Deref(Mut)?|From<.*BoundBrowserSessionRecovery|Into<.*BoundBrowserSession|create_disposable_context|destroy_disposable_context|advance_context_epoch|presentation_authority|finish)' \
  crates/originweave-browser-session/src/recovery.rs \
  crates/originweave-browser-session/src/browser_session.rs \
  crates/originweave-browser-session/src/lib.rs

printf '\n== destruction, hot ownership, uncertainty, abandonment, and reuse implementation ==\n'
rg -n -C 14 --type rust \
  '(enum\s+OwnedContextState|struct\s+OwnedContextRecord|fn\s+(destroy_disposable_context_with_port|context_for_authority_mut|end|has_unresolved_remote_ownership|record_transport_loss)|contexts\.(remove|get_mut|insert|is_empty)|UnprovenDestruction|RecoveryRequiredOwnedHandle|TransportLossOwnedHandle|next_epoch|context_epoch|port\.destroy_disposable_context\s*\()' \
  crates/originweave-browser-session/src/browser_session.rs \
  crates/originweave-browser-session/src/recovery.rs

printf '\n== focused hostile tests ==\n'
for file in \
  crates/originweave-browser-session/tests/recovery_owner_handoff.rs \
  crates/originweave-browser-session/tests/proven_destroy_releases_hot_ownership.rs
do
  printf '\n== %s ==\n' "$file"
  cat -n "$file"
done

printf '\n== protocol ownership and `#316` references ==\n'
rg -n -C 8 '(`#316`|BiDi|WebDriver|protocol-specific|protocol specific|remote liveness|recovery)' \
  docs/adr/0115-browser-session-recovery-custody-and-hot-ownership.md \
  docs/traceability/browser-session-lifecycle-authority.md \
  docs/uml/browser-session-lifecycle-authority.md \
  crates/originweave-browser-session/src

printf '\n== contract coverage of docs, public surface, and hostile tests ==\n'
rg -n -C 12 \
  '(0115|recovery_owner_handoff|proven_destroy_releases_hot_ownership|BoundBrowserSessionRecovery|into_recovery|browser_session|create_attempt_recovery_evidence|recovery_evidence|hot ownership|same.raw|same-raw|`#316`)' \
  tests/test_browser_session_lifecycle_contract.py

printf '\n== whitespace integrity ==\n'
git diff --check "${expected}^" "$expected"

Length of output: 204516


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '%s\n' '== ADR index entries for 0114 and 0115 =='
rg -n -C 8 '(0114|0115|Browser Session)' docs/adr/README.md

printf '%s\n' '== ADR 0115 exact decision, evidence, and adoption claims =='
sed -n '1,155p' docs/adr/0115-browser-session-recovery-custody-and-hot-ownership.md | nl -ba -v 1

printf '%s\n' '== Traceability recovery custody, hot ownership, and status claims =='
sed -n '70,170p' docs/traceability/browser-session-lifecycle-authority.md | nl -ba -v 70

printf '%s\n' '== UML recovery custody and same-raw-identity sections =='
sed -n '100,245p' docs/uml/browser-session-lifecycle-authority.md | nl -ba -v 100

printf '%s\n' '== Python repository contract complete architecture-document method =='
sed -n '264,430p' tests/test_browser_session_lifecycle_contract.py | nl -ba -v 264

printf '%s\n' '== Public recovery surface with all public impls =='
sed -n '1,150p' crates/originweave-browser-session/src/recovery.rs | nl -ba -v 1
rg -n -U --type rust 'impl(?:<[^>]+>)?\s+BoundBrowserSessionRecovery(?:<[^>]+>)?\s*\{(?s:.*?)^\}' crates/originweave-browser-session/src

printf '%s\n' '== Destroy path and completion/end/abandonment classification =='
sed -n '585,615p' crates/originweave-browser-session/src/browser_session.rs | nl -ba -v 585
sed -n '875,1010p' crates/originweave-browser-session/src/browser_session.rs | nl -ba -v 875
sed -n '1030,1125p' crates/originweave-browser-session/src/browser_session.rs | nl -ba -v 1030

printf '%s\n' '== exact-head docs contract diff integrity =='
git diff --check 77819f1236d295f9cca2c5eced72cac9b812b78f^ 77819f1236d295f9cca2c5eced72cac9b812b78f

Length of output: 69765


@seonghobae Review complete for exact head 77819f1236d295f9cca2c5eced72cac9b812b78f.

I found no implementation, documentation, or repository-contract defect in the requested slice.

  • BoundBrowserSessionRecovery<P> exposes only state(), recovery_evidence(), and create_attempt_recovery_evidence().
  • It exposes no raw port, inner BoundBrowserSession<P>, or inner BrowserSession.
  • The rustdoc compile_fail contracts cover create, direct and indirect presentation-authority lookup, epoch advance, destroy, and finish().
  • recovery_owner_handoff.rs distinguishes RecoveryRequired after unproven destroy from TransportLost.
  • The non-Clone port fixture confirms that into_recovery() moves the same adapter instance. The handoff does not create, destroy, or implicitly clean up browser state.

The hot-ownership documentation matches the implementation.

  • destroy_disposable_context_with_port validates the authority before adapter I/O.
  • Browser Session removes the hot context record only after port.destroy_disposable_context() returns Ok(()).
  • A destroy failure retains the record as Uncertain, records exact UnprovenDestruction { context, context_epoch } evidence, and enters RecoveryRequired.
  • proven_destroy_releases_hot_ownership.rs verifies 258 same-value reuse cycles. It verifies monotonic epochs and stale-authority rejection before adapter I/O.
  • The documentation correctly separates bounded command-authority state from durable recovery evidence and audit history.

The protocol boundary remains clear.

  • ADR 0115, traceability, and UML keep WebDriver BiDi pending/accepted/quarantined state, remote-liveness interpretation, and protocol recovery commands in #316.
  • The Browser Session domain retains recovery custody but does not claim protocol recovery authority.

The active-PR maturity boundary is also correct.

  • ADR 0115 is Proposed.
  • The traceability document is IMPLEMENTED_ON_ACTIVE_PR.
  • docs/adr/README.md states that ADR 0114 and ADR 0115 remain active-PR documentation until integration.
  • The documents do not present this branch as protected-main shipment.

git diff --check reported no whitespace errors.

This was a static review. It is not test, CI, or protected-main GREEN evidence.

You are interacting with an AI system.

Copy link
Copy Markdown
Contributor Author

@coderabbitai review

Please review exact head cde661784345b9489f2a98f4ccc4d261b6c8da3d. Focus on the cross-PR ADR collision repair and the Browser Session doctoring/contract consistency:

Verify there is no remaining 0115 collision in #317, that ADR 0116 is Proposed and code-current with src/recovery.rs, recovery_owner_handoff.rs, and proven_destroy_releases_hot_ownership.rs, and that the documents do not claim protected-main shipment. Also check that recovery custody cannot regain ordinary Browser Session authority and that proven destruction retires only hot command-authority state while failed destruction preserves uncertain ownership/evidence.

@coderabbitai

coderabbitai Bot commented Sep 14, 2026

Copy link
Copy Markdown

Tip

For best results, initiate chat on the files or code changes.

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

expected='cde661784345b9489f2a98f4ccc4d261b6c8da3d'
actual="$(git rev-parse HEAD)"
printf '== revision ==\nexpected=%s\nactual=%s\n' "$expected" "$actual"
git status --short
printf '\n== changed files at exact head ==\n'
git diff --name-status "${expected}^" "$expected"

printf '\n== ADR filename inventory ==\n'
fd -t f '^011[56].*\.md$' docs/adr -x sh -c 'printf "%s\n" "$1"' sh {}

printf '\n== all ADR 0115/0116 references in changed PR material ==\n'
rg -n -C 8 '(ADR[ -]?0115|ADR[ -]?0116|0115-browser-session|0116-browser-session|`#316`|`#317`)' \
  docs tests crates/originweave-browser-session

printf '\n== exact documentation and repository-contract patch ==\n'
git diff --find-renames --unified=90 "${expected}^" "$expected" -- \
  docs/adr/0116-browser-session-recovery-custody-and-hot-ownership.md \
  docs/adr/README.md \
  docs/traceability/browser-session-lifecycle-authority.md \
  docs/uml/browser-session-lifecycle-authority.md \
  tests/test_browser_session_lifecycle_contract.py

printf '\n== requested documentation and contract ==\n'
for file in \
  docs/adr/0116-browser-session-recovery-custody-and-hot-ownership.md \
  docs/adr/README.md \
  docs/traceability/browser-session-lifecycle-authority.md \
  docs/uml/browser-session-lifecycle-authority.md \
  tests/test_browser_session_lifecycle_contract.py
do
  printf '\n== %s ==\n' "$file"
  cat -n "$file"
done

printf '\n== focused source and tests ==\n'
for file in \
  crates/originweave-browser-session/src/recovery.rs \
  crates/originweave-browser-session/src/browser_session.rs \
  crates/originweave-browser-session/tests/recovery_owner_handoff.rs \
  crates/originweave-browser-session/tests/proven_destroy_releases_hot_ownership.rs \
  crates/originweave-browser-session/tests/destroy_failure_requires_recovery.rs
do
  printf '\n== %s ==\n' "$file"
  ast-grep outline "$file" --items all --type struct,enum,trait,impl,function,method \
    --match 'BoundBrowserSessionRecovery|BoundBrowserSession|BrowserSession|Recovery|destroy|context|authority|end' || true
  cat -n "$file"
done

printf '\n== public recovery surface and authority escape paths ==\n'
rg -n -C 10 --type rust \
  '(struct\s+BoundBrowserSessionRecovery|impl.*BoundBrowserSessionRecovery|fn\s+into_recovery|pub(\([^)]*\))?\s+(?:const\s+)?fn|browser_session|lifecycle_port|into_(inner|port|browser_session)|Deref(Mut)?|From<.*BoundBrowserSessionRecovery|Into<.*BoundBrowserSession|create_disposable_context|destroy_disposable_context|advance_context_epoch|presentation_authority|finish)' \
  crates/originweave-browser-session/src

printf '\n== hot ownership, failure, and abandonment implementation ==\n'
rg -n -C 14 --type rust \
  '(enum\s+OwnedContextState|struct\s+OwnedContextRecord|fn\s+(destroy_disposable_context_with_port|context_for_authority_mut|end|has_unresolved_remote_ownership|record_transport_loss)|contexts\.(remove|get_mut|insert|is_empty)|UnprovenDestruction|RecoveryRequiredOwnedHandle|TransportLossOwnedHandle|next_epoch|context_epoch|port\.destroy_disposable_context\s*\()' \
  crates/originweave-browser-session/src

printf '\n== contract coverage for ADR number, custody, and hot ownership ==\n'
rg -n -C 14 \
  '(0115|0116|recovery_owner_handoff|proven_destroy_releases_hot_ownership|BoundBrowserSessionRecovery|into_recovery|browser_session|compile_fail|hot ownership|same.raw|same-raw|protected.main|protected-main|IMPLEMENTED_ON_ACTIVE_PR|Proposed)' \
  tests/test_browser_session_lifecycle_contract.py

printf '\n== whitespace integrity ==\n'
git diff --check "${expected}^" "$expected"

printf '\n== exact-head GitHub status ==\n'
gh pr view 317 --repo ContextualWisdomLab/OriginWeave \
  --json headRefOid,mergeStateStatus,reviewDecision,statusCheckRollup \
  --jq '{headRefOid, mergeStateStatus, reviewDecision, checks: [.statusCheckRollup[] | {name, status, conclusion, workflowName}]}' || true

Length of output: 204446


🏁 Script executed:

#!/bin/bash
set -euo pipefail

expected='cde661784345b9489f2a98f4ccc4d261b6c8da3d'
printf '== revision ==\nexpected=%s\nactual=%s\n' "$expected" "$(git rev-parse HEAD)"

printf '\n== direct-parent changed ADR and documentation paths ==\n'
git diff --name-status "${expected}^" "$expected" -- docs/adr docs/traceability docs/uml tests/test_browser_session_lifecycle_contract.py

printf '\n== ADR 0115 and ADR 0116 files ==\n'
fd -t f '0115|0116' docs/adr -x sh -c 'printf "\n== %s ==\n"; cat -n "$1"' sh {}

printf '\n== direct-parent ADR collision diff ==\n'
git diff --unified=30 "${expected}^" "$expected" -- docs/adr docs/traceability/browser-session-lifecycle-authority.md docs/uml/browser-session-lifecycle-authority.md tests/test_browser_session_lifecycle_contract.py

printf '\n== `#317` changed-file references to 0115/0116 ==\n'
git diff --name-only 6d87dff5dc572fbd74d06309d574a998f23cf02f.."$expected" | while IFS= read -r file; do
  case "$file" in
    docs/*|tests/*|crates/originweave-browser-session/*)
      if rg -n -i '(ADR[ -]?0115|ADR[ -]?0116|0115-browser-session|0116-browser-session)' "$file"; then
        printf '%s\n' "-- file: $file"
      fi
      ;;
  esac
done

printf '\n== requested documentation, focused sections ==\n'
sed -n '1,240p' docs/adr/0116-browser-session-recovery-custody-and-hot-ownership.md | nl -ba -v 1
sed -n '1,220p' docs/adr/README.md | nl -ba -v 1
sed -n '1,230p' docs/traceability/browser-session-lifecycle-authority.md | nl -ba -v 1
sed -n '1,300p' docs/uml/browser-session-lifecycle-authority.md | nl -ba -v 1

printf '\n== recovery owner implementation ==\n'
cat -n crates/originweave-browser-session/src/recovery.rs
printf '\n== handoff implementation and Drop ==\n'
sed -n '560,650p' crates/originweave-browser-session/src/browser_session.rs | nl -ba -v 560
sed -n '990,1135p' crates/originweave-browser-session/src/browser_session.rs | nl -ba -v 990
printf '\n== destroy and recovery implementation ==\n'
sed -n '875,1005p' crates/originweave-browser-session/src/browser_session.rs | nl -ba -v 875

printf '\n== recovery ownership hostile tests ==\n'
cat -n crates/originweave-browser-session/tests/recovery_owner_handoff.rs
cat -n crates/originweave-browser-session/tests/proven_destroy_releases_hot_ownership.rs
printf '\n== contract architecture section ==\n'
sed -n '264,430p' tests/test_browser_session_lifecycle_contract.py | nl -ba -v 264

printf '\n== public recovery conversion and raw port escape search ==\n'
rg -n -C 4 --type rust \
  '(pub\s+(?:const\s+)?fn\s+|impl.*(Deref|From|Into)|struct\s+BoundBrowserSessionRecovery|fn\s+(browser_session|lifecycle_port|into_(inner|port|browser_session)|create_disposable_context|destroy_disposable_context|advance_context_epoch|presentation_authority|finish))' \
  crates/originweave-browser-session/src/recovery.rs crates/originweave-browser-session/src/browser_session.rs

Length of output: 191174


@seonghobae Review complete for exact head cde661784345b9489f2a98f4ccc4d261b6c8da3d.

I found no issue in the requested ADR collision repair or Browser Session documentation and contract slice.

  • #317 deletes docs/adr/0115-browser-session-recovery-custody-and-hot-ownership.md.
  • #317 uses docs/adr/0116-browser-session-recovery-custody-and-hot-ownership.md for the recovery-custody and hot-ownership decision.
  • The remaining ADR 0115 reference is in docs/adr/README.md. It correctly states that #316 reserves ADR 0115. It does not assign ADR 0115 to #317.
  • ADR 0116 is Proposed. It explicitly requires protected main integration and independently evidenced real-browser post-conditions before promotion.
  • The traceability document remains IMPLEMENTED_ON_ACTIVE_PR. It explicitly states that protected-main integration is required for capability maturity.
  • The UML states that it describes an active-PR domain contract. It does not claim adapter implementation or protected-main shipment.

The documentation is code-current with the reviewed implementation.

  • BoundBrowserSessionRecovery<P> keeps BoundBrowserSession<P> private.
  • It exposes only state(), recovery_evidence(), and create_attempt_recovery_evidence().
  • It exposes no raw port, inner bound session, inner BrowserSession, conversion, or ordinary lifecycle method.
  • The compile_fail examples cover create, presentation-authority lookup, indirect browser_session() lookup, epoch advance, destroy, and finish().
  • recovery_owner_handoff.rs verifies that RecoveryRequired and TransportLost each transfer the same non-Clone adapter without create, destroy, or implicit cleanup I/O.

The hot-ownership statements also match the source.

  • destroy_disposable_context_with_port validates the authority before adapter I/O.
  • A successful destroy removes the hot ownership record only after the port returns Ok(()).
  • A failed destroy retains the record as Uncertain.
  • A failed destroy retains exact UnprovenDestruction { context, context_epoch } evidence and enters RecoveryRequired.
  • proven_destroy_releases_hot_ownership.rs verifies 258 same-value reuse generations, monotonic epochs, and stale-authority rejection before adapter I/O.
  • ADR 0116 and the traceability document keep WebDriver BiDi tuple state, remote-liveness interpretation, and protocol recovery operations under #316.

git diff --check reports no whitespace errors.

This is a static review only. Rust contracts and Production coverage are queued, and PR #317 remains UNSTABLE.

You are interacting with an AI system.

@seonghobae
seonghobae marked this pull request as draft September 14, 2026 18:16
@seonghobae
seonghobae marked this pull request as ready for review September 14, 2026 19:02

Copy link
Copy Markdown
Contributor Author

@coderabbitai review

Please review current exact head 09f5dde3ebedc0a7abcedac55bc966c64a71f86f, including the lossless whole-file doctoring repairs in docs/product-technical-gap-baseline.md, docs/doctoring.md, and CHANGELOG.md. Verify that recovery custody / bounded hot ownership are no longer described as future work, the immutable WebDriver BiDi 9 September 2026 publication is used consistently without erasing the 3 September predecessor provenance, and no Browser Session / #316 canonical-owner boundary or active-PR-vs-protected-main claim was widened.

@seonghobae
seonghobae marked this pull request as draft September 14, 2026 20:01

Copy link
Copy Markdown
Contributor Author

@coderabbitai review

Exact head 9d48b3e0f81dfadd20140fb5145794a5c2df1292. Focus this pass on crates/originweave-browser-session/src/browser_session.rs and crates/originweave-browser-session/tests/user_context_identity_length.rs.

Fresh source verification found that DisposableIsolationId::parse still rejects empty text, surrounding whitespace, and decoded Unicode control characters even though the current WebDriver BiDi browser.UserContext type is CDDL text. The new hostile RED requires exact round-trip preservation for "", " context ", decoded "ctx\n", and a 4097-byte value. Please verify the finding against the current code and standards semantics; do not infer a narrower Chromium grammar without versioned primary evidence. Treat this as representation/addressability only, not mutation authority. No GREEN claim is made on this head.

@coderabbitai

coderabbitai Bot commented Sep 14, 2026

Copy link
Copy Markdown

Tip

For best results, initiate chat on the files or code changes.

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

expected='9d48b3e0f81dfadd20140fb5145794a5c2df1292'
actual="$(git rev-parse HEAD)"
printf '== revision ==\nexpected=%s\nactual=%s\n' "$expected" "$actual"
git status --short

printf '\n== direct-parent changes ==\n'
git diff --name-status "${expected}^" "$expected"

printf '\n== focused file outlines ==\n'
for file in \
  crates/originweave-browser-session/src/browser_session.rs \
  crates/originweave-browser-session/tests/user_context_identity_length.rs
do
  printf '\n== %s ==\n' "$file"
  ast-grep outline "$file" --items all --type struct,enum,impl,function,method \
    --match 'DisposableIsolationId|parse|identity|context|user' || true
done

printf '\n== parser declaration, error handling, and all call sites ==\n'
rg -n -C 12 --type rust \
  '(struct\s+DisposableIsolationId|enum\s+DisposableIsolationId|impl\s+DisposableIsolationId|fn\s+parse\s*\(|DisposableIsolationId::parse\s*\(|is_empty|trim|control|is_control|chars\(\)|len\(\)|4096)' \
  crates/originweave-browser-session/src crates/originweave-browser-session/tests

printf '\n== focused source and hostile test ==\n'
sed -n '1,260p' crates/originweave-browser-session/src/browser_session.rs | nl -ba -v 1
printf '\n== crates/originweave-browser-session/tests/user_context_identity_length.rs ==\n'
cat -n crates/originweave-browser-session/tests/user_context_identity_length.rs

printf '\n== identity representation contracts and documentation ==\n'
rg -n -C 8 \
  '(DisposableIsolationId|browser\.UserContext|user context|UserContext|4096|whitespace|control character|identity)' \
  docs tests crates/originweave-browser-session

printf '\n== exact identity-related patch ==\n'
git diff --find-renames --unified=60 "${expected}^" "$expected" -- \
  crates/originweave-browser-session/src/browser_session.rs \
  crates/originweave-browser-session/tests/user_context_identity_length.rs \
  tests/test_browser_session_lifecycle_contract.py \
  docs

printf '\n== public type surface and construction sites ==\n'
rg -n -C 6 --type rust \
  '(pub\s+(?:struct|enum)\s+DisposableIsolationId|pub\s+(?:const\s+)?fn\s+(parse|as_str)|DisposableIsolationId\s*\{|DisposableIsolationId\()' \
  crates/originweave-browser-session/src

printf '\n== whitespace integrity ==\n'
git diff --check "${expected}^" "$expected"

Length of output: 202724


🤖 web_search executed:

Official W3C WebDriver BiDi Working Draft 9 September 2026 browser.UserContext CDDL definition: is it defined as text, and does that definition specify non-empty, whitespace, control-character, or maximum-length restrictions?

Length of output: 2337


@seonghobae I verified the finding on exact head 9d48b3e0f81dfadd20140fb5145794a5c2df1292.

P1 — Remove the local DisposableIsolationId grammar.

DisposableIsolationId::parse rejects three values that the new hostile contract requires it to preserve:

  • Line 106 rejects "".
  • Line 109 rejects " context " through trim().
  • Line 109 rejects decoded "ctx\n" through char::is_control.

crates/originweave-browser-session/tests/user_context_identity_length.rs now passes all four values to parse(). The test will fail on the first case, "".

The W3C WebDriver BiDi definition is:

browser.UserContext = text

The inspected specification result defines no non-empty rule, whitespace rule, control-character rule, or maximum-length rule for this type. A local parser must not narrow this protocol representation without versioned primary evidence for that narrower constraint.

Make DisposableIsolationId::parse preserve the input string exactly. Remove DisposableIsolationIdError::Empty and DisposableIsolationIdError::InvalidCharacter if no other valid domain constraint remains. Keep the value opaque. Do not trim, normalize, filter, hash, or apply a local length limit.

This change affects protocol addressability and recovery evidence only. It must not change lifecycle mutation authority. PresentationMutationAuthority, aggregate-issued requests, and ownership validation must remain the authorization boundaries.

The prior 4097-byte case remains necessary, but it is insufficient by itself. Keep all four cases in user_context_identity_length.rs.

The retrieved W3C publication guidance also remains applicable: the dated 9 September 2026 publication is standards provenance, not runtime qualification. The separately qualified runtime revision must not change from this representation repair alone.

This is a static review. I did not run tests or CI.

The earlier learning that requires non-empty, non-control, non-surrounding-whitespace values now conflicts with this review. Please confirm that it is obsolete before I remove it.


✏️ Learnings added
Learnt from: seonghobae
URL: https://github.com/ContextualWisdomLab/OriginWeave/pull/317

Timestamp: 2026-09-14T20:04:25.160Z
Learning: For ContextualWisdomLab/OriginWeave PR `#317`, `DisposableIsolationId::parse` in `crates/originweave-browser-session/src/browser_session.rs` represents WebDriver BiDi `browser.UserContext` addressability and recovery evidence, not mutation authority. Because the protocol type is CDDL `text` with no identified non-empty, whitespace, control-character, or local maximum-length restriction, the parser must preserve the exact input losslessly, including `""`, surrounding whitespace, decoded control characters, and values longer than 4096 bytes. Aggregate-issued requests and `PresentationMutationAuthority` remain the separate authorization boundaries.

Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.


🧠 Learnings used
Learnt from: seonghobae
URL: https://github.com/ContextualWisdomLab/OriginWeave/pull/317

Timestamp: 2026-09-14T14:57:51.198Z
Learning: For ContextualWisdomLab/OriginWeave WebDriver BiDi standards traceability, `https://www.w3.org/TR/2026/WD-webdriver-bidi-20260909/` is the immutable 9 September 2026 W3C Working Draft publication, and `https://www.w3.org/TR/2026/WD-webdriver-bidi-20260903/` is its previous dated publication. Publication freshness must remain separate from OriginWeave runtime qualification; the runtime-compatible adapter pin remains `2026-09-03` until dedicated compatibility and pinned-browser evidence qualifies a newer revision.

Learnt from: seonghobae
Repo: ContextualWisdomLab/OriginWeave PR: 317
File: crates/originweave-browser-session/tests/user_context_identity_length.rs:7-8
Timestamp: 2026-09-14T14:46:25.294Z
Learning: In `crates/originweave-browser-session/src/lib.rs`, `DisposableIsolationId::parse` must not impose an arbitrary 4096-byte limit. WebDriver BiDi `browser.UserContext` is protocol `text` and has no 4096-byte limit, so the parser must preserve non-empty, non-control, non-surrounding-whitespace values losslessly regardless of length.

You are interacting with an AI system.

Copy link
Copy Markdown
Contributor Author

@coderabbitai review

Please review exact head 9a886b4bb8feeddef746b5bad2646f5c414d4189. Focus first on the browser.UserContext representation repair: DisposableIsolationId::parse must preserve WebDriver BiDi protocol text exactly, including empty, surrounding-whitespace, control-containing, and >4096-byte strings, without normalization or a Browser Session-local lexical grammar. Also re-check whether the public read-only Browser Session projection still exposes any caller-mintable presentation capability. Treat Draft-policy skipped Actions as no execution evidence.

@coderabbitai

coderabbitai Bot commented Sep 14, 2026

Copy link
Copy Markdown

Tip

For best results, initiate chat on the files or code changes.

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

expected='9a886b4bb8feeddef746b5bad2646f5c414d4189'
actual="$(git rev-parse HEAD)"
printf '== revision ==\nexpected=%s\nactual=%s\n' "$expected" "$actual"
git status --short

printf '\n== direct-parent changed files ==\n'
git diff --name-status "${expected}^" "$expected"

printf '\n== relevant module and test outlines ==\n'
for file in \
  crates/originweave-browser-session/src/lib.rs \
  crates/originweave-browser-session/src/browser_session.rs \
  crates/originweave-browser-session/src/recovery.rs \
  crates/originweave-browser-session/tests/user_context_identity_length.rs \
  crates/originweave-browser-session/tests/recovery_owner_handoff.rs
do
  if test -f "$file"; then
    printf '\n== %s ==\n' "$file"
    ast-grep outline "$file" --items all --type struct,enum,trait,impl,function,method \
      --match 'DisposableIsolationId|BrowserSession|BoundBrowserSession|BoundBrowserSessionRecovery|PresentationMutationAuthority|parse|presentation_authority|recovery' || true
  fi
done

printf '\n== parser declaration, implementation, and all construction/call sites ==\n'
rg -n -C 12 --type rust \
  '(struct\s+DisposableIsolationId|enum\s+DisposableIsolationIdError|impl\s+DisposableIsolationId|fn\s+parse\s*\(|DisposableIsolationId::parse\s*\(|is_empty|trim|is_control|chars\(\)|len\(\)|4096|as_str\s*\()' \
  crates/originweave-browser-session/src crates/originweave-browser-session/tests tests

printf '\n== public Browser Session projections and capability-minting paths ==\n'
rg -n -C 12 --type rust \
  '(struct\s+(BrowserSession|BoundBrowserSession|BoundBrowserSessionRecovery|PresentationMutationAuthority)|impl.*(BrowserSession|BoundBrowserSession|BoundBrowserSessionRecovery)|pub(\([^)]*\))?\s+(?:const\s+)?fn\s+|fn\s+presentation_authority\s*\(|PresentationMutationAuthority\s*\{|presentation.*authorit|browser_session\(\)|Deref(Mut)?|From<.*BoundBrowserSessionRecovery|Into<.*BoundBrowserSession)' \
  crates/originweave-browser-session/src

printf '\n== focused source sections ==\n'
sed -n '1,280p' crates/originweave-browser-session/src/browser_session.rs | nl -ba -v 1
sed -n '520,1135p' crates/originweave-browser-session/src/browser_session.rs | nl -ba -v 520
if test -f crates/originweave-browser-session/src/recovery.rs; then
  cat -n crates/originweave-browser-session/src/recovery.rs
fi
cat -n crates/originweave-browser-session/src/lib.rs

printf '\n== requested hostile tests and relevant contracts ==\n'
for file in \
  crates/originweave-browser-session/tests/user_context_identity_length.rs \
  crates/originweave-browser-session/tests/recovery_owner_handoff.rs \
  tests/test_browser_session_lifecycle_contract.py
do
  if test -f "$file"; then
    printf '\n== %s ==\n' "$file"
    cat -n "$file"
  fi
done

printf '\n== identity and read-only projection references in documentation/contracts ==\n'
rg -n -C 8 \
  '(DisposableIsolationId|browser\.UserContext|CDDL\s+`?text|4096|whitespace|control.character|lossless|presentation.authority|read-only|recovery custody|BoundBrowserSessionRecovery)' \
  docs tests crates/originweave-browser-session

printf '\n== compile-fail contracts and projection escape checks ==\n'
rg -n -U -C 10 \
  '(compile_fail|BoundBrowserSessionRecovery|browser_session\(\)|presentation_authority|PresentationMutationAuthority|Deref|into_(inner|port|browser_session)|lifecycle_port)' \
  crates/originweave-browser-session/src tests

printf '\n== exact patch and whitespace integrity ==\n'
git diff --find-renames --unified=70 "${expected}^" "$expected" -- \
  crates/originweave-browser-session/src \
  crates/originweave-browser-session/tests/user_context_identity_length.rs \
  tests/test_browser_session_lifecycle_contract.py \
  docs
git diff --check "${expected}^" "$expected"

printf '\n== exact-head GitHub checks ==\n'
gh pr view 317 --repo ContextualWisdomLab/OriginWeave \
  --json headRefOid,mergeStateStatus,reviewDecision,statusCheckRollup \
  --jq '{headRefOid, mergeStateStatus, reviewDecision, checks: [.statusCheckRollup[] | {name, status, conclusion, workflowName}]}' || true

Length of output: 204616


@seonghobae Review complete for exact head 9a886b4bb8feeddef746b5bad2646f5c414d4189.

I used the existing browser.UserContext representation guidance for this review.

The DisposableIsolationId::parse repair is correct.

  • parse now stores value.to_owned() directly.
  • It preserves "".
  • It preserves surrounding whitespace.
  • It preserves decoded control characters.
  • It preserves values longer than 4096 bytes.
  • It does not trim, normalize, filter, or apply a local length limit.
  • DisposableIsolationIdError is retained only as a compatibility type. The parser does not emit it.

I found one authority-surface issue.

P1 — Remove indirect presentation-authority minting from the read-only projection.

BoundBrowserSession::browser_session(&self) -> &BrowserSession remains public at Line 1011 in crates/originweave-browser-session/src/browser_session.rs. BrowserSession::presentation_authority(&self, browsing_context) remains public at Line 682.

Therefore, code that has only a read-only bound-session reference can mint a current PresentationMutationAuthority for a known active context:

let authority = bound
    .browser_session()
    .presentation_authority(browsing_context)?;

The authority has private fields and no public constructor. However, this public lookup still mints it from a raw BrowsingContextId through a read-only projection. This conflicts with the stated capability boundary that raw browser identifiers do not mint presentation authority.

Remove BoundBrowserSession::browser_session() from the public API, or replace it with a narrow immutable projection that does not expose BrowserSession::presentation_authority. Add a compile-fail contract for the indirect path above. Keep recovery custody unchanged. BoundBrowserSessionRecovery<P> already avoids this exposure.

Rust contracts and Production coverage are SKIPPED for this exact head. These Draft-policy skipped Actions are not test or execution evidence.


🧠 Learnings used
Learnt from: seonghobae
URL: https://github.com/ContextualWisdomLab/OriginWeave/pull/317

Timestamp: 2026-09-14T20:04:25.436Z
Learning: For ContextualWisdomLab/OriginWeave PR `#317`, `DisposableIsolationId::parse` in `crates/originweave-browser-session/src/browser_session.rs` represents WebDriver BiDi `browser.UserContext` addressability and recovery evidence, not mutation authority. Because the protocol type is CDDL `text` with no identified non-empty, whitespace, control-character, or local maximum-length restriction, the parser must preserve the exact input losslessly, including `""`, surrounding whitespace, decoded control characters, and values longer than 4096 bytes. Aggregate-issued requests and `PresentationMutationAuthority` remain the separate authorization boundaries.

Learnt from: seonghobae
Repo: ContextualWisdomLab/OriginWeave PR: 317
File: crates/originweave-browser-session/tests/user_context_identity_length.rs:7-8
Timestamp: 2026-09-14T14:46:25.294Z
Learning: In `crates/originweave-browser-session/src/lib.rs`, `DisposableIsolationId::parse` must not impose an arbitrary 4096-byte limit. WebDriver BiDi `browser.UserContext` is protocol `text` and has no 4096-byte limit, so the parser must preserve non-empty, non-control, non-surrounding-whitespace values losslessly regardless of length.

You are interacting with an AI system.

@seonghobae
seonghobae marked this pull request as ready for review September 14, 2026 21:12

Copy link
Copy Markdown
Contributor Author

@coderabbitai review

Please review exact head 12c2d03b875c5a12fd8ba4225dc51c8a064d1d6b. The prior read-model capability finding is repaired by making BrowserSession::presentation_authority module-private while retaining BoundBrowserSession::presentation_authority as the explicit owner surface; crate-level compile_fail now proves bound.browser_session().presentation_authority(raw_context) is unavailable externally. Also verify the exact browser.UserContext protocol-text preservation repair remains intact. Do not infer GREEN from queued/skipped Actions.

@coderabbitai

coderabbitai Bot commented Sep 14, 2026

Copy link
Copy Markdown

Tip

For best results, initiate chat on the files or code changes.

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

expected='12c2d03b875c5a12fd8ba4225dc51c8a064d1d6b'
actual="$(git rev-parse HEAD)"
printf '== revision ==\nexpected=%s\nactual=%s\n' "$expected" "$actual"
git status --short

printf '\n== direct-parent changed files ==\n'
git diff --name-status "${expected}^" "$expected"

printf '\n== focused module outlines ==\n'
for file in \
  crates/originweave-browser-session/src/lib.rs \
  crates/originweave-browser-session/src/browser_session.rs \
  crates/originweave-browser-session/src/recovery.rs \
  crates/originweave-browser-session/tests/user_context_identity_length.rs \
  tests/test_browser_session_lifecycle_contract.py
do
  if test -f "$file"; then
    printf '\n== %s ==\n' "$file"
    ast-grep outline "$file" --items all --type struct,enum,trait,impl,function,method \
      --match 'BrowserSession|BoundBrowserSession|BoundBrowserSessionRecovery|PresentationMutationAuthority|DisposableIsolationId|parse|presentation_authority|browser_session' || true
  fi
done

printf '\n== exact focused patch ==\n'
git diff --find-renames --unified=80 "${expected}^" "$expected" -- \
  crates/originweave-browser-session/src \
  crates/originweave-browser-session/tests/user_context_identity_length.rs \
  tests/test_browser_session_lifecycle_contract.py \
  docs

printf '\n== authority declarations, visibility, construction, and external surface ==\n'
rg -n -C 12 --type rust \
  '(struct\s+(BrowserSession|BoundBrowserSession|BoundBrowserSessionRecovery|PresentationMutationAuthority)|impl.*(BrowserSession|BoundBrowserSession|BoundBrowserSessionRecovery)|pub(\([^)]*\))?\s+(?:const\s+)?fn\s+(browser_session|presentation_authority)|pub\(crate\]\s+(?:const\s+)?fn\s+presentation_authority|fn\s+presentation_authority\s*\(|PresentationMutationAuthority\s*\{|browser_session\(\)|Deref(Mut)?|From<.*BoundBrowserSessionRecovery|Into<.*BoundBrowserSession)' \
  crates/originweave-browser-session/src

printf '\n== authority call sites and compile-fail contracts ==\n'
rg -n -U -C 10 --type rust \
  '(presentation_authority\s*\(|browser_session\(\)|```compile_fail|BoundBrowserSessionRecovery)' \
  crates/originweave-browser-session/src crates/originweave-browser-session/tests

printf '\n== identity parser and call sites ==\n'
rg -n -C 12 --type rust \
  '(struct\s+DisposableIsolationId|enum\s+DisposableIsolationIdError|impl\s+DisposableIsolationId|fn\s+parse\s*\(|DisposableIsolationId::parse\s*\(|is_empty|trim|is_control|chars\(\)|len\(\)|4096|as_str\s*\()' \
  crates/originweave-browser-session/src crates/originweave-browser-session/tests tests

printf '\n== selected full source and contract sections ==\n'
sed -n '1,250p' crates/originweave-browser-session/src/browser_session.rs | nl -ba -v 1
sed -n '600,735p' crates/originweave-browser-session/src/browser_session.rs | nl -ba -v 600
sed -n '990,1080p' crates/originweave-browser-session/src/browser_session.rs | nl -ba -v 990
cat -n crates/originweave-browser-session/tests/user_context_identity_length.rs
rg -n -C 16 '(presentation_authority|browser_session\(\)|DisposableIsolationId|user_context_identity_length|compile_fail)' tests/test_browser_session_lifecycle_contract.py

printf '\n== raw-port and owner escape search ==\n'
rg -n -C 6 --type rust \
  '(lifecycle_port|into_(inner|port|browser_session)|as_mut|Deref(Mut)?|From<.*BoundBrowserSession|Into<.*BoundBrowserSession|->\s*&(?:mut\s+)?P|&mut\s+P)' \
  crates/originweave-browser-session/src

printf '\n== whitespace integrity ==\n'
git diff --check "${expected}^" "$expected"

printf '\n== exact-head GitHub status ==\n'
gh pr view 317 --repo ContextualWisdomLab/OriginWeave \
  --json headRefOid,mergeStateStatus,reviewDecision,statusCheckRollup \
  --jq '{headRefOid, mergeStateStatus, reviewDecision, checks: [.statusCheckRollup[] | {name, status, conclusion, workflowName}]}' || true

Length of output: 204384


🏁 Script executed:

#!/bin/bash
set -euo pipefail

expected='12c2d03b875c5a12fd8ba4225dc51c8a064d1d6b'
printf 'expected=%s\nactual=%s\n' "$expected" "$(git rev-parse HEAD)"

printf '\n== direct parent patch around authority visibility and contracts ==\n'
git diff --unified=45 "${expected}^" "$expected" -- \
  crates/originweave-browser-session/src/browser_session.rs \
  crates/originweave-browser-session/src/lib.rs \
  tests/test_browser_session_lifecycle_contract.py

printf '\n== compile-fail blocks and surrounding docs ==\n'
rg -n -U -C 14 --type rust \
  '```compile_fail[\s\S]{0,1500}(browser_session\(\)|presentation_authority|BoundBrowserSession)' \
  crates/originweave-browser-session/src

printf '\n== repository-contract assertions for the owner/read-model authority surface ==\n'
rg -n -C 20 \
  '(presentation_authority|browser_session\(\)|module-private|pub\(crate\)|compile_fail|BoundBrowserSession)' \
  tests/test_browser_session_lifecycle_contract.py

printf '\n== all public BrowserSession immutable methods and authority methods ==\n'
rg -n -C 3 --type rust \
  '(pub\s+(?:const\s+)?fn\s+(id|incarnation|state|transport_is_lost|recovery_evidence|create_attempt_recovery_evidence|presentation_authority)|^\s*fn\s+presentation_authority)' \
  crates/originweave-browser-session/src/browser_session.rs

printf '\n== external integration uses of BrowserSession::presentation_authority ==\n'
rg -n -C 5 --type rust \
  '\bBrowserSession\b.*presentation_authority|\.presentation_authority\s*\(' \
  crates/originweave-browser-session/tests \
  -g '!crates/originweave-browser-session/src/browser_session.rs'

printf '\n== parser direct patch and test ==\n'
git diff --unified=30 "${expected}^" "$expected" -- \
  crates/originweave-browser-session/src/browser_session.rs \
  crates/originweave-browser-session/tests/user_context_identity_length.rs
sed -n '84,122p' crates/originweave-browser-session/src/browser_session.rs | nl -ba -v 84
cat -n crates/originweave-browser-session/tests/user_context_identity_length.rs

Length of output: 39983


@seonghobae Review complete for exact head 12c2d03b875c5a12fd8ba4225dc51c8a064d1d6b.

The prior P1 is repaired.

  • BrowserSession::presentation_authority is now module-private in crates/originweave-browser-session/src/browser_session.rs.
  • BoundBrowserSession::presentation_authority remains public. It is the explicit owner surface.
  • BoundBrowserSession::browser_session() remains read-only. External callers cannot use it to call the private aggregate authority method.
  • crates/originweave-browser-session/src/lib.rs includes the required crate-level compile_fail contract for:
    bound.browser_session().presentation_authority(context)
  • I found no public conversion, raw-port accessor, or recovery-custody path that restores this authority-minting route.

The browser.UserContext representation repair remains intact.

  • DisposableIsolationId::parse stores value.to_owned() directly.
  • It does not reject empty text, whitespace, control characters, or values longer than 4096 bytes.
  • It does not trim, normalize, filter, or impose a local length limit.
  • user_context_identity_length.rs verifies exact round trips for "", " context ", "ctx\n", and a 4097-byte string.

git diff --check reports no whitespace errors.

This is a static source review. I did not run tests or CI. GitHub shows queued and skipped Actions, and PR #317 remains UNSTABLE. Those checks are not GREEN execution evidence.


🧠 Learnings used
Learnt from: seonghobae
URL: https://github.com/ContextualWisdomLab/OriginWeave/pull/317

Timestamp: 2026-09-14T20:04:25.436Z
Learning: For ContextualWisdomLab/OriginWeave PR `#317`, `DisposableIsolationId::parse` in `crates/originweave-browser-session/src/browser_session.rs` represents WebDriver BiDi `browser.UserContext` addressability and recovery evidence, not mutation authority. Because the protocol type is CDDL `text` with no identified non-empty, whitespace, control-character, or local maximum-length restriction, the parser must preserve the exact input losslessly, including `""`, surrounding whitespace, decoded control characters, and values longer than 4096 bytes. Aggregate-issued requests and `PresentationMutationAuthority` remain the separate authorization boundaries.

Learnt from: seonghobae
Repo: ContextualWisdomLab/OriginWeave PR: 317
File: crates/originweave-browser-session/tests/user_context_identity_length.rs:7-8
Timestamp: 2026-09-14T14:46:25.294Z
Learning: In `crates/originweave-browser-session/src/lib.rs`, `DisposableIsolationId::parse` must not impose an arbitrary 4096-byte limit. WebDriver BiDi `browser.UserContext` is protocol `text` and has no 4096-byte limit, so the parser must preserve non-empty, non-control, non-surrounding-whitespace values losslessly regardless of length.

You are interacting with an AI system.

@seonghobae
seonghobae marked this pull request as draft September 14, 2026 21:49
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working priority: high

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant