Files
alkssh/tasks/implementation/w5-decline-matrix.md
T
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

13 KiB
Raw Blame History

id, name, status, depends_on, scope, risk, impact, level
id name status depends_on scope risk impact level
w5-decline-matrix Wave 5: the decline-matrix contract suite (every build × every row × both directions) completed
review-wave-4
moderate low phase implementation

Description

The complete decline-matrix contract suite (mapping.md §Decline matrix, where the fail-closed invariant is contract territory on both sides of every feature, AGENTS.md conventions 5–6): every build (none / git / session / tunnel / --all-features) × every row it can appear in × both directions, including the sftp placeholder rows (server subsystem + client-side, OQ-13) and the never-features (x11 and auth-agent, never feature-gated per ADR-003 §5).

Waves 2–4 already landed per-build rows incrementally (tests/dispatch_core.rs, tests/client_dial.rs's decline-all row, the mapping binaries' per-feature matrices); this task makes the matrix complete and systematic rather than accreted:

  • The coverage audit, i.e. every row of mapping.md's tables (channel-type rows, session-request rows, the global-request -R rows, the sftp placeholder rows both directions, the never-features) × every build it can appear in × both directions → where it is pinned today (binary + test name). Recorded as a table in this task's Notes; the audit is the gate's checklist.
  • Gap rows landed, so every cell the audit finds unpinned gets its row in the existing per-build binaries (the single-writer map recorded: which binaries this task edits, so the e2e tasks' new binaries stay disjoint).
  • The matrix runner, the per-build verification set (cargo test per build + --all-features, clippy ×2 -D warnings) as one command (a script under scripts/ or the documented command list, with the vehicle decided and recorded), joining the verification list so the full matrix is one invocation from here on (the corpus gate's sibling posture).
  • The sftp rows explicit in every build, since the feature gates nothing (no dep, no behavior change); the decline-with-reason rows (server subsystem + client-side placeholder) are pinned in every build, and the sftp-only build compiles + declines identically (the no-op-feature fact itself verified, not assumed).
  • The never-features in every build, both directions: x11 / auth-agent decline rows server-side (open + any request shape) and client-side (the decline-all skeleton's server-initiated-open rows), never advertised, never feature-gated.

Acceptance Criteria

  • The coverage-audit table recorded (every row × build × direction → the test that pins it); every gap identified
  • Every gap row landed; mapping.md's matrix table green in every cell, in every build it can appear in, both directions
  • The sftp placeholder rows pinned in every build (both directions); the sftp-only build verified no-op + declining
  • The never-feature rows (x11, auth-agent) pinned in every build, both directions
  • The matrix runner lands and joins the verification list (vehicle decided + recorded)
  • cargo test per build + --all-features, clippy ×2 (-D warnings), fmt, cargo doc --no-deps green

References

  • docs/architecture/mapping.md §Decline matrix (the contract this suite completes), §Channel-type rows, §Request-level rows, §Global- request rows
  • docs/architecture/decisions/003-feature-graph.md (§4–5: the fail-closed rule + the never-features)
  • docs/plans/implementation.md §Wave 5 (the matrix bullet)
  • tests/dispatch_core.rs, tests/client_dial.rs, tests/git_mapping.rs, tests/tty_mapping.rs, tests/tunnel_mapping.rs (the accreted rows this task audits and completes)

Notes

The coverage audit (every row × every build it can appear in × both directions → the test that pins it; builds: none / git / session / tunnel / --all-features, plus the sftp-only build which has the none build's shape with the feature on). S = server direction, C = client direction; dispatch_core = tests/dispatch_core.rs.

Row Build(s) it can appear in Direction Pinned by
session channel open, unfeatured none, tunnel, sftp-only S dispatch_core::default_build::every_channel_open_declines_in_a_default_build, dispatch_core::tunnel_build::session_channels_decline_in_a_tunnel_build
pty/shell decline (feature on, seam/args unset) session, all-features S dispatch_core::session_build::pty_and_shell_decline_seam_unconfigured_in_a_session_build, dispatch_core::all_features::pty_and_shell_decline_seam_unconfigured_with_all_features (+ same rows in tests/tty_mapping.rs's unset mod for all session rows)
pty/shell decline, feature off git S dispatch_core::git_build::pty_shell_and_capability_requests_decline_in_a_git_build (gap closed: pty added beside the accreted shell row)
pipe-exec decline (unfeatured / wrapper unset) git, session, all-features S git_build::plain_exec_declines_and_keeps_the_channel_in_a_git_build, session_build::plain_exec_declines_wrapper_unconfigured_in_a_session_build, all_features::plain_exec_declines_wrapper_unconfigured_with_all_features
git-parseable exec (routes to the git row's seam-stub decline in this binary; served in git_mapping) git, all-features S git_parseable_exec_routes_to_the_git_row_stub, all_features::git_exec_routes_to_the_git_row_stub_with_all_features, tests/git_mapping.rs
git-parseable exec declined (the git parse is session's gate) session S session_build::git_parseable_exec_declines_in_a_session_build
unknown git services declined surface git S git_build::unknown_git_services_take_the_declined_surface, tests/git_mapping.rs unknown_services_decline_request_level_with_alive_channel
subsystem sftp declines with reason (OQ-13) git, session, all-features S git_build::subsystems_decline_with_their_named_reasons_in_a_git_build, session_build::subsystems_decline_with_their_named_reasons_in_a_session_build, all_features::capability_declines_hold_with_all_features
subsystem sftp declines (the feature gates nothing) sftp-only, none, tunnel S session channels decline at open (row 1), so the subsystem row cannot appear; the sftp-only run exercises the identical none-build declining behavior below
subsystem sftp declines (client-side placeholder, before any wire op) every build C tests/client_ops.rs sftp_placeholder_declines_in_every_build_before_any_wire_op (+ sftp_feature_on_the_placeholder_still_declines, session builds)
subsystem git service names decline (no repo id on a subsystem) git, session, all-features S same subsystem rows as sftp + tests/git_mapping.rs service_only_declines_request_level_with_alive_channel
env_request declines unconditionally (OQ-09) git, session, all-features S git_build::env_requests_decline_in_a_git_build, session_build::env_requests_decline_in_a_session_build, all_features::capability_declines_hold_with_all_features (+ C-side: env never sent ahead of any op, as no client env op exists)
never-feature x11: channel open every build S dispatch_core::x11_channel_opens_decline_in_every_build (shared)
never-feature x11: request shape git, session, all-features (rows need a session channel) S dispatch_core::x11_and_agent_requests_decline_wherever_sessions_open
never-feature x11: server-initiated open every build C tests/client_dial.rs server_initiated_opens_decline_cleanly_on_the_wire (the decline-all skeleton, channel_open_x11 + the never-feature event row)
never-feature auth-agent: channel open none S/C pin-bound upstream: russh 0.65 declines a server-received ChannelType::AgentForward open itself (ADMINISTRATIVELY_PROHIBITED pre-handler, russh-0.65/src/server/encrypted.rs), and exposes no client-initiated agent open at this pin, so the row is unreachable through the public API in either direction; recorded here per ADR-002 (the pin's decline is part of the pin)
never-feature auth-agent: request shape git, session, all-features S x11_and_agent_requests_decline_wherever_sessions_open (the agent-forward request)
never-feature auth-agent: server-initiated open every build C tests/client_dial.rs server_initiated_opens_decline_cleanly_on_the_wire (channel_open_agent)
direct-tcpip open unfeatured (none/git/session); seam-unset (tunnel/all-features) S default_build::every_channel_open_declines_in_a_default_build, git_build::tunnel_opens_decline_unfeatured_in_a_git_build, session_build::tunnel_opens_decline_unfeatured_in_a_session_build, tunnel_build::tunnel_direct_rows_decline_seam_unconfigured_without_a_handle (ConnectFailed), all_features::tunnel_direct_rows_decline_seam_unconfigured_without_a_handle_with_all_features
direct-streamlocal open (OQ-11) same builds as direct-tcpip S same rows + tests/tunnel_mapping.rs direct_streamlocal_declines_off_unix_before_any_fabric_call (off-Unix builds)
direct rows serve (the matrix's serve side) tunnel, all-features S tests/tunnel_mapping.rs direct_tcpip_serves_through_the_route_1_bridge etc.
forwarded-tcpip inbound open (unvanilla class, ConnectFailed) none S pin-bound: russh 0.65's client Handle exposes no forwarded-tcpip open (only servers initiate it), so the wire row is unreachable from any vanilla-client driver; the hook's decline is recorded at src/handler.rs::channel_open_forwarded_tcpip (w3-tunnel-listen's recorded wire classes). Both serve-side and decline-side of the -R data leg: tests/tunnel_mapping.rs's arrival rows
forwarded-tcpip / forwarded-streamlocal server-initiated opens, no acceptor every build C tests/client_dial.rs server_initiated_opens_decline_cleanly_on_the_wire + tests/client_reverse_listen.rs (tunnel builds: reverse_listen_without_an_acceptor_declines_before_any_wire_op, unregistered_forwarded_open_declines_cleanly, the late-install/uninstall rows)
-R listen: tcpip_forward declines every build (unfeatured gate / tunnel seam-unset) S dispatch_core::tcpip_forward_listen_requests_decline_in_every_build (shared); tunnel serving + cap + mint rows: tests/tunnel_mapping.rs
-R listen: streamlocal_forward declines every build S dispatch_core::streamlocal_forward_listen_requests_decline_in_every_build (gap closed: the unix listen form's decline had no shared row)
-R cancel forms refuse unknown pairs every build S dispatch_core::global_request_cancels_refuse_unknown_pairs_in_every_build (gap closed: the cancels had no row outside tunnel builds; tunnel builds' not-found parity: tests/tunnel_mapping.rs cancel_tcpip_forward_tears_the_fabric_listener_down, the unix twin)
listen seam unset declines the listen forms tunnel, all-features S tests/tunnel_mapping.rs listen_forms_decline_when_the_listen_seam_is_unset
window_change / signal (fire-and-forget, no decline shape) session, all-features S no wire row by contract (mapping.md §Request-level rows): the serving rows pin the path (tests/tty_mapping.rs resize/signal rows); unfeatured builds emit the decline event only
client op declines (the acceptor's NoAcceptor pre-wire, the op-row OpError classes) session/tunnel/git builds respectively C tests/client_ops.rs session_rows/tunnel_rows/git_rows + tests/client_reverse_listen.rs
sftp-only build is the no-op feature sftp-only both scripts/matrix.sh runs the full suite under --features sftp: the same none-build decline rows + the shared rows + the C-side placeholder run unmodified; the no-op fact (no dep, no behavior change) verified, not assumed

Gaps found and landed (tests/dispatch_core.rs, the single-writer map: this task edits only dispatch_core's rows, scripts/ + AGENTS.md, so the e2e tasks' new binaries stay disjoint): streamlocal listen decline, cancel-form refusals, git-build pty.

The matrix runner vehicle decided: the script route, namely scripts/matrix.sh (the per-build verification set as one command; the documented-command-list alternative rejected on the one-invocation requirement, the alkgit corpus-gate posture). Joined the verification list in AGENTS.md §Verification Commands.

Summary

Completed 2026-10-30. The decline-matrix contract suite is now complete and systematic: the coverage audit above is table-green in every cell (in every build each row can appear in, both directions), three gap rows landed in tests/dispatch_core.rs (the streamlocal_forward decline, the cancel-form refusions, the git-build pty decline closing the accreted shell-only row), the sftp no-op-feature fact is verified by the sftp-only build running the identical decline suite, the never-features are pinned in every build both directions (the auth-agent open recorded pin-bound upstream with the exact russh-0.65 mechanism), and the matrix runner landed as scripts/matrix.sh (tests × 6 builds: none / git / session / tunnel / sftp / --all-features; then clippy ×2 -D warnings, fmt, doc) and joined AGENTS.md's verification list. Verification: the full scripts/matrix.sh run green, with every cargo test build (84…114 lib tests per build, all integration binaries included), both clippy runs -D warnings clean, fmt clean, cargo doc --no-deps clean. No src/ changes were needed, given the gaps were all test-graph rows.