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

152 lines
13 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
id: w5-decline-matrix
name: "Wave 5: the decline-matrix contract suite (every build × every row × both directions)"
status: completed
depends_on: [review-wave-4]
scope: moderate
risk: low
impact: phase
level: 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.