Files
glm-5.3-flash f4b5f896b4 docs: sweep em dashes from all markdown prose
Renames the written-usage rule into practice across the contract surface:
docs/ (architecture, research, plans, sdd), tasks/ (all 43 task files,
taskgraph validate passes), AGENTS.md, README.md, CHANGELOG.md, and the
.opencode/agents/ prompts. Every replacement is a sense-preserving
rewrite (colons, semicolons, commas, parentheses, sentence splits, varied),
never a mechanical swap; no spaced hyphens were substituted. Enum fields,
frontmatter, tables, wire data, and cited identifiers survive verbatim;
en dashes inside numeric ranges keep their meaning.

Verification: taskgraph validate (43 tasks), repo grep clean (single
exempt hit: the verification command's own search pattern in AGENTS.md),
cargo test, clippy --all-targets -D warnings, fmt --check, doc all green.
2026-11-04 20:28:00 +00:00

9.3 KiB
Raw Permalink Blame History

id, name, status, depends_on, scope, risk, impact, level
id name status depends_on scope risk impact level
w5-e2e-client Wave 5: real-tool e2e, client direction (the door against a real sshd) completed
review-wave-4
moderate medium phase implementation

Description

The client-direction real-tool e2e, which takes the reverse-runner shape re-landed against a real sshd (docs/plans/implementation.md §Wave 5, the real-tool e2e bullet's client half; the wave-4 client rows ran against the in-process fake sshd double, and the wave-4 gate named real-sshd e2e wave-5 territory explicitly). A dedicated new binary (decide + record the placement, for which tests/e2e_client.rs reusing tests/common is the recommended posture, disjoint from the matrix task's binaries), under the check-gated skipped-with-reason posture:

  • The sshd fixture: a spawned real OpenSSH server the dial targets: host key generated (ed25519), a listen port picked, the HostKeyPolicy accepting the fixture's known fingerprint (the case-1 pin posture, with the policy seam consumed against a real presented key), a generated ed25519 user key authorized in the fixture's authorized_keys (the decrypt-at-dial path over the in-memory vault double). The fixture's exact shape (spawned per-test vs a shared per-binary instance; the sshd_config knobs, publickey-only, no password) decided + recorded.
  • The op rows against real sshd: exec (exit-status parity through a real server's reaping), the direct-tcpip dial (a real TCP connection through the server's forwarding, -L-shaped), the -R leg: tcpip_forward with the port-0 mint against a real server's actually-bound reply, the server-initiated forwarded-tcpip open served through the acceptor registry (the reverse-runner consumer pattern end to end), the cancel form tearing the real listen down.
  • The decline rows against a real server, the sftp client-side placeholder decline (subsystem sftp → the typed decline), the unfeatured direct-tcpip decline (the connection stays usable, which is the typed-open decline mirror's real-server row), the host-key policy mismatch hard-decline (a wrong-fingerprint fixture → no dial, the case-1 posture against a real presented key).
  • Every skip reasoned (no sshd in the environment / fixture constraints), every finding fixed in-task or booked for the gate, never absorbed silently.

Acceptance Criteria

  • The sshd fixture lands (spawned real server, ed25519 host + user keys, the policy pin posture); its shape decided + recorded
  • Exec, direct-tcpip, and the -R leg (port-0 mint + accepted arrival + cancel) green against real sshd (or reasoned-skipped)
  • The decline rows green: sftp placeholder, unfeatured direct-tcpip (connection stays usable), host-key mismatch hard-decline
  • Every finding fixed in-task or booked for the gate (recorded)
  • cargo test (+ the builds the new binary rides), clippy ×2 (-D warnings), fmt, cargo doc --no-deps green

References

  • docs/plans/implementation.md §Wave 5 (the real-tool e2e bullet), §Wave 4 (the fake-sshd posture this task's real server replaces for e2e)
  • docs/architecture/client.md (the dial surface, op rows, the acceptor), docs/research/reverse-runner.md (the consumer pattern the client direction serves)
  • docs/research/poc-2*.md / the reverse-runner extraction (the real-sshd client-direction evidence this re-lands against the shipped crate)
  • tests/client_dial.rs, tests/client_ops.rs, tests/client_reverse_listen.rs (the double-driven rows this task mirrors against the real server), tests/common/sshd_double.rs (the double's semantics, which the real fixture must match)

Notes

The sshd fixture decided: tests/e2e_client.rs (the new binary), reusing tests/common, and the wave-4 client binaries stay single-writer per the decomposition's file map; this task edited only its own binary (tests/e2e_client.rs, an in-file support module). Fixture shape: per-row spawned real sshd, since each row's config knobs differ (AllowTcpForwarding yes/no) and each row dials its own server, so a shared per-binary instance buys nothing; per-row isolation (own workdir, own minted ed25519 host key via ssh-keygen, own probe-verified port from the e2e-server's monotonic allocator posture) beats the spawn cost. Knob set: publickey-only, StrictModes no (the throwaway workdir carries none of the real home-dir hygiene), UsePAM no, UseDNS no, PidFile none, PermitRootLogin prohibit-password, host key + authorized_keys (fixture alpha's public blob) at absolute paths, per-row AllowTcpForwarding. Readiness is a bounded TCP probe; a server that never comes up is the row's reasoned skip with the server's own -e log tail as the reason (worked as non-root in this environment, verified against OpenSSH 9.6p1). The dial rides the decrypt-at-dial path over the in-memory vault double (VaultKeySource over TestVault, alpha sealed) and the case-1 pin posture (MemHostKeyPolicy::pin) accepts only the minted host fingerprint.

Row inventory (all check-gated, #[cfg(unix)] + feature-gated per row group; deadline-bounded end to end (the POC-1 §5 posture); Contract stamp: markers cite client.md / reverse-runner.md):

Row Build(s) What it pins
exec_row::real_sshd_exec_pumps_streams_and_reports_the_exit_status session (+all) the wave-4 double-driven exec row re-landed: stdout/stderr separated through a real server, the nonzero exit (3) reported in-band last, the real server's reaping, parity not a canned 0; the case-1 pin accept decision on record against the really-presented key
direct_tcpip_row::real_sshd_direct_tcpip_round_trips_a_live_tcp_connection tunnel (+all) the -L-shaped dial through a real server's forwarding into a live loopback echo server, bytes end to end
reverse_rows::real_sshd_port_zero_mint_serves_the_accepted_arrival_and_the_data_leg tunnel (+all) the port-0 mint surfaces the actually-bound port (verified by dialing it), the server-initiated forwarded-tcpip arrives into the installed acceptor, the external dialer's bytes round trip, giving the reverse-runner consumer pattern end to end
reverse_rows::real_sshd_cancel_tears_the_listen_down_and_unknown_cancels_refuse tunnel (+all) explicit-port listen actually bound; the listener handle's cancel() frees the real bound port (later dials refused); a stateless cancel of the unknown listener is ListenRefused; connection stays usable
declines::real_connection_sftp_placeholder_declines_with_the_connection_intact every build the OQ-13 placeholder against a real authenticated connection: typed decline before any wire op, connection intact
declines::real_presented_key_mismatch_hard_declines_and_the_right_pin_dials every build the wrong-pinned policy hard-declines the dial (DialError::HostKeyRejected), the policy log records the real fingerprint as rejected, and the correctly-pinned dial to the same server succeeds, and the decline is the policy's decision, not the server's
unfeatured_direct_decline::real_server_refused_direct_tcpip_declines_and_the_connection_stays_usable tunnel+session (all-features) a real server with AllowTcpForwarding no refuses the typed open → OpenDeclined(Prohibited), and a following exec is served, the connection-stays-usable fact end to end

Decisions recorded (for the wave-5 gate):

  • The "connection stays usable" decline row is gated tunnel and session (rides --all-features only): the post-decline exec is the stays-usable proof's vehicle, and exec is a session-gated op. Under tunnel-only builds the row is absent; the fake-double mirror (tests/client_ops.rs) carries the tunnel-only stays-usable rows.
  • The sftp and host-key-mismatch rows run in every build (feature-free ops on SshConnection), so even the no-feature matrix build exercises two real-server rows.
  • The fixture skips are honest: no sshd in the environment / no ssh-keygen / spawn refusal / never-listening (the server's -e log tail carried in the reason), never silently green.

Findings: no src/ changes needed, as every observed issue was e2e-graph local (compile/fmt-level only). No bookings for the gate beyond the decisions above.

Summary

Completed 2026-10-30. The client-direction real-tool e2e landed as tests/e2e_client.rs (7 rows, fixture shape above): exec (exit-status parity, nonzero through real reaping), the direct-tcpip dial, the -R leg (port-0 mint + accepted arrival + data leg + cancel tearing the real listen down + unknown-cancel ListenRefused), and the three decline rows (sftp placeholder, host-key mismatch hard-decline with the real presented fingerprint on record, server-refused direct-tcpip with the connection staying usable). Verification: full scripts/matrix.sh run green: cargo test per build (none / git / session / tunnel / sftp / --all-features; the new binary compiles in every build and runs only its rows' builds: 2 / 2 / 3 / 5 / 2 / 7 rows), both clippy runs -D warnings clean, fmt clean, cargo doc --no-deps clean; plus 10 consecutive all-features e2e_client runs green (no flakes; per-row workdirs + the e2e-server's port allocator carried over). Skips are reasoned prints only; on this host sshd 9.6p1 is present, so every row ran.