docs(arch): unweld CallClient from QUIC — spawn_dispatch primary, connect convenience (ADR-017 Am. 2026-07-13)

Same fix as ADR-080 (ChannelClient) applied to the call crate. The
existing code already had the right structure — spawn_dispatch is not
feature-gated (transport-agnostic), connect is #[cfg(feature = "quinn")]
— but the docs inverted the framing: connect was 'primary,' spawn_dispatch
was 'lower-level.' That welding masked the call protocol's
transport-agnosticism (ADR-012 EventEnvelope; ADR-065 from_stream/from_bidi
accept any AsyncRead + AsyncWrite).

Changes:
- client-and-adapters.md: reframe spawn_dispatch as the transport-
  agnostic primary constructor (one-way door), connect as the QUIC
  convenience (two-way door, feature-gated on quinn). Mirror the
  from_connection / connect_quic pattern from ADR-080/channel-client.md.
  Fix all 'QUIC-backed' / 'over QUIC' / 'opens a QUIC connection' framing
  to be transport-agnostic. Fix from_call description, adapter location
  map, exchange-of-operations example, Constraints section.
- call-protocol.md: 'runs over QUIC bidirectional streams' → 'runs over
  any ordered, reliable bidirectional stream.' Fix stream model, stream
  lifecycle (connection drop / stream reset now list all transports,
  not just QUIC). Fix CallConnection.connection doc comment.
- operation-registry.md: FromCall provenance comment 'QUIC forwarding
  stub' → 'call-protocol forwarding stub.' Fix from_call description.
- call README.md: fix client-and-adapters.md description, fix design
  principle #11 framing.
- ADR-017: add Amendment (2026-07-13) documenting the spawn_dispatch /
  connect reframe, mirroring ADR-080's amendment.
- overview.md, architecture README: update ADR-017 and
  client-and-adapters.md table summaries.
- OQ-015, OQ-007: fix 'opens QUIC connections' / 'bidirectional QUIC
  streams' framing in resolution text.

No code changes — spawn_dispatch and connect already exist with the
right feature-gate structure. This is a documentation reframe.
This commit is contained in:
glm-5.2 committed 2026-07-13 09:53:43 +00:00
1 parent a26401aadd
commit b0cc0a01b7
9 files changed
+135 -46

No files matched your search

@@ -2,7 +2,7 @@
## Status
Accepted (amended 2026-06-26 — see "Amendments" below)
Accepted (amended 2026-06-26 and 2026-07-13 — see "Amendments" below)
## Context
@@ -439,4 +439,46 @@ served by consuming `OperationSpec` directly. The §5 "adapters live in
alknet-call" one-way-door statement is corrected above to "the adapter
trait lives in `alknet-call`; implementations live with their transport."
See [ADR-066](066-from-jsonschema-as-http-adapter.md) and
[client-and-adapters.md](../crates/call/client-and-adapters.md) §"from_jsonschema".
[client-and-adapters.md](../crates/call/client-and-adapters.md) §"from_jsonschema".
## Amendments (2026-07-13)
### `CallClient` transport-agnostic API (mirrors ADR-080's amendment)
The §1 Decision framed `CallClient::connect(addr: SocketAddr,
credentials)` as the primary constructor and described it as "opens a
QUIC connection." The operational spec
([client-and-adapters.md](../crates/call/client-and-adapters.md))
framed `spawn_dispatch(connection)` as the "lower-level API" that
`connect()` uses after the QUIC dial. That framing welded the
client-side one-way-door API to QUIC — the same welding ADR-065
unwound on the server side and ADR-080 corrected for `ChannelClient`.
The call protocol is transport-agnostic (ADR-012 EventEnvelope framing;
ADR-065 `Connection::from_stream`/`from_bidi` accept any
`AsyncRead + AsyncWrite`). The client side is half of that protocol
and must not be coupled to a transport. This amendment reframes the
existing code (which already has the right structure —
`spawn_dispatch` is not feature-gated, `connect` is
`#[cfg(feature = "quinn")]`):
- **`CallClient::spawn_dispatch(connection: Connection)`** — the
transport-agnostic primary constructor and the one-way-door API.
Takes a pre-established `Connection` (any transport), spawns the
shared dispatch loop, returns a live `CallConnection`. Mirrors the
server-side `CallAdapter::handle(Connection)` and
`ChannelClient::from_connection` (ADR-080).
- **`CallClient::connect(addr, credentials)`** — a QUIC convenience
constructor: dial QUIC (feature-gated on `quinn`), then
`spawn_dispatch`. Additive and two-way-door. `connect_tcp_tls`,
`connect_webtransport`, etc. join it as transports are added,
without touching the `spawn_dispatch` contract.
The door-type classification is unchanged: `spawn_dispatch` is one-way
(the handler-facing surface), `connect` is two-way (additive
convenience). The `AlknetClient` extraction (OQ-55 — the shared
dial+TLS seam) remains deferred; what is deferred is the shared *dial*,
not a QUIC-welded client API. `spawn_dispatch` is decided now.
See [client-and-adapters.md](../crates/call/client-and-adapters.md)
§"CallClient" for the reframed operational spec.