Address the root cause of rework-causing hedging: the architect was
put in a logical bind where it couldn't express justified uncertainty
('the pieces exist but the shape isn't clear yet'). The only options
were 'decide now' (premature) or 'deferred(scope)' (false — the
information isn't missing, it's un-synthesized). The agent picked
deferred(scope) with a circular blocking condition (OQ-64 blocked on
OQ-55, OQ-55 needs OQ-64) because there was no honest way to say 'I
can see the pieces but I can't see the shape.'
Changes to the architect role spec:
- Add deferred(unclear) state: the pieces exist but the composition
isn't clear; resolution requires investigation (work through
examples, POC), not waiting. Has an investigation target and an
impacts field.
- Add 'Impacts' field to the OQ format: what does this block
downstream? Be specific ('blocks the first hub deployment because
the hub dials workers' not 'blocks the hub crate'). The triage
signal that makes deferral urgency visible — the field that would
have made the AlknetClient circular hedge visible.
- Add circular-reasoning guard to self-review: 'check that your
blocking condition isn't a prerequisite of the thing you're
deferring.'
- Trim anti-patterns #9-#11 (hedging synonyms catalog, ~40 lines):
detection belongs in the reviewer, not the architect's self-review.
The architect is too close to its own reasoning to see its own
circular hedges.
- Trim door-types section (30→10 lines): keep the one-paragraph
summary, cut the elaboration.
Changes to the architecture-reviewer role spec:
- Add Decision Quality (F) category: false-deferral check
distinguishing three cases — (1) hedging on a resolved decision, (2)
false deferral / circular hedge (the blocking condition is a
prerequisite of the thing being deferred), (3) legitimate deferral.
- Add Impacts Field Coverage (G) category: check that unresolved OQs
have specific impacts fields.
- Note: the Decision Quality category is often the highest-value
check on poorly-defined projects — the architect cannot self-review
it (circular reasoning is invisible from inside the circle).
Retrofit existing OQs:
- Add Impacts field to all 16 unresolved OQs (10 deferred, 6 open).
- Update OQ-63 (TlsError shape) to reflect ADR-087's client-side
addition — the error type now covers both server and client
variants.
- Move OQ-65 (WebSocket carrying channels) to alknet-http theme
(done in prior commit; this commit adds its impacts field).
- Verified: no circular reasoning found in existing deferrals. The
AlknetClient hedge (OQ-64) was the circular one; it's already
resolved by ADR-087.
87 lines
5.3 KiB
Markdown
87 lines
5.3 KiB
Markdown
# OQ-55: AlknetClient / Client Establishment Extraction
|
|
|
|
- **Origin**: `docs/research/alknet-channels/phase-0-findings.md` OQ-CH-14
|
|
(the `AlknetClient` clarification); `docs/research/alknet-channels/poc-summary.md`
|
|
§"Issues Surfaced" #1 (the `BidiStreamSource` finding that motivates
|
|
separating the client-extraction question from the `Connection` extension
|
|
question).
|
|
- **Status**: deferred(scope)
|
|
- **Door type**: two-way
|
|
- **Priority**: medium
|
|
- **Impacts**: Does NOT block individual transport dials — each
|
|
transport-specific dial helper builds its `TlsClientConfig` (ADR-087)
|
|
and its own connector standalone. Blocks only the *shared*
|
|
`AlknetClient::dial()` extraction (one entry point that picks the
|
|
transport and calls the right connector). Low impact until a second
|
|
transport's dial exists and the duplicated dial boilerplate becomes
|
|
worth extracting.
|
|
- **Blocked on**: a **second transport's** real dial existing, not just a
|
|
second QUIC dial. The blocking condition is met when, e.g., the SSH
|
|
crate's raw-TCP dial or the HTTP-wrapped call dial exists — so the
|
|
transport-polymorphic dial+TLS seam is extractable from two
|
|
*different* transport implementations, not two QUIC variants.
|
|
`ChannelClient`'s `connect_quic` does not unblock this; it is a second
|
|
*client* but the same *transport shape*. `ChannelClient`'s
|
|
`from_connection` (the transport-agnostic take-over, ADR-080) is decided
|
|
and is not the thing being deferred — the shared *dial* is. The deferral
|
|
is on transport-polymorphism of the dial, not on client count or on the
|
|
channels protocol's API.
|
|
- **What is NOT deferred (amended by ADR-087)**: the client-side TLS
|
|
config (`TlsClientConfig`) is **not** part of this deferral. OQ-64
|
|
is resolved — `alknet-tls` provides `TlsClientConfig` now, unblocked
|
|
by this OQ. The TLS config is a **prerequisite** for the dial, not a
|
|
consequence of it; it is transport-agnostic (ADR-034 verifier
|
|
selection + ADR-084 provider, both already decided). Each
|
|
transport-specific dial helper builds its `TlsClientConfig` and
|
|
passes it to its transport's connector. This OQ defers only the
|
|
*transport-polymorphic dial extraction* — the shared `AlknetClient::dial()`
|
|
that picks the transport and calls the right connector. When a second
|
|
transport's dial exists, the dial seam is extractable; the TLS config
|
|
is already shared by then.
|
|
- **Resolution**: Not yet decidable. The shared substance across
|
|
TLS-carrying transports is ADR-034's verifier-selection rule (PeerEntry
|
|
presence → fingerprint pin : CA-verify / fail-closed) and the
|
|
`rustls::ClientConfig` construction — now centralized in
|
|
`TlsClientConfig::new` (ADR-087, OQ-64 resolved). What remains
|
|
transport-specific is the *dial* itself — `quinn::Endpoint::connect`,
|
|
`TcpStream::connect` + `TlsConnector::connect`, iroh's
|
|
`Endpoint::connect`. We have one of ~5 shapes implemented (QUIC; the
|
|
others being HTTP, TCP+TLS, WebTransport, raw TCP). Extracting a
|
|
QUIC-shaped connector to core and naming it `AlknetClient` would bake
|
|
QUIC in as *the* establishment shape — the same welding ADR-065
|
|
unwound on the server side, repeated on the client side. The dial is
|
|
transport-polymorphic; the shared TLS config is narrow and now
|
|
extracted (ADR-087). Until a second transport's dial exists, the seam
|
|
between "dial" (per-transport) and "spawn the dispatcher" (per-crate)
|
|
is not extractable from two real shapes — it's guessable from one.
|
|
- **What does NOT block on this**: each crate building its own client
|
|
standalone with a shared `TlsClientConfig` (ADR-087), and each
|
|
transport-specific dial helper. `CallClient`'s transport-agnostic
|
|
take-over (`spawn_dispatch`) and `ChannelClient`'s transport-agnostic
|
|
take-over (`from_connection`, ADR-080) are decided; `connect_quic`
|
|
dials QUIC and calls `from_connection`. The SSH crate's TCP client,
|
|
the HTTP call client, a `connect_tcp_tls` / `connect_webtransport`
|
|
helper — each builds its `TlsClientConfig` (ADR-087) and its own dial
|
|
standalone. Core already permits all of this —
|
|
`Connection::from_stream` / `from_bidi` (ADR-065) handles the
|
|
non-QUIC transport on the server side, and nothing prevents a client
|
|
from constructing a `Connection` the same way after its own
|
|
transport-specific dial. The friction is the duplicated dial
|
|
boilerplate (each dial helper calls its transport's connector), not
|
|
duplicated TLS config (that is shared via ADR-087). The
|
|
bidirectionality criterion (a crate needs a Client type when (a) the
|
|
endpoint has protocol-level authority — e.g., channels' id allocation
|
|
— or (b) the protocol needs a reliable establishment interface) is
|
|
met by each crate independently; `AlknetClient` is the eventual
|
|
*shared dial seam*, not a prerequisite for any single client to
|
|
exist.
|
|
- **Cross-references**: ADR-034 (verifier selection — centralized in
|
|
`TlsClientConfig::new` per ADR-087), ADR-087 (`TlsClientConfig` is
|
|
not blocked on this OQ — the TLS config is extracted; only the dial
|
|
remains deferred), ADR-065 (server-side transport generalization —
|
|
the client-side analogue this OQ's deferral avoids preempting),
|
|
ADR-070 (the `BidiStreamSource` extension point, which is the
|
|
*Connection* opening and is orthogonal to the *client* establishment
|
|
question), OQ-CH-14 in
|
|
`docs/research/alknet-channels/phase-0-findings.md` (the research-scope
|
|
question this core-scope OQ carries forward). |