Repository navigation
[adr] Behavioral contract for the low-level WebDriver BiDi layer - #17786
Conversation
PR Summary by QodoAdd ADR defining behavioral contract for low-level WebDriver BiDi layer
AI Description
Diagram
High-Level Assessment
Files changed (1)
|
Code Review by Qodo
1.
|
|
Code review by qodo was updated up to the latest commit 98e1858 |
There was a problem hiding this comment.
Pull request overview
Adds a new ADR to the cross-binding decision log that defines a behavioral contract for Selenium’s internal, low-level WebDriver BiDi protocol layer (validation, extensibility, parsing strictness, and surface shape), intended to keep binding implementations aligned on wire-observable behavior.
Changes:
- Introduces a decision record enumerating required outbound/inbound behaviors for the BiDi low-level layer.
- Defines a two-tier taxonomy (Compliance vs Decision) to separate spec-forced requirements from Selenium-standardized choices.
- Records rationale, alternatives, and consequences to support consistent adoption across bindings.
|
Code review by qodo was updated up to the latest commit dfe1c07 |
|
Code review by qodo was updated up to the latest commit 5b77f31 |
|
Code review by qodo was updated up to the latest commit e965829 |
|
Code review by qodo was updated up to the latest commit 1b0e91a |
Code Review by Qodo
1.
|
|
Code review by qodo was updated up to the latest commit 541031b |
|
Code review by qodo was updated up to the latest commit deb870f |
📄 The decision, rationale, considered options, and consequences are in the record file this PR adds;
read it there. Below are proposal notes and review logistics.
🔗 Related
since it isn't yet being used (the code currently in use is referred to as "Python now"). Java's adoption
of this contract is in progress in [java][bidi] Add BiDi code generator #17777.
📝 Proposal notes
shared schema makes some behavior cheap to derive, but the contract is still the behavior every binding
exhibits, regardless of how it is implemented.
messages back into typed objects. Transport behavior is out of scope and should be decided separately if
needed.
ADR, showing where each implementation currently stands. The summaries after the table
differentiate the reasons for divergences (marked for replacement, minimizing maintenance costs, or
intentional design choices).
most of the cost versus handwriting: closed-type rejection (6a), null and type checks (7a/7b), typed value
objects (1). The exception is per-type extensibility in static languages: carrying and retaining undeclared
fields on extensible types (6b/9b) stays costly even generated, because preserving and re-serializing them
requires a separate deserialization path (though the webdriverbidi-net project shows it is possible even
when handwritten, despite the cost). Two valid alternatives to the ADR positions are:
implementations).
tolerate what can be represented without inventing data; reject what would produce a false typed
object (a corrupt value or an unknown closed-union variant, 7a/7b/7c). Of the two ways a browser drifts
from the binding's schema, the ADR tolerates one and rejects the other: extra fields a newer browser adds
are tolerated (9a/9b, forward compatibility), while a required field an older browser omits is rejected
(8a) rather than fabricated or tracked as absent, since no static binding can hold an omitted required
field consistently. A project schema override can relax a specific field when a remote end lags, degrading
to a missing field rather than a failed session. The remaining way to tighten drift handling is:
(the layer breaks against a newer browser).
📊 Implementation comparison
How each implementation (current, proposed, and external) stands on selected comparison points against the ADR
The record states ten decisions across three sections (Representation, Outbound, Inbound). The table groups
its comparison points the same way: a row's number is the decision it tests, with letters marking multiple
points under one decision.
Per-implementation through-lines
time, while inbound is whatever falls out of casting the payload onto the object, with no runtime checks,
so 7a/7b/7c and 8a are all ❌ (nothing rejects a malformed or missing value).
closed types reject extras, extensible types carry vendor extras, and both malformed and missing inbound
values error. It is now fully aligned with the inbound decisions; the manual, temporary relaxations it
ships when a browser lags a required field are exactly the project schema overrides this ADR sanctions.
bag on the top-level payload (command params, result, event args), not on the nested types, so a per-type
extra has nowhere to attach, and missing fields get defaults instead of being tracked as absent. Those
choices avoid expensive per-type work, but they are lossy, which is why it diverges on things
that are costly by hand and cheap to generate.
the inbound scalar-arm check in [rb] reject an inbound BiDi scalar outside its union's declared arms #17947).
this contract and diverges on most inbound checks, and is superseded by Python next.
point; [py] update new BiDi layer generation to conform to latest proposed ADR #17942 replaced the tolerated-absence path with rejecting a missing required field.
their declared type (7b — a string or fractional number, or an off-case enum, is coerced instead of
rejected), per-type extras aren't carried or retained (6b/9b), nested types don't warn on an undeclared
field (9c), and scalar-arm unions aren't generated from the schema's
scalarValuessignal (input.Origin).Notes
① Cheaper for static implementations: the type system gives it with little extra code, even
un-generated.
② Cheaper for dynamic implementations: a plain runtime value does the job, with no static wrapper or
typed map.
③ Narrows a small, bounded
js-uintfield (a status code, a line or column number) to a 32-bitint; thegenuinely wide fields (byte sizes, counts) stay
long, so the miss is limited to these small slots.④ Selenium's shared
NumberCoerceris too lenient to enforce this (it truncates fractional integers andaccepts float- or string-encoded numbers); the BiDi layer likely needs a stricter, BiDi-scoped coercer.
⑤ Performs no runtime union dispatch, so an undeclared closed-union variant is not rejected.
⑥ Fills a missing required field with a default (
0/null/false) a caller can't distinguish from a realvalue, so downstream code acts on fabricated data with no signal the field never arrived.
⑦ Compliant, but does not log the warning the ADR requires here.
🗣 Discussion
Discussed at the TLC meetings below; see the minutes for the full discussion and attribution.
📌 Tracking
Tracking issue: (linked on acceptance)