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
This commit is contained in:
glm-5.3-flash committed 2026-11-03 19:31:53 +00:00
1 parent 9ca8c52601
commit e196ed43e3
115 files changed
+3593 -3531

No files matched your search

+2 -2
View File
@@ -9,7 +9,7 @@ fast-socks5 v1.0.0 (https://github.com/dizda/fast-socks5) and are
carried in-tree by alksocks (extraction per ADR-011,
docs/architecture/decisions/011-inline-vendor.md; extraction range
git 57b107b..5b18d4d; fork point = the tree as of commit 5b18d4d,
the extraction review's gate commit — ownership per ADR-013,
the extraction review's gate commit; ownership per ADR-013,
docs/architecture/decisions/013-targeted-fork.md). Upstream has no
per-file license headers; this bundled notice is the license
obligation's carrier. The extraction cut and the fork's deliberate
@@ -19,7 +19,7 @@ Modifications after the fork point are Copyright (c) 2026 the
alksocks developers, licensed under the crate license
(MIT OR Apache-2.0, per alksocks' Cargo.toml); the upstream MIT
notice above is preserved per its terms. The upstream project is
third-party and unmodified in place — see the ADRs for what was
third-party and unmodified in place; see the ADRs for what was
changed after the fork point.
---
+10 -10
View File
@@ -1,12 +1,12 @@
//! Forked from fast-socks5 v1.0.0 (upstream `src/client.rs` —
//! Forked from fast-socks5 v1.0.0 (upstream `src/client.rs`;
//! extraction; ownership per ADR-013, see the crate root's `mod.rs`).
//!
//! Extracted region: the `T`-generic
//! client session — [`Socks5Stream<S>`] with the `use_stream` path
//! client session: [`Socks5Stream<S>`] with the `use_stream` path
//! (the version/method greeting, the method-choice read, the
//! password-auth sub-handshake, the request encoder `request_header`,
//! the reply reader `read_request_reply`, and the socket accessors)
//! plus the [`AsyncRead`]/[`AsyncWrite`] impls — the consumer half's
//! plus the [`AsyncRead`]/[`AsyncWrite`] impls, the consumer half's
//! stream session (ADR-002; POC #1's fit: a channels `BiStream` is
//! fed here directly). A `Socks5Stream` is itself a stream, so
//! `pump_bidi`-shaped consumers and the front doors can pipe through
@@ -14,10 +14,10 @@
//!
//! Not forked (rfc1928.md's not-forked inventory row 2):
//! `Socks5Datagram`
//! (struct + impl — the kernel `UdpSocket` associate client; the
//! (struct + impl: the kernel `UdpSocket` associate client; the
//! crate's associate session is first-party per ADR-003's handle
//! swap) and the `Socks5Stream<TcpStream>` convenience impl
//! (`connect`/`connect_with_password`/`connect_raw` — the
//! (`connect`/`connect_with_password`/`connect_raw`, the
//! `tokio::net::TcpStream` dialers; the consumer's transport is a
//! channels `BiStream`, not a TCP dial). Upstream
//! `Config::connect_timeout` is cut with its only consumer,
@@ -26,13 +26,13 @@
//!
//! Extraction-time adjustments (mechanical, ADR-011 §Amendment):
//! `log` calls dropped (the bindings they consumed renamed); `anyhow`
//! dropped — errors stay `SocksError`-typed (the missing-target-addr
//! dropped; errors stay `SocksError`-typed (the missing-target-addr
//! case at upstream `client.rs:285` uses the typed
//! [`SocksError::MissingTargetAddr`] variant instead of
//! `anyhow::Error::msg(...).into()`); `use_password_auth`'s
//! `unreachable!()` (upstream `client.rs:207` — `methods.get(1)`
//! `unreachable!()` (upstream `client.rs:207`, where `methods.get(1)`
//! being `Some(AuthenticationMethod::None)`) is an error return
//! ([`SocksError::AuthenticationRejected`]) — convention 2 forbids
//! ([`SocksError::AuthenticationRejected`]); convention 2 forbids
//! panics in library code, and the state is reachable only by misuse;
//! the `Socks5Datagram` error sites (`SocksError::Other(anyhow!(...))`,
//! the `"[::]:0".parse().unwrap()` sites) go with the cut region. No
@@ -41,7 +41,7 @@
//!
//! Tests are first-party (upstream's client test module rides the cut
//! net region): raw-byte mock servers over tokio duplex streams, the
//! pinned byte sequences written directly — keeping the client's wire
//! pinned byte sequences written directly, keeping the client's wire
//! expectations uncorrelated from the forked server typestate.
use crate::rfc1928::util::stream::read_exact;
@@ -208,7 +208,7 @@ where
ref username,
ref password,
}) => (username, password),
// upstream `unreachable!()` — `methods.get(1)` being
// upstream `unreachable!()` (`methods.get(1)` being
// `Some(AuthenticationMethod::None)`: reachable only by
// misuse; the faithful translation is an error return,
// not a panic (convention 2). Same message as upstream's
+7 -7
View File
@@ -1,12 +1,12 @@
//! The RFC 1928 core: a targeted fork of fast-socks5 v1.0.0's
//! `T`-generic subset (MIT, third-party upstream —
//! `T`-generic subset (MIT, third-party upstream:
//! github.com/dizda/fast-socks5; `/workspace/fast-socks5` is the
//! read-only reference checkout, kept for differential testing of
//! RFC edge cases).
//!
//! Arrived by extraction (ADR-011 §Amendment, docs/architecture/
//! decisions/011-inline-vendor.md): only the code already generic
//! over `T: AsyncRead + AsyncWrite + Unpin` was copied in-tree — the
//! over `T: AsyncRead + AsyncWrite + Unpin` was copied in-tree; the
//! kernel-net handlers, the `socks4` module, and the legacy `Config`/
//! `Authentication` layer were **not** copied. The extraction cut,
//! the fork's two carried changes (BIND support, `new_reply`
@@ -15,7 +15,7 @@
//! `docs/architecture/rfc1928.md`.
//!
//! Ownership (ADR-013, docs/architecture/decisions/013-targeted-fork.md):
//! this is fully owned code — project conventions apply
//! this is fully owned code, so project conventions apply
//! unconditionally (no vendor carve-outs, no verbatim preservation,
//! no lint-allow posture). Fork point: the tree as of the
//! extraction-review commit (`5b18d4d`); extraction provenance in
@@ -32,13 +32,13 @@
//! `use_stream` path + the `AsyncRead`/`AsyncWrite` impls) lives in
//! [`client`].
//!
//! License: upstream is MIT with no per-file headers — the bundled
//! License: upstream is MIT with no per-file headers; the bundled
//! notice (`LICENSE`, this module's directory, v1.0.0 attribution +
//! the fork-point statement) is the license obligation's carrier.
//!
//! Maintenance posture: this code co-evolves with the wrapper and is
//! the crate's only RFC 1928 wire vocabulary ("no second codec,
//! ever" — ADR-011's rationale, carried). Nothing is absorbed
//! ever": ADR-011's rationale, carried). Nothing is absorbed
//! silently from upstream; nothing waits on upstream.
pub mod client;
@@ -216,7 +216,7 @@ impl ReplyError {
/// Map a wire reply code to its variant. Codes outside the RFC
/// table (`0x09`–`0xFF`, unassigned per RFC 1928 §6) map to
/// `GeneralFailure` — the RFC's catch-all — instead of panicking:
/// `GeneralFailure`, the RFC's catch-all, instead of panicking:
/// the value comes off the wire (the client session feeds the raw
/// reply byte here), so a misbehaving peer must surface as a typed
/// error, never a panic. Upstream's `unreachable!()` arm was the
@@ -379,7 +379,7 @@ mod tests {
}
/// F-1 (review): unassigned reply codes (`0x09`–`0xFF`, RFC 1928
/// §6) surface as `GeneralFailure` — the RFC's catch-all — never
/// §6) surface as `GeneralFailure` (the RFC's catch-all), never
/// a panic. Upstream's `unreachable!()` arm was removed: the code
/// comes off the wire (`read_request_reply` → `from_u8`), so a
/// misbehaving peer must not crash the consumer task.
+16 -16
View File
@@ -1,4 +1,4 @@
//! Forked from fast-socks5 v1.0.0 (upstream `src/server.rs` —
//! Forked from fast-socks5 v1.0.0 (upstream `src/server.rs`;
//! extraction; ownership per ADR-013, see the crate root's `mod.rs`).
//!
//! Extracted region: the explicit
@@ -6,13 +6,13 @@
//! negotiation with the `AuthMethod<T>` machinery, `read_command`,
//! `reply_success`, `reply_error`, `transfer`, `wait_on_tcp`,
//! `ErrorContext<T>`), `SocksServerError`, and the published reply
//! encoder `new_reply` (carried change #2 — upstream's is private,
//! encoder `new_reply` (carried change #2: upstream's is private,
//! `fn new_reply` at `server.rs:1380`; here it is `pub` so the
//! BIND reply#2 writer and consumer reply readers reuse the
//! 10/22-byte reply form instead of re-encoding it). The cut regions
//! (the legacy `Socks5Server`/`Socks5Socket`/`Config`/`Authentication`
//! layer, the `run_*` kernel-net handlers, `DnsResolveHelper`, the
//! net-only tests) stay in `/workspace/fast-socks5` — the not-forked
//! net-only tests) stay in `/workspace/fast-socks5`, the not-forked
//! inventory row 1 in `docs/architecture/rfc1928.md`.
//!
//! Extraction-time adjustments (mechanical, ADR-011 §Amendment):
@@ -23,22 +23,22 @@
//! `SocksServerError::AuthenticationRejected` are cut; the variant
//! stays (the typestate's `accept_password_auth` produces it).
//!
//! Post-fork owned changes (extend, never replace — convention 17):
//! Post-fork owned changes (extend, never replace, per convention 17):
//! the keep-or-cut sweep (review 002 N-1) cut the producer-replaced
//! machinery — the `auth_method_enums!` macro (upstream's enum
//! machinery: the `auth_method_enums!` macro (upstream's enum
//! generator) with its generated `StandardAuthentication` enum +
//! `allow_no_auth`, `skip_auth_this_is_not_rfc_compliant`, and
//! `CheckResult`'s `Option`/`Result` impls (`bool` stays) — none had
//! `CheckResult`'s `Option`/`Result` impls (`bool` stays); none had
//! an in-tree consumer once the producer drove `negotiate_auth`
//! directly. Kept with note (the hand-port map, ADR-013):
//! `transfer`/`wait_on_tcp` are differential-suite-consumed (their
//! semantics stay pinned there), and the error vocabulary keeps its
//! upstream variants. One variant was added, not absorbed:
//! `SocksServerError::Refused` — the honest refusal vocabulary for
//! `SocksServerError::Refused`: the honest refusal vocabulary for
//! the producer's in-stream policy refusals (review 002 N-4; never
//! reaches the wire). Recorded in docs/architecture/rfc1928.md.
//!
//! The BIND note (ADR-007): BIND's driving is producer-side code —
//! The BIND note (ADR-007): BIND's driving is producer-side code;
//! the typestate already returns `TCPBind` from `read_command()`,
//! `reply_success(sock_addr)` carries reply#1's BND.ADDR, and
//! reply#2 is written with the published `new_reply`. No BIND code
@@ -159,7 +159,7 @@ impl<T> Socks5ServerProtocol<T, states::Opened> {
/// Closure-result adapter for [`Socks5ServerProtocol
/// ::accept_password_auth`]'s credential checker: `bool` (`is_good`).
/// The `Option`/`Result` impls upstream-adjacent shape were cut in
/// the keep-or-cut sweep (review 002 N-1) — every in-tree checker
/// the keep-or-cut sweep (review 002 N-1); every in-tree checker
/// returns `bool`; re-add with a typed-rejection design if a door
/// ever wants it.
pub trait CheckResult {
@@ -240,7 +240,7 @@ pub trait AuthMethodSuccessState<T> {
/// Use blank structs for individual protocol implementations;
/// `negotiate_auth` takes any slice of `AuthMethod`s, so a custom
/// selection is a slice of the method structs (upstream's
/// enum-generator macro was cut — review 002 N-1; nothing in-tree
/// enum-generator macro was cut (review 002 N-1); nothing in-tree
/// drove a multi-method slice).
pub trait AuthMethod<T>: Copy {
type StartingState;
@@ -354,7 +354,7 @@ impl<T: AsyncWrite + Unpin> PasswordAuthenticationImpl<T, password_states::Recei
/// Notify the client with a "NOT_ACCEPTABLE" reply and drop the socket.
///
/// The status byte is `0xff` — RFC 1929 requires only that a
/// The status byte is `0xff`: RFC 1929 requires only that a
/// rejection be any non-`0x00` byte; `0xff` is the convention
/// upstream picked (borrowed from the method-negotiation phase's
/// `SOCKS5_AUTH_METHOD_NOT_ACCEPTABLE` constant, which is a
@@ -530,7 +530,7 @@ impl<T: AsyncRead + AsyncWrite + Unpin> Socks5ServerProtocol<T, states::Authenti
}
/// The RFC reply form's placeholder BND.ADDR for error replies
/// (statically constructed — upstream parsed `"0.0.0.0:0"` at runtime).
/// (statically constructed; upstream parsed `"0.0.0.0:0"` at runtime).
const V4_UNSPECIFIED_ZERO: SocketAddr = SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::UNSPECIFIED, 0));
/// Generate reply code according to the RFC.
@@ -606,14 +606,14 @@ mod tests {
/// `SocksServerError::to_reply_error`'s full mapping table (review
/// 001 L-3): `UnknownCommand` → `CommandNotSupported`,
/// `AddrError` delegates to its own table, and every remaining
/// variant falls through to `GeneralFailure` — the mapping the
/// variant falls through to `GeneralFailure`, the mapping the
/// producer's error surface relies on when mapping session errors
/// to reply codes (`try_notify!`'s secondary-error write). The
/// fall-through arms (`Io`/`FromUtf8`/`EOF`/auth/refusal variants)
/// are exercised here directly, since driving each from the wire
/// is not one-test-cheap. Upstream `src/server.rs:62-70`. The
/// table also pins the vocabulary honesty (review 002 N-4):
/// `Bug` and `Refused` are log-vocabulary variants — both fall
/// `Bug` and `Refused` are log-vocabulary variants; both fall
/// through to `GeneralFailure` (they never reach the wire from
/// the typestate; the producer's refusal sites write the RFC
/// reply before the session error).
@@ -655,13 +655,13 @@ mod tests {
}
/// The refusal vocabulary (review 002 N-4): `Refused` is the
/// honest display for the producer's in-stream policy refusals —
/// honest display for the producer's in-stream policy refusals:
/// not `BUG:` (a working-as-designed refusal is not a bug) and
/// not "Authentication rejected" (an ACL refusal is not an auth
/// failure). It is log/observability vocabulary: never encoded
/// on the wire by the typestate (only `to_reply_error` maps it,
/// and the producer's refusal sites write the RFC reply before
/// the session error). Upstream has no such variant — the
/// the session error). Upstream has no such variant; that is the
/// fork's owned addition, recorded in the module doc.
#[test]
fn refused_variant_display_is_honest_and_wire_invisible() {
+2 -2
View File
@@ -1,10 +1,10 @@
//! The RFC 1928 core's utility modules (upstream `src/util/` —
//! The RFC 1928 core's utility modules (upstream `src/util/`;
//! extraction; see the fork statement in the crate root's `mod.rs`
//! and ADR-013 for ownership).
//!
//! Upstream layout: `stream.rs` (the `read_exact!`/`ready!` macros +
//! `ConnectError`) and `target_addr.rs` (`TargetAddr` + the address
//! codec) — filled by the wire-vocabulary extraction task.
//! codec), filled by the wire-vocabulary extraction task.
pub mod stream;
pub mod target_addr;
+4 -4
View File
@@ -1,10 +1,10 @@
//! Forked from fast-socks5 v1.0.0 (upstream `src/util/stream.rs` —
//! Forked from fast-socks5 v1.0.0 (upstream `src/util/stream.rs`;
//! extraction; ownership per ADR-013, see the crate root's `mod.rs`).
//!
//! Upstream carries the `read_exact!`/`ready!` macros, `ConnectError`,
//! and the `tcp_connect*` dialers; only the `read_exact!` macro +
//! `ConnectError` rode the extraction (the dialers and the `ready!`
//! macro are the cut kernel-net region — the crate's local backend is
//! macro are the cut kernel-net region: the crate's local backend is
//! first-party tokio code, ADR-008, and nothing in the extracted
//! region used `ready!`). Extraction-time adjustment: upstream
//! exports the macro at the crate root via `#[macro_export]`; here it
@@ -13,11 +13,11 @@
//! public API).
//!
//! [`ConnectError`] is currently constructed nowhere in-tree (review
//! 001 C-1 — upstream's sole construction site was the cut
//! 001 C-1: upstream's sole construction site was the cut
//! `tcp_connect` dialer). The keep-or-cut trigger (cut if the
//! producer layer landed without consuming it) fired and the
//! disposition is **keep** (review 002 N-1): it stays as the
//! hand-port map's companion — the dial-error → reply-code mapping
//! hand-port map's companion (the dial-error → reply-code mapping
//! (`to_reply_error`) is what a future kernel-net port (e.g. the
//! dial-callback shape, ADR-004) would reuse, and its mapping is
//! pinned by the module's test.
+7 -6
View File
@@ -1,10 +1,11 @@
//! Forked from fast-socks5 v1.0.0 (upstream `src/util/target_addr.rs`
//! — extraction; ownership per ADR-013, see the crate root's
//! Forked from fast-socks5 v1.0.0 (upstream `src/util/target_addr.rs`;
//! extraction; ownership per ADR-013, see the crate root's
//! `mod.rs`).
//!
//! Upstream carries `TargetAddr`, the address codec (`read_address`),
//! and the `resolve_dns` helper; only the codec rode the extraction
//! (DNS resolution is the cut net region — `tokio::net::lookup_host`
//! (DNS resolution is the cut net region, since
//! `tokio::net::lookup_host`
//! is not in the wasm-clean graph). The `resolve_dns` method is cut
//! with its two `AddrError` variants (`DNSResolutionFailed`,
//! `NoDNSRecords`).
@@ -110,7 +111,7 @@ impl TargetAddr {
/// The blocking `ToSocketAddrs` form: IP targets resolve directly;
/// domain targets cannot be resolved here (domain resolution is the
/// egress's job — ADR-012) and error.
/// egress's job, per ADR-012) and error.
impl std::net::ToSocketAddrs for TargetAddr {
type Iter = IntoIter<SocketAddr>;
@@ -210,7 +211,7 @@ impl ToTargetAddr for &TargetAddr {
pub enum Addr {
V4([u8; 4]),
V6([u8; 16]),
// `String` — validated UTF-8 at the codec boundary (`read_address`
// `String`, validated UTF-8 at the codec boundary (`read_address`
// converts before constructing).
Domain(String),
}
@@ -218,7 +219,7 @@ pub enum Addr {
/// This function is used by the client & the server.
///
/// The domain arm's allocation is bounded by construction: the wire
/// length field is one byte, so the claimed length is at most 255 —
/// length field is one byte, so the claimed length is at most 255;
/// the attacker-controlled byte cannot ask for more. Any change that
/// widens that field (an FRAG-style extension) requires an explicit
/// cap here before the allocation.