Files
alksocks/tasks/implementation/core-params-errors.md
T
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

6.9 KiB

id, name, status, depends_on, scope, risk, impact, level
id name status depends_on scope risk impact level
implementation/core-params-errors Core: params schema + scope constants + typed error surface completed
implementation/core-crate-deps
narrow low component implementation

Description

Implement src/params.rs and src/error.rs per wire.md §The Open Op, ADR-001, and ADR-006, i.e. the wire-stable surface (the one-way door) and the typed open-error vocabulary. Template: /workspace/@alkdev/alktunnels/src/params.rs (the sibling's spec builder + scope-constant posture).

Deliverables:

  1. src/params.rs:
    • Constants: OP_SOCKS5_OPEN = "channels/socks5/sub", SOCKS5_ALPN = "alk/socks5", SOCKS5_OPEN_SCOPE = "socks5:open", SOCKS5_BIND_SCOPE = "socks5:bind" (ADR-001/ ADR-006; the scope-string convention <alpn-family>: <capability>; stable once published).
    • socks5_open_spec() -> OperationSpec: Sub-typed, external, channel-open marker on SOCKS5_ALPN, trivial params: input schema {type: object}, additionalProperties: false (empty params stay valid forever, by ADR-001's additive-only extension rule); reply schema {"channel_id": integer, minimum 1}; required_scopes = ["socks5:open"]. Follow the alktunnels spec-builder shape exactly (the registry validates against these schemas).
    • The trivial params struct (Socks5Params {}, with serde deny_unknown_fields, empty) for the consumer's open call.
  2. src/error.rs: the typed open-error surface (alkcall ADR-049 §4):
    • SocksOpenError: the consumer-side open-failure vocabulary mapping the channel:open_failed reasons (dial_failed / unknown_resource / resource_shortage / handler_error / timeout) plus the registry's FORBIDDEN/INVALID_INPUT classes, shaped on alktunnels' TunnelOpenError + establishment_reason helper.
    • The session-level error type for the consumer sessions (SocksSessionError-shaped: open errors vs in-stream SocksError vs I/O). The exact variant set is the task's judgment; keep it faithful to what the sessions actually surface (a refused session never yields a phantom channel; a refused target is an RFC reply code surfaced through the session type, per consumer.md §Error surface).
  3. lib.rs re-exports for the new public surface (convention 15).

Tests: spec-builder shape assertions (op id, ALPN marker, scopes, schemas; the trivial-params schema rejects unknown fields), params serde round-trip (empty object valid; unknown field rejected), the error-reason mapping table.

Acceptance Criteria

  • Constants + spec builder per ADR-001/ADR-006 exactly (op id, ALPN, both scopes, trivial schemas)
  • Input schema rejects unknown fields (the loud-rejection posture)
  • Typed open-error surface maps the ADR-049 §4 reasons
  • lib.rs re-exports the public surface
  • cargo test green; clippy/fmt clean; both wasm checks clean

References

  • docs/architecture/wire.md §The Open Op (the normative op shape)
  • docs/architecture/decisions/001-alpn-and-open-op.md (trivial params; the one-way door)
  • docs/architecture/decisions/006-access-control.md (the two scopes)
  • docs/architecture/decisions/011-inline-vendor.md §4 (the typed open-error surface)
  • Template: /workspace/@alkdev/alktunnels/src/params.rs (tunnel_open_spec, the scope constants, establishment_reason)
  • POC sketch: /workspace/alksocks-connect-poc/src/params.rs

Notes

Filled by the implementation agent (2026-09-23).

  • src/params.rs: the four wire-stable constants (OP_SOCKS5_OPEN, SOCKS5_ALPN, SOCKS5_OPEN_SCOPE, SOCKS5_BIND_SCOPE), the trivial-params struct Socks5Params {} (serde deny_unknown_fields, + Serialize so tests round-trip it; as_json() names the canonical wire form), socks5_open_spec() (Sub/External/channel-open marker on alk/socks5; input schema {type: object, additionalProperties: false}; reply schema channel_id integer minimum 1 required; required_scopes = ["socks5:open"]), and the establishment_reasonhelper re-export (the alktunnels posture, which delegates toChannelOpenError::establishment_reason`).
  • src/error.rs: SocksOpenError (single Open variant wrapping ChannelOpenError, the same shape as alktunnels' TunnelOpenError; the From impl covers the channel:open_failed/FORBIDDEN/INVALID_INPUT classes), the typed SocksEstablishmentReason vocabulary (ADR-049 §3's five codes, parse/as_str; unknown codes are None, never silently aliased, since the vocabulary is one-way), SocksOpenError::reason() as the typed view, and SocksSessionError (open errors vs in-stream Rfc(SocksError) vs Io; the RFC reply-code refusals ride the session type per consumer.md §Error surface). SocksError/ SocksServerError/ReplyError re-exported from rfc1928 (SocksServerError lives in rfc1928::server, not the module root).
  • lib.rs re-exports both modules' public surface (convention 15).
  • Deliberate deviations from the task text: the params struct derives Serialize (test round-trip needs it; harmless on the wire, since the consumer sends json!({})); SocksEstablishmentReason is a separate named type rather than an inline match so the mapping table is testable in both directions; unknown reason codes do NOT alias a variant (one-way vocabulary discipline).
  • rustdoc links to error::SocksOpenError from params are fully-qualified (crate::error::…); plain [SocksOpenError] does not resolve cross-module in this crate's layout.

Summary

Filled on completion (2026-09-23).

Implemented the wire-stable open-op surface and the typed open-error vocabulary:

  • params: constants + socks5_open_spec() per ADR-001/ADR-006 exactly (op id channels/socks5/sub, ALPN alk/socks5, scopes socks5:open/socks5:bind, trivial empty-object input schema with loud additionalProperties: false, reply {channel_id: integer, minimum 1} required).
  • Socks5Params {} with deny_unknown_fields: empty object valid, unknown fields rejected.
  • error: SocksOpenError mapping the ADR-049 §4 reasons (dial_failed/unknown_resource/resource_shortage/ handler_error/timeout) through the typed SocksEstablishmentReason (unknown codes never aliased); the registry's FORBIDDEN/INVALID_INPUT classes correctly non-establishment; SocksSessionError shaped open-vs-Rfc-vs-Io per consumer.md §Error surface.
  • 9 new tests (spec shape, constants, serde round-trip + unknown-field rejection, the reason mapping table, non-establishment classes, future-reason non-aliasing, the three-plane session surface).
  • lib.rs re-exports the public surface.

Verification: cargo test (104 green: 67 lib + 37 rfc1928 + 0 doc), cargo clippy --all-targets -- -D warnings clean, cargo fmt --check clean, cargo doc --no-deps zero warnings, both wasm checks (cargo check + cargo clippy on wasm32-unknown-unknown) clean.