test(mapping): complete the decline-matrix contract suite (w5-decline-matrix)
- coverage audit recorded in the task file: every mapping.md row × every build it can appear in × both directions → the pinning test - gap rows landed in tests/dispatch_core.rs: the streamlocal_forward decline, the cancel-form refusions, the git-build pty decline - never-features pinned in every build both directions; the auth-agent open recorded pin-bound upstream (russh 0.65 declines AgentForward opens pre-handler; no client initiator at the pin) - sftp-only build verified no-op + declining via the matrix run - matrix runner landed: scripts/matrix.sh (cargo test × 6 builds, clippy ×2 -D warnings, fmt, doc), joined AGENTS.md verification list Verification: scripts/matrix.sh green end to end
This commit is contained in:
1 parent
f0c4127c28
commit
d5be18baed
4 files changed
+155
-8
No files matched your search
@@ -164,6 +164,9 @@ cargo clippy --all-targets -- -D warnings
|
||||
cargo fmt --check
|
||||
cargo doc --no-deps # if docs changed
|
||||
cargo publish --dry-run --allow-dirty # before a release
|
||||
scripts/matrix.sh # the decline-matrix runner: the full suite per
|
||||
# feature build (none / git / session / tunnel /
|
||||
# sftp / --all-features), clippy ×2, fmt, doc
|
||||
```
|
||||
|
||||
If feature flags are added, also run `cargo test --all-features` and
|
||||
|
||||
Executable
+25
@@ -0,0 +1,25 @@
|
||||
#!/usr/bin/env bash
|
||||
# The decline-matrix runner (tasks/implementation/w5-decline-matrix.md):
|
||||
# the per-build verification set as one command — `cargo test` per
|
||||
# feature build (none / git / session / tunnel / sftp-only /
|
||||
# --all-features; the sftp-only run is the no-op-feature fact itself
|
||||
# verified), clippy ×2 (-D warnings, with and without
|
||||
# --all-features), fmt, doc.
|
||||
set -euo pipefail
|
||||
cd "$(dirname "$0")/.."
|
||||
|
||||
default_build="none (default = [])"
|
||||
for build in "" "--features git" "--features session" "--features tunnel" "--features sftp" "--all-features"; do
|
||||
label="${build:-$default_build}"
|
||||
echo "== cargo test [$label]"
|
||||
cargo test $build
|
||||
done
|
||||
|
||||
echo "== clippy (no all-features)"
|
||||
cargo clippy --all-targets -- -D warnings
|
||||
echo "== clippy (all-features)"
|
||||
cargo clippy --all-features --all-targets -- -D warnings
|
||||
echo "== fmt"
|
||||
cargo fmt --check
|
||||
echo "== doc"
|
||||
cargo doc --no-deps
|
||||
@@ -1,7 +1,7 @@
|
||||
---
|
||||
id: w5-decline-matrix
|
||||
name: Wave 5 — the decline-matrix contract suite (every build × every row × both directions)
|
||||
status: pending
|
||||
status: completed
|
||||
depends_on: [review-wave-4]
|
||||
scope: moderate
|
||||
risk: low
|
||||
@@ -79,8 +79,72 @@ matrix **complete and systematic** rather than accreted:
|
||||
|
||||
## Notes
|
||||
|
||||
> To be filled by implementation agent
|
||||
**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) — 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 — 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 | — | 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 — 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`) | — | 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 — the e2e tasks' new binaries stay disjoint): streamlocal
|
||||
listen decline, cancel-form refusals, git-build pty.
|
||||
|
||||
**The matrix runner vehicle decided**: the script route —
|
||||
`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
|
||||
|
||||
> To be filled on completion
|
||||
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 — 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 — the gaps were all test-graph rows.
|
||||
+60
-5
@@ -22,8 +22,9 @@ mod common;
|
||||
use std::sync::Arc;
|
||||
|
||||
use common::harness::{
|
||||
LoopbackSession, adapter, authenticate_publickey, await_close, drive, next_message,
|
||||
open_session_channel, open_x11_channel, tcpip_forward,
|
||||
LoopbackSession, adapter, authenticate_publickey, await_close, cancel_streamlocal_forward,
|
||||
cancel_tcpip_forward, drive, next_message, open_session_channel, open_x11_channel,
|
||||
streamlocal_forward, tcpip_forward,
|
||||
};
|
||||
use common::{MemHostKeyPolicy, MemIdentityProvider, fixture, identity};
|
||||
use russh::client::Msg;
|
||||
@@ -135,6 +136,49 @@ async fn tcpip_forward_listen_requests_decline_in_every_build() {
|
||||
session.unmount().await;
|
||||
}
|
||||
|
||||
/// - *Contract stamp:* mapping.md §Global-request rows (the
|
||||
/// `streamlocal-forward` unix listen form is the `tunnel` row's
|
||||
/// other half: unfeatured builds decline before any fabric call,
|
||||
/// a `tunnel` build declines on the unset listen seam — the same
|
||||
/// refused-global-request wire shape both ways).
|
||||
#[tokio::test]
|
||||
async fn streamlocal_forward_listen_requests_decline_in_every_build() {
|
||||
let session = authenticated().await;
|
||||
let err = streamlocal_forward(&session.client, "/run/alkssh-matrix.sock")
|
||||
.await
|
||||
.expect_err("the unix listen request declines");
|
||||
assert!(
|
||||
matches!(err, Error::RequestDenied),
|
||||
"expected the declined unix listen's RequestDenied, got {err:?}"
|
||||
);
|
||||
session.unmount().await;
|
||||
}
|
||||
|
||||
/// - *Contract stamp:* mapping.md §Global-request rows (the cancel
|
||||
/// forms: `false` for an unknown pair — the sshd not-found parity;
|
||||
/// unfeatured builds decline their gate before reaching the
|
||||
/// listener map, a `tunnel` build's empty map misses the pair —
|
||||
/// the same refused-cancel wire shape both ways).
|
||||
#[tokio::test]
|
||||
async fn global_request_cancels_refuse_unknown_pairs_in_every_build() {
|
||||
let session = authenticated().await;
|
||||
let denied = cancel_tcpip_forward(&session.client, "127.0.0.1", 8080)
|
||||
.await
|
||||
.expect_err("the tcpip cancel of a no-listen pair refuses");
|
||||
assert!(
|
||||
matches!(denied, Error::RequestDenied),
|
||||
"expected the refused cancel's RequestDenied, got {denied:?}"
|
||||
);
|
||||
let denied = cancel_streamlocal_forward(&session.client, "/run/alkssh-matrix.sock")
|
||||
.await
|
||||
.expect_err("the streamlocal cancel of a no-listen pair refuses");
|
||||
assert!(
|
||||
matches!(denied, Error::RequestDenied),
|
||||
"expected the refused cancel's RequestDenied, got {denied:?}"
|
||||
);
|
||||
session.unmount().await;
|
||||
}
|
||||
|
||||
/// - *Contract stamp:* mapping.md §Decline matrix (git-build row,
|
||||
/// request side: `x11` / `auth-agent` requests decline; ADR-003 §5).
|
||||
#[cfg(any(feature = "git", feature = "session"))]
|
||||
@@ -222,8 +266,8 @@ mod default_build {
|
||||
mod git_build {
|
||||
use super::*;
|
||||
use common::harness::{
|
||||
open_direct_streamlocal, open_direct_tcpip, request_env, request_exec, request_shell,
|
||||
request_subsystem,
|
||||
open_direct_streamlocal, open_direct_tcpip, request_env, request_exec, request_pty,
|
||||
request_shell, request_subsystem,
|
||||
};
|
||||
|
||||
/// - *Contract stamp:* the task's channel-open gate (session
|
||||
@@ -309,11 +353,22 @@ mod git_build {
|
||||
}
|
||||
|
||||
/// - *Contract stamp:* mapping.md §Decline matrix (git-build row:
|
||||
/// pty/shell and all session requests decline).
|
||||
/// pty/shell and all session requests decline — the pty row
|
||||
/// declines unfeatured (`channel_failure`, the channel alive)
|
||||
/// exactly as the shell row does).
|
||||
#[tokio::test]
|
||||
async fn pty_shell_and_capability_requests_decline_in_a_git_build() {
|
||||
let session = authenticated().await;
|
||||
|
||||
let mut channel = open_session_channel(&session.client)
|
||||
.await
|
||||
.expect("the session channel opens");
|
||||
request_pty(&channel)
|
||||
.await
|
||||
.expect("the pty request rides the wire");
|
||||
expect_failure(&mut channel, "the pty row declines unfeatured").await;
|
||||
expect_channel_alive(&mut channel).await;
|
||||
|
||||
let mut channel = open_session_channel(&session.client)
|
||||
.await
|
||||
.expect("the session channel opens");
|
||||
|
||||
Reference in new issue
Block a user