Files
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

9.3 KiB

id, name, status, depends_on, scope, risk, impact, level, tags
id name status depends_on scope risk impact level tags
tunnels/local-bind-halves local feature, TCP bind halves + the bind-first assembly pieces completed
tunnels/bindfirst-producer
narrow low component implementation
local
tcp
bind
listener
feature-gated

Description

Extend the local feature (non-wasm, tokio/net-gated; tunnels/local-socket-halves established the pattern) with the bind-first shape's substrate half: a real TCP bind function (BindFn-compatible) and an accept loop producing (peer, TargetHandle) pairs, plus the direct-op convenience dial closure composition (the DialFn the direct op shares with the base op, likely already satisfied by local_dial; verify, don't duplicate).

The bind half

/// The real-socket BindFn source (the `local` feature's shape 3
/// backing): bind `addr` (port 0 = OS-chosen), report the REAL
/// bound address (the `bound` reply field's source, ADR-008 §2),
/// expose the accept loop that feeds the dispatcher.
pub async fn bind_tcp_first(addr: &str)
    -> Result<BoundListener, TunnelEstablishError>;

pub fn local_bind_first() -> BindFn;   // renders via bind_tcp_first
  • BoundListener.listener carries the TcpListener (type-erased in the plan; tunnels/bindfirst-producer's shape note); the accept loop is assembly code composed ON it: the protocol crate stays socket-blind, so the loop lives here (local = substrate code, ADR-004):
  • TcpListenerHalves already has an accept loop (accept_loop feeding an AcceptQueue, the accept-first shape). The bind-first shape needs a DIFFERENT loop: per-accept it must produce (peer_addr, TargetHandle) for the accept dispatcher (which opens forwarded channels per accept), not feed a queue for the establisher to pop. Add a sibling method/struct (e.g. TcpListenerHalves::accept_pairs or a new BindFirstAcceptLoop adapter); decide the honest shape, document in Notes. The peer address rides from accept()'s return value (reply#2 fidelity's source, ADR-008 §3's peer carrier).
  • The direct-op DialFn: local_dial() already renders (Substrate, backing) → halves with the UDP framed adapter; the direct establisher calls the SAME closure with the rendered target string. Verify the UDP wrap happens INSIDE the closure (it does; review 001 U-1) so direct-udp gets the framed adapter free; add a test pinning direct-udp-through-local_dial framing if missing.

Tests (tests/, real sockets; the local suite's posture)

  • Bind port 0 → bound reports the REAL OS-chosen address (the reply#1 fidelity pin).
  • Two clients dial the bound address → two accepts → two forwarded channels (via the bind-first harness) → both pump independently (peer fidelity: each forwarded open carries the REAL accepted peer).
  • Unix-domain bind (OQ-TN-14 parity): bind_unix_first-equivalent. Decide whether unix bind-first ships in this task or is deferred with a note (the wire enum already carries unix; the local dial path exists, and the bind path is the same shape; prefer shipping both, matching dial parity).

Acceptance Criteria

  • bind_tcp_first + the accept-pairs loop (peer + halves per accept) behind local
  • The bound address is the REAL OS-chosen one (port 0 resolved, pinned by test)
  • Direct-op DialFn reuse verified (no duplicate dial code; UDP framed-adapter wrap composes)
  • Unix bind-first parity decided + shipped or explicitly deferred with rationale
  • cargo test --all-features green; default-crate wasm32 checks still clean (the feature gate holds)
  • Clippy/fmt clean (both feature sets)

References

  • docs/architecture/decisions/008-listen-metadata-forwarded-op.md (§1 shape 3, the bind-first establisher; §3 the peer carrier)
  • docs/architecture/producer.md §The Registration API (BindFn shape)
  • tasks/tunnels/local-socket-halves.md (the local pattern + the existing bind_tcp/TcpListenerHalves/AcceptQueue accept-first shapes)
  • tasks/tunnels/bindfirst-producer.md (the BindFn/ BindFirstAcceptLoop contracts this implements)
  • OQ-TN-14 (unix ships with local v1; stdio is out, as in alktty's)

Notes

  • The task sketch's bind_tcp_first(addr: &str) signature held: the committed [BindFn] contract takes (substrate, backing); the substrate dispatch lives inside [local_bind_first] (tcp → [bind_tcp_first], unix → [bind_unix_first], udp → fails loud), matching the type-erased closure precedent (the protocol crate stays socket-blind; the closure is the assembly side).
  • The accept-pairs shape: a *_pair method, not a sibling loop struct: [TcpListenerHalves::accept_pair] (one accept per call → (peer, halves)). The task floated accept_pairs or a BindFirstAcceptLoop adapter struct; one method is the honest shape: the DISPATCHER owns the loop pacing (the committed BindFirstAcceptLoop contract is one closure call = one accept), so the substrate half is just the single-accept step with the peer attached. local_bindfirst_accept_loop() is the adapter: it wraps the *_pair methods with the downcast (the assembly side of the type-erased injection; a foreign listener type fails loud as handler_error, pinned by test). The accept-FIRST [TcpListenerHalves::accept_loop] (queue-feeding) is untouched.
  • UDP bind-first is NOT here: refused loud (a decision the task left open): a UDP socket has no accept, so the bind-first shape is not DEFINED for udp; [local_bind_first] maps Substrate::Udp to a handler_error rather than faking a listener (pinned by udp_bind_first_fails_loud). The per-peer associate shape (an accept-analogue for UDP) would be a wire/protocol design decision, an ADR if it ever ships. The direct/forward udp dial shapes cover the substrate.
  • Unix bind-first SHIPPED (the task's preferred posture, OQ-TN-14 dial parity): bind_unix + UnixListenerHalves (a sibling of [TcpListenerHalves]: local_addr, the queue-feeding accept_loop, and the bind-first accept_pair) + [bind_unix_first]. The bind-first accept_pair's peer is the ACCEPTED client's bound path; an unnamed (autobind) client fails loud: the forwarded op's peer param is required on the wire, an addressless peer cannot be represented (ADR-008 §3). The unix parity test binds the CLIENT to a named path first (socket2; std has no UnixStream::bind), making the peer representable.
  • bind_unix maps bind failure to ResourceShortage (matching [bind_tcp]'s posture, the held-resource class), not DialFailed: the bind IS the dial for the establisher's error table (shape-3: unbindable/held → dial_failed-class rides the establisher's passthrough), but the substrate function's own class follows the sibling bind functions.
  • Direct-op DialFn reuse: verified, no duplication: [local_dial] is unchanged and shared; the UDP wrap happens INSIDE the closure (review 001 U-1). The missing pin was added: direct_udp_through_local_dial_round_trips_framed (tests/ local_halves.rs) drives TunnelSession::open_direct + Substrate::Udp through [local_dial] against a REAL udp echo socket, with the datagram boundary preserved through the direct op's establisher path.

Summary

Implemented the local feature's bind-first substrate half (shape 3, ADR-008 as amended) per the task: src/local/mod.rs now has bind_tcp_first (the real-socket [BindFn] source: port 0 resolved via local_addr, the bound reply field's data), TcpListenerHalves::accept_pair (one accept → (peer, halves), with the peer riding accept()'s return, the forwarded op's peer carrier), bind_unix + UnixListenerHalves + bind_unix_first (unix bind-first parity SHIPPED per OQ-TN-14; unnamed peer fails loud, the forwarded peer param requires an address), local_bind_first() (the [BindFn] convenience: tcp/unix dispatch, udp fails loud: no accept on a UDP socket, the shape is not defined there, never a fake listener) and local_bindfirst_accept_loop() (the [BindFirstAcceptLoop] convenience: downcasts the plan's opaque listener to the module's wrappers, one pair per call; a foreign listener fails loud). The accept-FIRST shapes (accept_loop + AcceptQueue) are untouched.

Tests: tests/local_bindfirst.rs (4, #![cfg(feature = "local")]), covering the tcp e2e over REAL sockets (bind port 0 → the reply's bound is the REAL OS-chosen address the test then DIALS; a fake bound would strand the dial; two clients → two accepts → two forwarded channels → independent interleaved pumps, each forwarded open carrying the REAL accepted peer, distinct ports), unix parity (real socket path in bound; a named-path client connects; the forwarded open carries the REAL peer path), the udp refusal, and the downcast-miss probe. Plus direct_udp_through_local_dial_round_trips_framed in tests/ local_halves.rs, the direct-op DialFn reuse pin (the framed udp adapter composes through the direct op; review 001 U-1).

Verified: cargo test (default) and cargo test --all-features green (46 lib + 151 integration total; the local suites 20 + 4), clippy --all-targets --all-features -D warnings clean, fmt clean, wasm32-unknown-unknown check passes (the default crate stays wasm-clean; the bind-first half lives behind local, non-wasm by design).