From 92881551f1926a03033cb432f63f0467a7357ddd Mon Sep 17 00:00:00 2001 From: Titus Fortner Date: Wed, 10 Jun 2026 13:49:49 -0500 Subject: [PATCH 1/4] [docs] add design decision record process and template --- docs/decisions/0000-template.md | 56 ++++++++++++++++++++++++++ docs/decisions/README.md | 71 +++++++++++++++++++++++++++++++++ 2 files changed, 127 insertions(+) create mode 100644 docs/decisions/0000-template.md create mode 100644 docs/decisions/README.md diff --git a/docs/decisions/0000-template.md b/docs/decisions/0000-template.md new file mode 100644 index 0000000000000..a3377850d5517 --- /dev/null +++ b/docs/decisions/0000-template.md @@ -0,0 +1,56 @@ +# NNNN. Title stating the decision as a fact + + + +- Status: Proposed +- Date: YYYY-MM-DD +- Discussion: + +## Context + +What problem are we solving, and what forces are in tension? W3C spec language, user +expectations, existing behavior that differs between bindings, implementation constraints. +Link prior discussions (issues, TLC notes) here as background — but summarize them, since +this section must make sense without following any links. + +## Decision + +The decision, stated in language-neutral terms. This is the normative part: what every +binding MUST do, and what is explicitly left to per-language idiom. Use code sketches per +language here if the API shape is part of what's being decided. + +## Considered options + +- **Option A** — what it is; why it was rejected or accepted +- **Option B** — what it is; why it was rejected or accepted + + + +## Consequences + +What gets easier, what gets harder, what users will notice. Deprecations triggered by this +decision and their timelines. Follow-up decisions this one makes necessary. + +## Binding status + + + +| Binding | Status | Notes / tracking link | +|------------|---------|-----------------------| +| Java | pending | | +| Python | pending | | +| Ruby | pending | | +| .NET | pending | | +| JavaScript | pending | | + +## Appendix (optional) + +Durable supporting material the decision relies on: benchmarks, spec excerpts, survey of +behavior in other tools. Delete this section if there is none — ephemeral evidence belongs +in the PR thread instead. diff --git a/docs/decisions/README.md b/docs/decisions/README.md new file mode 100644 index 0000000000000..c706533c91fea --- /dev/null +++ b/docs/decisions/README.md @@ -0,0 +1,71 @@ +# Design Decisions + +This directory is the log of design decisions that apply across the Selenium project — +one file per decision, numbered in the order they were proposed. + +Selenium ships the same API in multiple languages. Decisions about user-visible behavior, +API shape, and cross-binding semantics need to be made once, recorded, and implemented +consistently everywhere. This log is the canonical record of those decisions: when a +question comes up in review, the answer should be a link to a file here. + +## What needs a decision record + +- User-visible behavior that should be consistent across bindings: API naming and shape, + error types and messages, default timeouts, capability handling +- WebDriver Classic / BiDi semantics and how the protocol is exposed (or deliberately not exposed) +- Deprecation and backwards-compatibility commitments +- Anything the TLC has labeled [`A-needs-decision`](https://github.com/SeleniumHQ/selenium/labels/A-needs-decision) + and resolved + +## What doesn't + +- Single-binding internals (a Java maintainer picking a data structure) +- Build tooling and infrastructure choices +- Anything cheaply reversible + +When in doubt, ask whether the question is likely to be raised again. If it is, record the decision. + +## Process + +1. **Propose.** Anyone may propose: copy [0000-template.md](0000-template.md) to `NNNN-short-title.md` using the + next unused number, fill it in, and open a PR with `Status: Proposed`. Keep it to about a + page — if the debate already happened in an issue, the record can be short and link to it. +2. **Discuss.** The PR thread is the discussion record. Decisions that need synchronous + discussion are raised at a TLC meeting; the outcome goes back into the PR. Disagreement + about the considered options is resolved by revising the document during review, so the + merged record reflects the debate accurately. The TLC sets its meeting agenda; proposals + advance as agenda time allows. +3. **Decide.** The Selenium Project Lead merges the record once the approval requirements + below are met and discussion has run its course, with the status updated to `Accepted` — + merging constitutes acceptance. Proposals the TLC considers and declines are merged as + `Rejected`; proposals withdrawn or abandoned before TLC consideration are closed and the + number lapses. +4. **Implement.** Each binding tracks its convergence in the decision's binding-status table. + Updating that table (and only that table) doesn't require TLC review. + +## Approval + +- TLC members respond to a proposal with a GitHub review: an approval, a "no objection" + comment review (saw it, deferring to the others), or a request-changes review stating + what would resolve it. +- Records are accepted by consensus: a majority of TLC members have responded, none with + an unresolved objection. Before acceptance, a record must have been open at least one + week and an agenda item at a TLC meeting — no one should learn of a decision after it + is made. +- If substantive edits are made, the author re-requests reviews. +- An objection that revision cannot resolve — including support for a different considered + option — is discussed at a TLC meeting. If consensus still fails, the Selenium Project + Lead decides which position prevails; the record is updated to match, and overruled + dissent is summarized rather than erased. + +## Rules + +- **A decision must stand alone.** A reader gets the decision, the rationale, and the rejected + alternatives without following any links; linked material is background, not required reading. +- **Accepted decisions are immutable**, except for the status line and the binding-status + table. Changing a decision means a new record that supersedes the old one — update the old + record's status to `Superseded by [NNNN](...)`. +- **Numbers are stable once merged.** They get cited in reviews and issues. If two open PRs + claim the same number, the later one renames before merge. Gaps in numbering are acceptable. +- **Durable supporting material goes in the record itself** (an Appendix section at the end). + Ephemeral evidence and debate stay in the PR thread. From f32ab0fc33e5a50f6abaa84f7a79e313e5512375 Mon Sep 17 00:00:00 2001 From: Titus Fortner Date: Thu, 11 Jun 2026 08:25:06 -0500 Subject: [PATCH 2/4] [adr] BiDi is an implementation mechanism, not a public API --- docs/decisions/.index.md | 3 ++ ...001-bidi-is-an-implementation-mechanism.md | 49 +++++++++++++++++++ 2 files changed, 52 insertions(+) create mode 100644 docs/decisions/.index.md create mode 100644 docs/decisions/0001-bidi-is-an-implementation-mechanism.md diff --git a/docs/decisions/.index.md b/docs/decisions/.index.md new file mode 100644 index 0000000000000..ae755876f9421 --- /dev/null +++ b/docs/decisions/.index.md @@ -0,0 +1,3 @@ +# Index of Decisions + +* 0001 - BiDi is an implementation mechanism, not a public API diff --git a/docs/decisions/0001-bidi-is-an-implementation-mechanism.md b/docs/decisions/0001-bidi-is-an-implementation-mechanism.md new file mode 100644 index 0000000000000..3e53209f41e27 --- /dev/null +++ b/docs/decisions/0001-bidi-is-an-implementation-mechanism.md @@ -0,0 +1,49 @@ +# 0001. BiDi is an implementation mechanism, not a public API + +- Status: Proposed +- Date: 2026-06-11 +- Discussion: _PR pending_, https://github.com/SeleniumHQ/selenium/issues/17628 + +## Context + +Bindings use WebDriver BiDi to implement and extend the Selenium API. This record +settles BiDi's relationship to the public API: what users program against, and where +protocol code lives. CDP was managed differently across the bindings, but all of them +resulted in users needing to understand CDP implementation details to work with it +to one extent or another. This is happening in BiDi the same way. + +## Decision + +BiDi is an implementation mechanism, not a public API. New capabilities are exposed as +high-level, protocol-neutral APIs, idiomatic per language, subject to our standard +deprecation policy. Everything in a BiDi namespace is internal: still reachable for +anything not yet wrapped, but not documented and subject to change without warning. + +A binding conforms when: + +1. The supported API never references BiDi — no types, methods, properties, or entry points. +2. BiDi implementation code (including everything generated from CDDL) is visibly internal — marked per language convention as not intended for public consumption. + +## Considered options + +1. Expose the whole protocol as a public API (Rejected) + * not user-friendly syntax +2. A supported-but-separate public protocol namespace (Rejected) + * multiple implementations are confusing +3. Internal implementation mechanism only (Accepted) + +## Consequences + +- Users never need to know which protocol services a command. +- Each new capability requires API design work across bindings before it ships. +- Existing public surfaces that fail the conformance tests are brought in line per the deprecation policy, tracked below. + +## Binding status + +| Binding | Status | Notes / tracking link | +|------------|---------|-----------------------| +| Java | pending | | +| Python | pending | | +| Ruby | pending | | +| .NET | pending | | +| JavaScript | pending | | From 3d214954b89b7d6cf193536f6b190f25fa25a26e Mon Sep 17 00:00:00 2001 From: Titus Fortner Date: Thu, 11 Jun 2026 10:47:21 -0500 Subject: [PATCH 3/4] add examples with details as requested --- .../0001-bidi-is-an-implementation-mechanism.md | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/docs/decisions/0001-bidi-is-an-implementation-mechanism.md b/docs/decisions/0001-bidi-is-an-implementation-mechanism.md index 3e53209f41e27..7e4525897acf7 100644 --- a/docs/decisions/0001-bidi-is-an-implementation-mechanism.md +++ b/docs/decisions/0001-bidi-is-an-implementation-mechanism.md @@ -24,13 +24,20 @@ A binding conforms when: 1. The supported API never references BiDi — no types, methods, properties, or entry points. 2. BiDi implementation code (including everything generated from CDDL) is visibly internal — marked per language convention as not intended for public consumption. +This includes removing/renaming: +* `HasBiDi` interface +* `bidi_connection`, `bidi`, `getBidi`, `AsBiDiAsync` methods +* `enable_bidi` methods in options + ## Considered options 1. Expose the whole protocol as a public API (Rejected) * not user-friendly syntax 2. A supported-but-separate public protocol namespace (Rejected) * multiple implementations are confusing -3. Internal implementation mechanism only (Accepted) +3. Allow methods and classes to reference BiDi + * users shouldn't need to know the underlying implementation mechanism, things should just work without additional ceremony +4. Internal implementation mechanism only (Accepted) ## Consequences From cdc95ab4ee4041c26a8ad05894e706d5cb895485 Mon Sep 17 00:00:00 2001 From: Titus Fortner Date: Tue, 23 Jun 2026 10:18:36 -0500 Subject: [PATCH 4/4] [adr] rework BiDi record: number by PR, drop index, reframe table and examples --- docs/decisions/.index.md | 3 - ...001-bidi-is-an-implementation-mechanism.md | 56 ------------ ...670-bidi-is-an-implementation-mechanism.md | 89 +++++++++++++++++++ 3 files changed, 89 insertions(+), 59 deletions(-) delete mode 100644 docs/decisions/.index.md delete mode 100644 docs/decisions/0001-bidi-is-an-implementation-mechanism.md create mode 100644 docs/decisions/17670-bidi-is-an-implementation-mechanism.md diff --git a/docs/decisions/.index.md b/docs/decisions/.index.md deleted file mode 100644 index ae755876f9421..0000000000000 --- a/docs/decisions/.index.md +++ /dev/null @@ -1,3 +0,0 @@ -# Index of Decisions - -* 0001 - BiDi is an implementation mechanism, not a public API diff --git a/docs/decisions/0001-bidi-is-an-implementation-mechanism.md b/docs/decisions/0001-bidi-is-an-implementation-mechanism.md deleted file mode 100644 index 7e4525897acf7..0000000000000 --- a/docs/decisions/0001-bidi-is-an-implementation-mechanism.md +++ /dev/null @@ -1,56 +0,0 @@ -# 0001. BiDi is an implementation mechanism, not a public API - -- Status: Proposed -- Date: 2026-06-11 -- Discussion: _PR pending_, https://github.com/SeleniumHQ/selenium/issues/17628 - -## Context - -Bindings use WebDriver BiDi to implement and extend the Selenium API. This record -settles BiDi's relationship to the public API: what users program against, and where -protocol code lives. CDP was managed differently across the bindings, but all of them -resulted in users needing to understand CDP implementation details to work with it -to one extent or another. This is happening in BiDi the same way. - -## Decision - -BiDi is an implementation mechanism, not a public API. New capabilities are exposed as -high-level, protocol-neutral APIs, idiomatic per language, subject to our standard -deprecation policy. Everything in a BiDi namespace is internal: still reachable for -anything not yet wrapped, but not documented and subject to change without warning. - -A binding conforms when: - -1. The supported API never references BiDi — no types, methods, properties, or entry points. -2. BiDi implementation code (including everything generated from CDDL) is visibly internal — marked per language convention as not intended for public consumption. - -This includes removing/renaming: -* `HasBiDi` interface -* `bidi_connection`, `bidi`, `getBidi`, `AsBiDiAsync` methods -* `enable_bidi` methods in options - -## Considered options - -1. Expose the whole protocol as a public API (Rejected) - * not user-friendly syntax -2. A supported-but-separate public protocol namespace (Rejected) - * multiple implementations are confusing -3. Allow methods and classes to reference BiDi - * users shouldn't need to know the underlying implementation mechanism, things should just work without additional ceremony -4. Internal implementation mechanism only (Accepted) - -## Consequences - -- Users never need to know which protocol services a command. -- Each new capability requires API design work across bindings before it ships. -- Existing public surfaces that fail the conformance tests are brought in line per the deprecation policy, tracked below. - -## Binding status - -| Binding | Status | Notes / tracking link | -|------------|---------|-----------------------| -| Java | pending | | -| Python | pending | | -| Ruby | pending | | -| .NET | pending | | -| JavaScript | pending | | diff --git a/docs/decisions/17670-bidi-is-an-implementation-mechanism.md b/docs/decisions/17670-bidi-is-an-implementation-mechanism.md new file mode 100644 index 0000000000000..637b5b6d2187f --- /dev/null +++ b/docs/decisions/17670-bidi-is-an-implementation-mechanism.md @@ -0,0 +1,89 @@ +# 17670. BiDi is an implementation mechanism, not a public API + +- Status: Proposed +- Discussion: https://github.com/SeleniumHQ/selenium/pull/17670 + +## Context + +Bindings use WebDriver BiDi to implement and extend the Selenium API. This record +settles BiDi's relationship to the public API: what users program against, and where +protocol code lives. CDP was managed differently across the bindings, but all of them +resulted in users needing to understand CDP implementation details to work with it +to one extent or another. This is happening in BiDi the same way. + +No binding conforms yet: each exposes BiDi implementation details through the driver, +and none treats its BiDi namespace as internal. + +| Binding | Current behavior | +|------------|------------------| +| Java | Driver leaks the raw BiDi connection (`HasBiDi.getBiDi()`) — internal module plumbing surfaced on the driver; protocol types are public | +| Python | `driver.network` / `driver.script` are the right high-level names, but return the low-level BiDi modules (`bidi`-namespaced) instead of neutral wrappers, exposing the low-level API | +| Ruby | Driver exposes a BiDi accessor (`driver.bidi`) | +| .NET | Driver extension returns a BiDi type (`AsBiDiAsync()` → `IBiDi`) | +| JavaScript | Driver exposes a BiDi accessor (`driver.getBidi()`) | + +## Decision + +BiDi is an implementation mechanism, not a public API. New capabilities are exposed as +high-level, protocol-neutral APIs, idiomatic per language, subject to our standard +deprecation policy. Every binding must: + +1. never reference BiDi in its supported API — no BiDi types in signatures, and no driver method or property that exposes BiDi or hands back a BiDi object; and +2. keep BiDi implementation code (including everything generated from CDDL) visibly internal — marked per language convention as not intended for public consumption. + +This relocates low-level access, it does not remove it: the BiDi namespace stays reachable +by naming it directly (`BiDi::Network.new(driver)`) — technically reachable, but unsupported, +undocumented, and subject to change without warning — and what is removed is the driver as a +path into BiDi. Each binding in the table above shows that one fault — the driver offering a +way in. `HasBiDi.getBiDi()` is the starkest: not access anyone uses, just internal plumbing +that surfaced on the driver. + +Marking a surface *Beta* (or any "stabilizing toward public" status) does not satisfy +requirement 2: Beta signals an API on its way to becoming supported, which is the opposite +of internal. + +Illustratively (Ruby): + +Supported, high-level — `driver.network` is the protocol-neutral API; it returns a neutral +wrapper, not a BiDi type, and offers no path down to the protocol: + +```ruby +driver.network.add_request_handler(...) +``` + +Internal, low-level — reached only by naming the BiDi namespace explicitly; accessible but internal/unsupported: + +```ruby +BiDi::Network.new(driver).add_intercept(phases: [:before_request_sent], url_patterns: [...]) +``` + +Not allowed — the supported API providing a path to the low-level protocol, whether through a +dedicated accessor or through the high-level object itself: + +```ruby +driver.bidi.network.add_intercept(...) # a BiDi accessor on the driver +driver.network.add_intercept(...) # driver.network leaking the low-level call +``` + +## Considered options + +1. Expose the whole protocol as a public API (Rejected) + * the protocol is generated from a living CDDL spec and regenerated as the spec changes, so we cannot manage deprecations on it — it can't be committed to as supported, public API + * exposing the raw protocol is not user-friendly syntax anyway +2. A supported, separate public API for conveniently working with the low-level BiDi implementation — a "mid-level" tier beside the high-level API (Rejected) + * protocol coupling — users build on BiDi-shaped concepts, the same trap CDP created + * mixing a protocol-agnostic high-level API with a BiDi-specific mid-level one leaves users unsure which to reach for + * it raises a large set of unanswered questions about which level owns which orchestration functionality +3. Allow methods and classes to reference BiDi (Rejected) + * users shouldn't need to know the underlying implementation mechanism; things should just work without additional ceremony +4. Internal implementation mechanism only (Accepted) + +## Consequences + +- Users never need to know which protocol services a command. +- Each new capability requires API design work across bindings before it ships. +- Existing public surfaces that fail the conformance tests are brought in line per the + deprecation policy, tracked in the ADR implementation-tracking issue linked from this + record's PR. +- How the internal BiDi layer is produced and structured — generated definitions, their + separation from orchestration — is decided separately.