Files
alktunnels/tasks/tunnels/decide-take-framed-halves.md
T
glm-5.3-flash 0ceecf582b docs: remove em dashes from repo prose and forbid them going forward
- Rewrote the remaining em dashes outside docs/reviews/ (preserved as
  historical record, matching the alktty precedent): tests/ (313),
  fuzz/ (34 plus one shell comment), tasks/ (671 plus 12 kept in
  fences), docs/research + sdd_process + plans (525 plus 25 in
  fences), docs/architecture/overview.md fence labels (13), README,
  CHANGELOG, AGENTS.md, fuzz/README.md (176), and the
  .opencode/agents prompts (110)
- Rewrote fenced content too: ASCII tree and module-map label
  separators, Rust snippet comments, and template placeholders; wire
  data, JSON/BAST examples, and transcripts survive verbatim
- Rewrote 6 assert/expect message literals in tests/ and 2 format
  strings in fuzz/shared/src; no test data changed
- Added Project Convention 18: no em dashes in prose (doc comments,
  markdown including docs/ and tasks/, agent prompts, commit
  messages); fence content rule, en-dash clause rule, and the
  no-spaced-hyphen prohibition follow alktty's convention 15

Follows 6b20924 (src doc comments) and cd0afe0 (docs/architecture).

Verification: cargo test (141 pass); cargo clippy --all-targets -D
warnings; cargo fmt --check; cargo doc --no-deps; wasm check and
clippy for wasm32-unknown-unknown with -D warnings; em dash grep over
the repo returns matches only in docs/reviews/
2026-11-03 14:12:46 +00:00

4.7 KiB

id, name, status, depends_on, scope, risk, impact, level, tags, completed
id name status depends_on scope risk impact level tags completed
tunnels/decide-take-framed-halves N-3 decision: take_halves on datagram sessions hands RAW FRAMED bytes; decide the API posture before the first external consumer completed
tunnels/review-impl
single low component research
api
udp
codec
adr-005
api-stability
review-remediation
2026-09-08

Description

Review 001 finding N-3 (docs/reviews/001-implementation-review.md): a datagram session exposes send_datagram/recv_datagram (codec applied) until take_halves; then the caller owns RAW halves whose wire bytes carry the codec framing ([len: u16 BE][payload], documented on take_halves). The doc covers it, but it is a footgun asymmetry: the same session flips from "codec applied" to "codec is yours" depending on which accessor was called, and nothing in the type system warns about it.

This is an API-posture question, not a bug: ADR-005's surface becomes ABI-stable at the first external consumer, so the decision must land before then. The options on the table:

  1. Keep as-is + docs (today's posture): raw framed halves are power-user territory; the doc note carries it.
  2. Add a convenience take_framed_halves (the review's sketch): a DatagramReader-wrapped pair that keeps the codec so the halves API matches the datagram methods' framing posture. Additive to the raw take_halves, which stays.
  3. Re-shape take_halves for datagram sessions (breaking, before the window closes): e.g. a datagram session's take_halves returns framed-preserving halves by default, raw access via a distinct explicit method, which removes the footgun entirely at the cost of diverging take_halves semantics per substrate.

The decision should weigh: the pump_against shape (a UDP session pumped against an accepted local half needs the FRAMED adapter on the accepted side (the same framing-responsibility seam N-11's doc note covers), what an actual consumer (alkcall's assembly layer, a DNS-proxy shape) reaches for first, and whether a DatagramReader + write-frame wrapper pair is small enough to just ship (option 2 is a thin compose of existing public pieces (src/wire.rs already exposes both codec halves of the job).

Work

  • Re-read review 001 §N-3 + ADR-005 (the API-stability window); confirm no external consumer has pinned the take_halves import style yet. If one has, the window is closed; record that and choose the additive path (2) or docs-only (1)).
  • Write the decision as an ADR amendment (ADR-005) or a new ADR if the posture adds a public method; file the OQ only if the decision defers again.

Verification

  • taskgraph graph stays acyclic; if an ADR lands, docs/ architecture/ and the crate docs stay in sync (cargo doc --no-deps clean); if a method lands, the full battery + wasm checks pass.

Acceptance Criteria

  • A written decision (ADR amendment or task-note resolution) with the chosen option and rationale, before the first external consumer
  • If the decision adds surface: tests + battery green; review 001 §N-3 gets a resolution note

Resolution (2026-09-08)

Option 1: keep as-is, docs carry it. Recorded as ADR-005's §Amendment (2026-09-08, N-3). Decision inputs:

  • No external consumer exists (workspace grep verified 2026-09-08) so the window is open and the breaking reshape (option 3) was available but nothing demonstrates the need.
  • One take_halves semantic across substrates (the tunnel's wire bytes, uninterpreted) beats per-substrate divergence; the framing asymmetry is ADR-003's spec, not drift.
  • The pump_against seam composes the framed ADAPTER (UdpHalf, ADR-003's boundary codec, which landed with the U-1 remediation the same day), not framed halves; option 2 would not feed that use.
  • Option 2 (take_framed_halves) is additive and can land after the first consumer defines its shape without breaking anything, so deferring costs nothing. Design note recorded in the ADR: an AsyncRead-faced wrapper re-collides with F-2's empty-datagram/EOF ambiguity; the codec-side framing wants accessor-shaped surface, which the session already has.

Docs sync: ADR-005 §Amendment; consumer.md's datagram section states the posture; review 001 §N-3 resolution note landed. No code change (the decision adds no surface), so the standing battery is the verification: 63 default / 76 local green, doc build clean.

References

  • docs/reviews/001-implementation-review.md §N-3 (the finding) and §N-11 (the adjacent pump_against framing seam)
  • docs/architecture/decisions/005-consumer-session-owns-teardown.md (the API-stability window), src/consumer.rs (take_halves, TakenHalves), src/wire.rs (frame_datagram, DatagramReader)