Files
glm-5.3-flash e196ed43e3 docs: remove em dashes from repo prose and forbid them going forward
- Rewrote em dash prose across the whole tree outside docs/reviews/
  (the historical reviews stay verbatim, matching the alktunnels and
  alktty precedent): src doc comments and inline comments (587),
  docs/architecture (319), docs/research + sdd_process (484), tests
  (361), tasks (899), fuzz (28), README/CHANGELOG/Cargo.toml (77),
  AGENTS.md (85), and the .opencode/agents prompts (109)
- Fenced content follows the precedent: comments, ASCII-diagram label
  separators, and template placeholders rewritten; wire data, JSON/BAST
  documents, byte diagrams, and transcripts survive verbatim; assert,
  expect, panic, error-display, and format message literals rewritten
  as prose; no code, fixture, or test data change
- src/rfc1928/LICENSE: the two dashes in alksocks' own provenance
  header rewritten; the MIT legal text untouched
- AGENTS.md adds Project Convention 18: no em dashes in prose; varied
  restructure, no spaced-hyphen substitutes, fenced prose rule, en
  dash numeric ranges exempt, docs/reviews/ exempt from the greps
- tasks/*.md frontmatter: the name values rewritten by the sweep are
  now quoted (a raw colon inside an unquoted YAML scalar parsed as
  null); taskgraph validate reports 37 tasks, no issues

Verification: cargo test; cargo clippy --all-targets -D warnings;
cargo fmt --check; cargo doc --no-deps; fuzz corpus replay
(cargo test --manifest-path fuzz/shared/Cargo.toml); wasm check and
clippy for wasm32-unknown-unknown with -D warnings; em dash grep over
the repo returns matches only in docs/reviews/ and the bast.md BAST
JSON data fence
2026-11-03 19:31:53 +00:00

8.6 KiB

id, name, status, depends_on, scope, risk, impact, level
id name status depends_on scope risk impact level
implementation/composed-egress Composed: the direct-op egress (lazy destination→tunnel table) completed
moderate medium component implementation

Description

The composed feature's egress provider (ADR-008's composed variants): the ASSOCIATE egress that dials each destination through an alktunnels 0.2.0 direct-op channel (TunnelSession::open_direct, Substrate::Udp): per-target ACL for free (each destination rides its own tunnel:direct-governed channel), the lazy destination→tunnel table LRU-evicted at a cap (POC #2's ComposedEgress shape).

Deliverables:

  1. Cargo.toml: composed = ["dep:alktunnels"] (the only feature that adds the alktunnels dependency, ADR-009); alktunnels pinned "0.2.0" (the sibling pin posture; convention 11's deliberate-bump rule applies). Verify the composed graph stays wasm-clean (alktunnels' default crate is wasm-clean; the composed providers use only its ungated channels surface, and the wasm check with --features composed is a hard gate).
  2. src/composed/mod.rs (feature composed): the composed providers, named per producer.md §Wiring:
    • direct_egress(...) → EgressFactory: the POC #2 ComposedEgress shape ported to the landed seam types: a lazy destination→tunnel table: each new destination opens one alktunnels UDP-tunnel channel (open_direct, Substrate::Udp), a relay task per tunnel drains inbound datagrams into a shared queue, the write half stays in the table (LRU-capped; dropping a write half EOFs the tunnel producer-side; the session's Drop reaps the channel entry (alktunnels ADR-005).
    • The Sync-bound wrinkle (the batch-1 review's seam contract vs alktunnels' surface): the landed TargetHandle/BindTarget Handle halves are Box<dyn AsyncRead + Send + Sync + Unpin>, but alktunnels' TakenHalves yields Send + Unpin only. The composed providers need an owner-wrapper inside the handle payloads (egress.rs's documented posture: "keep-alive state a substrate needs rides inside the halves; store a provider-side owner wrapper"). For the egress the wrapper is the table entry's session (the egress owns it, not a handle payload); for the composed accept (the sibling task) the wrapper is load-bearing, so design it here and share it.
    • The readiness contract (load-bearing): next_reply parks on the shared inbound queue (recv().await, Pending until a relay task delivers); a closed queue (all relays ended) returns Err, never a hot Ok(None) (the POC's closed-queue arm; the stage's zero-yield defense, review 002 R-1, is the backstop, not the design). Replies carry inner bytes (addr + data; the reply address is the entry's recorded destination; framing-once).
    • Per-datagram send is awaited inline (the stage interleaves sends with reply drains), so a slow egress applies backpressure to the association, not to other channels (wire.md's note: "the batch-2 composed-egress author inherits this bound knowingly"). The table's open cost rides the send path (lazy open on first datagram; the POC's measured ~0.8 ms).
    • The resource/target mapping: open_direct takes a SubstrateAddr: the destination's TargetAddr renders to it (IP forms direct; domain form passes through unresolved, producer-side resolution, the ssh -D semantic; ADR-012). Constructor config carries the LRU cap (the 256-channel default cap bounds fan-out, ADR-010's posture; the cap is config, not wire).
    • Module doc: names the ADRs (008/009), the per-target-ACL benefit, the readiness contract, the framing-once invariant, the backpressure bound.
  3. Tests (tests/composed-egress.rs): the POC #2 shape end to end over channels: a tunnel-producing peer (the alktunnels registry shape, register_tunnel_direct_openable), the composed egress against it, datagram round-trips through the tunnel (multiple destinations → multiple tunnels; the LRU eviction at cap pinned by test); the readiness contract re-pinned (park-then- datagram, no spin); the closed-queue → association-end shape; framing-once (no double prefix, the POC #2 bug class).

Acceptance Criteria

  • cargo test passes; cargo test --all-features passes
  • cargo check --features composed --target wasm32-unknown- unknown clean; the composed graph is wasm-clean (hard gate)
  • cargo clippy --all-targets --all-features -- -D warnings clean; cargo fmt --check clean
  • alktunnels pinned "0.2.0"; the dep rides composed alone (default graph unchanged, per the cargo tree spot-check)
  • The lazy table + LRU eviction land per the POC shape (pinned by test)
  • The readiness contract honored (real async park; closed queue → Err, never a hot Ok(None)), pinned by test
  • Framing-once (replies are inner bytes; no double prefix), pinned by test
  • Per-datagram send backpressure documented on the constructor

References

  • docs/architecture/decisions/008-provider-defaults.md (the composed variants), 009-feature-gates.md, 003 (the handle swap), 010 (the cap), 012 (resolution)
  • docs/architecture/producer.md §Wiring (composed::direct_egress), §The Three Seams
  • POC reference: /workspace/alksocks-udp-poc/src/egress.rs (ComposedEgress: the table, the relay tasks, the closed-queue posture)
  • Landed surfaces: src/egress.rs (the seam contracts + the owner-wrapper posture), alktunnels 0.2.0 (TunnelSession::open_direct, TakenHalves, register_tunnel_direct_openable)

Notes

Landed as src/composed/mod.rs (feature composed) + tests/composed-egress.rs, with a wire_tunnel_direct topology added to tests/harness.rs (the consumer-side topology with alktunnels' direct op on the producer's serving registry; the composed egress's ChannelClient is the direct-op caller).

Two deviations from the POC shape, both deliberate:

  • The closed-queue condition is a live-relay count, not a queue close. The egress holds its own factory Sender, so the shared mpsc queue can never fully disconnect: recv() would never return None and the POC's closed-queue arm was unreachable through this surface. Relays instead emit an Ended marker on their tunnel's EOF; next_reply counts them (live_relays) and returns the association-end Err when the count reaches zero and the queue is drained. The readiness contract is unchanged (the wait is a real async park; a drained-but-live relay set parks).
  • The tunnel codec is alktunnels' wire::DatagramReader ([len] + payload), not this crate's RFC-framed one; the POC fed the tunnel read half into the SOCKS wire reader (a bug class the round-trip test caught during bring-up); the composed egress frames the tunnel wire once with the tunnel codec and the RFC wire once downstream (framing-once holds at both boundaries).

The Sync-bound wrinkle resolves as designed: the shared SessionOwner wrapper (half + session in one owner) is exported for the composed accept (the sibling task); the egress itself owns the session in the table entry (the POC's shape) and the relay task owns the bare read half.

cargo tree --edges normal spot-check: alktunnels rides composed alone (absent from the default normal graph; the dev-dep copy is dev-only).

Summary

The composed egress landed: composed = ["dep:alktunnels"] (alktunnels pinned "0.2.0"; dep rides composed alone), src/composed/mod.rs (direct_egress, the EgressFactory), the lazy destination→tunnel table with LRU eviction at cap (DEFAULT_ MAX_TUNNELS = 256), per-tunnel relay tasks draining into a shared inbound queue, SessionOwner (the owner wrapper shared with the composed accept), the readiness contract (real async park; ended relays → association-end Err, never a hot Ok(None)), framing- once, per-target ACL via the direct op, domain pass-through unresolved (ADR-012), and the backpressure bound documented on the constructor. tests/composed-egress.rs (6 tests over the wire_tunnel_direct harness topology): round-trips per destination → one tunnel each, table reuse, LRU eviction/re-dial, the readiness park, the closed-relay-set Err, framing-once through the wire layer, and the domain pass-through. Verification: cargo test (default) + --all-features pass; the wasm check with --features composed clean; clippy (--all-targets --all-features -- -D warnings) clean; cargo fmt --check clean; cargo doc --no-deps clean.