docs: N-3 decision — take_halves stays raw-framed on datagram sessions

Review 001 N-3 (tasks/tunnels/decide-take-framed-halves), decided
before the first external consumer:

- Option 1 (keep as-is, docs carry it), recorded as ADR-005
  §Amendment. No external consumer exists (window open — the breaking
  reshape was available but undemonstrated); one take_halves semantic
  across substrates beats per-substrate divergence; pump_against
  composes the framed adapter (UdpHalf), not framed halves; and the
  take_framed_halves convenience is additive — it can land after the
  first consumer defines its shape.
- consumer.md's datagram section states the posture; review 001 §N-3
  resolution note landed; task marked completed.

Docs-only (no code change). Verified: fmt --check, doc --no-deps
warning-free, 63 default / 76 local tests green.
This commit is contained in:
glm-5.3-flash committed 2026-09-08 21:24:28 +00:00
1 parent 8cf4cc8bb5
commit 0bf4da3bb6
4 files changed
+105 -5

No files matched your search

+5 -1
View File
@@ -84,7 +84,11 @@ one API, `-L` and `-R` alike.
`Some(bytes)` per datagram — possibly empty (`len=0` is a legal
empty datagram) — and `None` only on stream EOF (the channels-level
sentinel; the codec never collides with it, F-2). A >65535-byte
send is rejected at frame time (`Oversize`).
send is rejected at frame time (`Oversize`). `take_halves()` on a
datagram session yields the RAW halves — the wire bytes carry the
codec framing (ADR-005's N-3 amendment: one `take_halves` semantic
for every substrate; keep the session for codec-applied I/O, or
compose the framed adapter at your own boundary).
## Lifecycle and Teardown (the point of ADR-005)
@@ -2,7 +2,13 @@
## Status
Accepted (2026-09-07; resolves the reverse POC W3 finding)
Accepted (2026-09-07; resolves the reverse POC W3 finding).
Amended (2026-09-08, review 001 N-3 decision): the datagram
`take_halves` posture is pinned — raw framed halves stay the only
owned-halves shape (`take_halves` keeps its semantics for every
substrate), the datagram accessors remain the codec path, and a
framed-halves convenience is deferred to the first consumer that
reaches for it. See §Amendment (2026-09-08, N-3).
## Context
@@ -86,6 +92,58 @@ its teardown:
## Consequences
### Amendment (2026-09-08 — the N-3 datagram `take_halves` posture, decided before the first external consumer)
Review 001 N-3: a datagram session exposes codec-applied accessors
(`send_datagram`/`recv_datagram`) until `take_halves` — then the
caller owns RAW halves whose wire bytes carry the `[len: u16 BE]`
framing. Documented, but a footgun asymmetry the API window
(this ADR's surface becomes ABI-stable at the first external
consumer) forced a decision on. Decided: **option 1 — keep as-is,
carried by docs**, with the rationale:
- **No external consumer exists** (verified 2026-09-08 — no crate in
the workspace depends on alktunnels); the window is open, so a
breaking reshape was *available* — but nothing yet demonstrates a
need for a different shape.
- **The framing asymmetry is ADR-003's spec, not drift.** The
datagram data plane is mandatory-framing by substrate
discrimination; "raw halves of a datagram session carry the
frames" is the same statement as "the codec lives at the substrate
boundary." `take_halves` yielding raw halves keeps ONE semantic
across substrates (the tunnel's wire bytes, uninterpreted); a
per-substrate `take_halves` (option 3) diverges semantics by
substrate — the worse confusion at the type level even as it
removes the byte-level footgun.
- **`pump_against` needs the framed adapter, not framed halves.** The
adjacent seam (N-11, resolved in-review with the doc note): a UDP
session pumped against an accepted local half requires the FRAMED
adapter on the accepted side — the `local` feature's `UdpHalf` (or
an assembly equivalent), not a raw socket. What composes there is
the adapter AT the boundary, not a session-side wrapper pair; a
`take_framed_halves` convenience would not feed this use.
- **The convenience is additive and can land later.** Option 2's
`take_framed_halves` breaks nothing when added after the first
consumer (unlike a `take_halves` reshape), so its timing is
decoupled from this window — deferring it costs nothing now, and
the first real consumer's reach defines its shape (a
`DatagramReader`-wrapped pair has an open design question: an
`AsyncRead` face re-collides with the F-2 empty-datagram/EOF
ambiguity the codec exists to prevent — the codec-side framing
wants accessor-shaped surface, which the session already has).
- **The docs carry the contract.** `take_halves`'s doc states the
framing (landed with the implementation); consumer.md's datagram
variant section now states the posture explicitly.
**Consequence:** datagram callers wanting codec-applied I/O after
taking ownership should keep the session (drive `send_datagram`/
`recv_datagram`; the session IS the owner) or compose `UdpHalf` at
their own boundary — `take_halves` is the raw-wire escape hatch and
stays documented as such. Revisit only on a demonstrated consumer
need (file an OQ at that point).
### Consequences (original)
- **Assembly layers cannot leak adopted channels** by forgetting to
reap — the type is the discipline (the W3 gap closes structurally).
- **The reverse-flow initiator gets the same session shape** as the
+11
View File
@@ -447,6 +447,17 @@ convenience `take_framed_halves` that keeps the codec (a
the review notes it for the API-stability window (ADR-005's surface
becomes ABI-stable at first external consumer — decide before then).
**RESOLUTION (2026-09-08, decided before the first external
consumer):** option 1 — keep as-is, docs carry it — recorded as
ADR-005's §Amendment (2026-09-08, N-3). No external consumer exists
(window open, so the breaking reshape was available but
undemonstrated); one `take_halves` semantic across substrates beats
per-substrate divergence; the `pump_against` seam composes the
framed ADAPTER (UdpHalf), not framed halves; and the convenience
(option 2) is additive — it can land after the first consumer
defines its shape without breaking anything. consumer.md's datagram
section states the posture.
## N-4 [minor] — `open_reverse_channel` builds the params JSON by hand instead of serializing `TunnelParams` — FIXED in-review
`src/consumer.rs:395-409`: the substrate is matched to its string and