docs(tasks): decompose wave 4 — the client direction

Eight implementation tasks + the gate under tasks/implementation/,
folding the wave-3 gate's four booked findings in as dedicated tasks
(the gate's own disposition — wave-4 material, not wave-3 backports).

Hardening set (server-side, independent of the client chain,
scheduled first as cheap remediation):

- w4-git-exit-close: booked item 1 — the git row's status-before-close
  by construction (the seam/shape decided + recorded in-task; the
  saturated-buffer scenario pinned on the wire)
- w4-channel-not-held-rows: booked item 2 — the guard's wire rows on
  git/tty pipe/shell, per build (test-only)
- w4-tty-coverage: booked item 3 — the channel_close-fired cancel
  path + the Closed/Break no-exit-chunk rows + the write_all
  no-deadline known-limit record
- w4-router-collision: booked item 4 — the ListenRouter
  token-collision fail-closed pre-check

Client chain (sequential logistically — one client Handler impl):

- w4-dial: the russh client stack + the connection handle + the
  client Handler skeleton at fail-closed defaults, over an in-process
  fake-sshd server double
- w4-op-rows: exec/pty, direct-tcpip, git exec over the shared
  grammar, the sftp client-side decline — the POC-3 §D.3 mechanics
- w4-reverse-listen: the -R leg — forward requests + the acceptor
  registry + late-install (the wave's highest-risk task)
- w4-lifecycle: the OQ-12 primitives (cancellable handles, clean
  disconnect, no reconnect policy)

review-wave-4 depends on all eight. The gate's item 5 (upstream asks)
stays booked with its owners — no task. Plan wave-4 section carries
the decomposition record + four flagged interpretations; wave table
status updated.

Verification: taskgraph validate ✓ (35 tasks)
This commit is contained in:
glm-5.3-flash committed 2026-10-29 05:59:03 +00:00
1 parent 930d9c1996
commit dba64d65d3
10 files changed
+820 -3

No files matched your search

+60 -3
View File
@@ -1,7 +1,8 @@
---
status: draft
last_updated: 2026-10-28 (initial plan — five waves committed; wave 1
decomposed same day, see its decomposition record)
last_updated: 2026-10-29 (wave 4 decomposed after the wave-3 gate
closed green — absorbing the gate's four booked findings; waves 1–3
decomposed + implemented + gated 2026-10-28/29)
---
# alkssh — Implementation plan
@@ -46,7 +47,7 @@ mappings ∥ client direction) → validation & release.
| [1](#wave-1--foundations) | Manifest landing (russh pin, feature graph); error model; config surface (serve-config + dial-config + `Limits`); vault-backed key sources; host-key policy seam; fingerprint; test doubles | — | decomposed |
| [2](#wave-2--the-server-stack) | `SshAdapter::handle` mount + `Config` construction; the auth bridge; parse-only dispatch + fail-closed decline core; spawn-and-return plumbing; bounded resources; loopback harness | wave 1 | decomposed |
| [3](#wave-3--server-mappings) | session → alktty (translation bridge + exit derivation); git → alkgit (exec grammar + dispatch); tunnel → alktunnels route 1; sftp placeholder declines | wave 2 | decomposed |
| [4](#wave-4--client-direction) | Dial surface; op rows (exec/pty, direct-tcpip, git exec); the `-R` leg (listen + acceptor registry); lifecycle primitives | wave 1 | pending |
| [4](#wave-4--client-direction) | Dial surface; op rows (exec/pty, direct-tcpip, git exec); the `-R` leg (listen + acceptor registry); lifecycle primitives | wave 1 | decomposed |
| [5](#wave-5--validation--release) | Decline-matrix contract suite; real-tool e2e; fuzzing; docs; **the publish** | waves 3 + 4 | pending |
## Wave 1 — Foundations
@@ -385,6 +386,62 @@ wave 1 in principle, but decomposing it after wave 3's gate keeps the
session task sets in rhythm and lets it absorb any seam corrections
the mappings surfaced).
**Decomposition record** (2026-10-29, after the wave-3 gate closed
green): wave 4 decomposed into eight task files under
`tasks/implementation/` plus the gate. The wave-3 gate's four booked
findings are folded in as dedicated tasks (the gate's own
disposition — "wave-4 material"), not wave-3 backports: the
hardening set is server-side and independent of the client chain —
`w4-git-exit-close` (booked item 1: the git row's status-before-close
by construction — the seam/shape decided + recorded in-task),
`w4-channel-not-held-rows` (item 2: the guard's wire rows on all
three rows, per build — test-only), `w4-tty-coverage` (item 3: the
`channel_close`-fired cancel path + the Closed/Break no-exit-chunk
rows + the write_all no-deadline known-limit record), and
`w4-router-collision` (item 4: the `ListenRouter` token-collision
fail-closed pre-check); the gate's item 5 (the upstream asks) stays
booked with its owners — no task, nothing absorbed. The client chain
is deliberately sequential — `w4-dial` first (the russh client stack
+ the connection handle + the client Handler skeleton at fail-closed
defaults, over an in-process fake-`sshd` server double this task
lands), then `w4-op-rows` (exec/pty, direct-tcpip, git exec over the
shared grammar, the sftp client-side decline — the POC-3 §D.3
mechanics pinned), then `w4-reverse-listen` (the `-R` leg: forward
requests + the acceptor registry + late-install — the wave's
highest-risk task, growing the harness's `ForwardedAcceptor`
vocabulary into the crate's real surface), then `w4-lifecycle` (the
OQ-12 primitives); `review-wave-4` (the gate) depends on all eight.
As with waves 2–3, the chain is sequential *logistically* (the dial,
op rows, and listen leg all edit the same client Handler impl) even
where the branches are independent *conceptually*; the hardening
tasks carry no dependency on the client chain (disjoint files) and
are scheduled first as cheap remediation.
Interpretations recorded (flagged for the wave-4 gate):
- **The client Handler skeleton lands fail-closed in `w4-dial`, the
acceptor registry in `w4-reverse-listen`** — the dial task pins the
decline-all default for server-initiated opens (the ADR-003 §4
rule) and the listen task grows the registry that serves them; the
split mirrors wave 2's mount-before-mappings shape and keeps the
dial task's surface reviewable.
- **The client-direction test vehicle is an in-process fake `sshd`**
(a hand-rolled russh server double in `tests/common`) — the
loopback harness drives the *server* direction and is not the
vehicle here; real-`sshd` client-direction e2e stays wave-5
territory (the plan's wave-5 bullet), with per-task check-gated
real-tool smokes where cheap (the wave-3 smoke posture).
- **No `DialConfig` course-correction is anticipated** — the wave-1
shape already carries everything `client.md`'s dial surface names;
`w4-dial` decides + records if that proves wrong (the
`w3-seam-config` pattern), and the acceptor registry is
client-connection state, not serve-config.
- **The hardening tasks are ordered by single-writer needs, not
risk** — `w4-channel-not-held-rows` follows `w4-git-exit-close`
(both touch `tests/git_mapping.rs`) and `w4-tty-coverage` follows
the rows task (both touch `tests/tty_mapping.rs`), the wave-3
no-two-tasks-hold-one-file lesson applied to the test binaries.
## Wave 5 — Validation & release
- **The decline-matrix contract suite** (`mapping.md`'s table) — every