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:
1 parent
930d9c1996
commit
dba64d65d3
10 files changed
+820
-3
No files matched your search
@@ -0,0 +1,96 @@
|
||||
---
|
||||
id: review-wave-4
|
||||
name: Wave 4 gate — the client-direction review
|
||||
status: pending
|
||||
depends_on: [w4-dial, w4-op-rows, w4-reverse-listen, w4-lifecycle, w4-git-exit-close, w4-channel-not-held-rows, w4-tty-coverage, w4-router-collision]
|
||||
scope: narrow
|
||||
risk: low
|
||||
impact: phase
|
||||
level: review
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
The wave-4 review gate. The client direction completes the
|
||||
two-direction door — wave 5 builds the full matrix and the release on
|
||||
both directions, and the published crate freezes this surface. The
|
||||
gate reads the landed client direction against the architecture line
|
||||
by line:
|
||||
|
||||
- **client.md line-by-line** — §The dial surface (key source
|
||||
decrypt-at-dial fail-closed; the policy at `check_server_key`; the
|
||||
bounded knobs with the keepalive 15 s / 3 posture), §Op rows (each
|
||||
table row read against the landed code — the POC-3 §D.3 mechanics
|
||||
pinned in code + rows), §The acceptor (served only under a
|
||||
registered feature's acceptor; unregistered/unfeatured declined
|
||||
cleanly — never accept-and-drop; late-install across a live
|
||||
connection), §Lifecycle primitives (explicitly cancellable handles;
|
||||
`Disconnect::ByApplication`; **no reconnect/restore policy in the
|
||||
crate** — the boundary held).
|
||||
- **ADR-003 symmetry** — the same features gate both directions; the
|
||||
per-build matrices green client-side (the acceptor rows per build);
|
||||
honest advertisement unchanged; the sftp placeholder declines in
|
||||
every build both directions (OQ-13); x11/auth-agent never-features
|
||||
untouched.
|
||||
- **ADR-005 / OQ-05 / OQ-07** — decrypt-at-dial with every
|
||||
`KeySourceError` posture a no-dial outcome; the policy seam
|
||||
consumed at the hook (crate default connects to nothing; no
|
||||
accept-all built-in); the `Zeroizing` wrap posture intact.
|
||||
- **The wave-3 gate's booked items 1–4** — every one discharged or
|
||||
explicitly re-booked: the git status-before-close by construction
|
||||
(`w4-git-exit-close`), the channel-not-held wire rows
|
||||
(`w4-channel-not-held-rows`), the tty coverage minors
|
||||
(`w4-tty-coverage`), the router token-collision pre-check
|
||||
(`w4-router-collision`). Item 5 (the upstream asks) verified still
|
||||
booked with their owners — not absorbed silently.
|
||||
- **Bounded resources** — no new unbounded shape on the client
|
||||
surfaces: the acceptor registry bounded, the listener bookkeeping
|
||||
bounded, the pumps bounded (the convention-9 read over every new
|
||||
serving/pumping path).
|
||||
- **Tracing** — the uniform event vocabulary across the new
|
||||
client-side decline/serve paths; the safety posture held (no
|
||||
secrets, no key material, no username).
|
||||
- **The smokes** — the client-direction real-tool postures recorded
|
||||
honestly (required/conditionally-skipped with reason); nothing
|
||||
silently green; anything named wave-5 e2e territory named
|
||||
explicitly.
|
||||
- **Docs honesty** — module docs name what each new surface is, what
|
||||
remains wave-5 material, and any new upstream notes (the russh
|
||||
client-hook shapes at this pin; the acceptor-registry shape
|
||||
decision; the DialConfig disposition).
|
||||
|
||||
Verdict: green (wave 5 decomposes, absorbing corrections) or findings
|
||||
booked (remediation before wave 5 decomposes).
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
- [ ] Every client.md/ADR surface above read and confirmed (or
|
||||
corrected with the correction landed)
|
||||
- [ ] The client-side per-build matrices verified through the server
|
||||
double; the acceptor contract verified (registered/unregistered/
|
||||
unfeatured/late-install); the wave-3 booked items 1–4 verified
|
||||
discharged
|
||||
- [ ] Full verification set green (`cargo test`, `--all-features`,
|
||||
clippy ×2 `-D warnings`, fmt, `cargo doc --no-deps`)
|
||||
- [ ] `taskgraph validate` clean
|
||||
- [ ] Verdict recorded; wave 5 decomposition unblocked or findings
|
||||
booked
|
||||
|
||||
## References
|
||||
|
||||
- docs/plans/implementation.md (wave 4 section + this gate's role)
|
||||
- docs/architecture/client.md (the wave's contract), mapping.md (the
|
||||
server-direction mirror), overview.md, open-questions.md (OQ-05/
|
||||
07/11/12/13), ADRs 002/003/005/006
|
||||
- tasks/implementation/review-wave-3.md (the booked items this gate
|
||||
verifies discharged)
|
||||
- docs/research/reverse-runner.md (the consumer pattern the client
|
||||
direction serves), docs/research/poc-3-mapping-bakeoff.md §D.3
|
||||
|
||||
## Notes
|
||||
|
||||
> To be filled by implementation agent
|
||||
|
||||
## Summary
|
||||
|
||||
> To be filled on completion
|
||||
@@ -0,0 +1,66 @@
|
||||
---
|
||||
id: w4-channel-not-held-rows
|
||||
name: Wave 4 — channel-not-held guard wire rows (wave-3 gate booked item 2)
|
||||
status: pending
|
||||
depends_on: [w4-git-exit-close]
|
||||
scope: narrow
|
||||
risk: low
|
||||
impact: component
|
||||
level: implementation
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
Close the wave-3 gate's booked finding item 2
|
||||
(`review-wave-3` §Booked findings): the **`channel-not-held` guard
|
||||
lacks wire test rows**. The guard is a documented decline class on
|
||||
all three rows (git / tty pipe / shell) — a second exec (or request)
|
||||
on an already-served channel — constructible on the wire, but no row
|
||||
asserts it in any build. `mapping.md`'s every-row-tested-in-every-build
|
||||
convention names it contract territory; the fail-closed invariant is
|
||||
contract on both sides of every feature (AGENTS.md convention 5).
|
||||
|
||||
**Test-only task** — no production code change expected (if landing
|
||||
the rows exposes a guard gap, stop and book it for the gate rather
|
||||
than silently widening scope):
|
||||
|
||||
- Wire rows through the harness, **per build** (the per-build
|
||||
cfg-pinned module convention): a second exec on a served git
|
||||
channel → the channel-not-held decline; the same on the tty pipe
|
||||
row and the shell row — the decline class asserted in every build
|
||||
that carries the row (featured builds carry the serving rows; the
|
||||
decline-class rows ride the same per-build matrix the dispatch
|
||||
tests use).
|
||||
- The rows observe the decline's wire shape + the tracing event
|
||||
vocabulary (the `w3-decline-tracing` class/reason fields — the
|
||||
rows double as the vocabulary's coverage for this class).
|
||||
- Depends on `w4-git-exit-close` only to keep `tests/git_mapping.rs`
|
||||
single-writer (the hardening lands first; these rows land into the
|
||||
settled file).
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
- [ ] Wire rows for the channel-not-held decline class on all three
|
||||
rows (git / tty pipe / shell), per build, through the harness
|
||||
- [ ] The rows assert the wire shape + the tracing event class/reason
|
||||
- [ ] No production code change (or a stop-and-booked finding
|
||||
recorded for the gate — nothing silent)
|
||||
- [ ] `cargo test` (+ per-feature + `--all-features`), clippy ×2
|
||||
(`-D warnings`), fmt, `cargo doc --no-deps` pass
|
||||
|
||||
## References
|
||||
|
||||
- tasks/implementation/review-wave-3.md §Booked findings item 2
|
||||
- docs/architecture/mapping.md (the request-level table — the
|
||||
channel-not-held class; the every-row-tested convention)
|
||||
- tests/git_mapping.rs, tests/tty_mapping.rs, tests/dispatch_core.rs
|
||||
(the per-build matrix convention the rows join), src/observe.rs
|
||||
(the event vocabulary the rows assert)
|
||||
|
||||
## Notes
|
||||
|
||||
> To be filled by implementation agent
|
||||
|
||||
## Summary
|
||||
|
||||
> To be filled on completion
|
||||
@@ -0,0 +1,111 @@
|
||||
---
|
||||
id: w4-dial
|
||||
name: Wave 4 — the client dial surface (russh client stack + the connection handle)
|
||||
status: pending
|
||||
depends_on: [review-wave-3]
|
||||
scope: moderate
|
||||
risk: medium
|
||||
impact: phase
|
||||
level: implementation
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
Land the client direction's substrate — the door outward
|
||||
(`client.md` §The dial surface): the russh client stack every wave-4
|
||||
op row composes into, mirroring `w2-server-mount`'s role for the
|
||||
server direction. Everything later in the wave rides the authenticated
|
||||
connection handle this task lands.
|
||||
|
||||
- **The dial surface** — `connect`-shaped entry taking the wave-1
|
||||
[`DialConfig`](the address, the key source, the host-key policy, the
|
||||
`Limits`): the russh client `Config` built from the bounded knobs
|
||||
(keepalive 15 s / 3 default posture per the reverse-runner evidence —
|
||||
`client.md` §Correctness facts; inactivity; window/packet knobs per
|
||||
`overview.md`'s table), the dial target as `DialConfig.address`
|
||||
carries it. If the dial surface needs a `DialConfig` amendment,
|
||||
land + record it here (the `w3-seam-config` course-correction
|
||||
pattern) — the wave-1 shape is expected to hold; decide and record.
|
||||
- **Key source decrypt-at-dial** (ADR-005) — the [`KeySource`] load
|
||||
happens at dial time; locked / absent / decryption-failure /
|
||||
unknown-`key_version` are all **no-dial outcomes** with their
|
||||
distinct `KeySourceError` variants surfaced (never env vars, never
|
||||
plaintext at rest; the adapter-side `Zeroizing` wrap per OQ-07
|
||||
already landed in wave 1 — consumed here).
|
||||
- **Host-key policy at `check_server_key`** (OQ-05) — the wave-1
|
||||
[`HostKeyPolicy`] seam consumed at russh's client hook (the verdict
|
||||
type was pinned against this hook shape in `w1-host-key-policy`):
|
||||
the crate default ([`RejectAll`]) connects to nothing; case-1 pins
|
||||
hard-decline on mismatch; case-2 assembly policies decide. The
|
||||
crate is policy-blind — no accept-all built-in.
|
||||
- **The client Handler skeleton at fail-closed defaults** — the
|
||||
russh `client::Handler` impl the dial constructs: server-initiated
|
||||
channel opens (`server_channel_open_forwarded_tcpip` /
|
||||
`_forwarded_streamlocal` / `_unknown` / x11 / agent) **declined
|
||||
cleanly** — never russh's accept-and-drop default (ADR-003 §4, the
|
||||
client-side symmetry rule; the acceptor registry that serves them
|
||||
is `w4-reverse-listen`'s surface, this task pins the default).
|
||||
- **The authenticated connection handle** — the type everything else
|
||||
rides: the russh `client::Handle` wrapped with the crate's surface
|
||||
(publickey-only authentication from the key source — the family
|
||||
contract; no password/keyboard-interactive paths), the policy and
|
||||
limits carried, the lifecycle primitives' raw material (full
|
||||
cancellation/clean-close shape is `w4-lifecycle`'s).
|
||||
- **Test vehicle** — the client direction dials *vanilla* servers, so
|
||||
the loopback harness (alkssh server) is not the vehicle: land an
|
||||
in-process russh **server double** (a fake `sshd` — hand-rolled
|
||||
`russh::server` handler in `tests/common`) as the contract-test
|
||||
vehicle, decide + record its placement and shape. Contract rows:
|
||||
dial success (authenticated), each key-source failure posture
|
||||
(locked/absent/decryption), policy-decline rows (default-reject,
|
||||
pin-mismatch hard decline, case-2 accept), the bounded-config
|
||||
wiring observable, server-initiated open declined cleanly.
|
||||
- **The smoke posture** decided + recorded (an alkssh-client ↔
|
||||
alkssh-server in-process loopback is the cheap real-stack smoke;
|
||||
a real-`sshd` dial is wave-5 e2e territory — never silently
|
||||
dropped).
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
- [ ] The dial surface lands over `DialConfig` (+ any amendment
|
||||
decided and recorded); the russh client `Config` carries the
|
||||
bounded knobs (keepalive 15 s / 3 posture, inactivity, window)
|
||||
- [ ] Key source decrypt-at-dial; every `KeySourceError` failure
|
||||
posture is a no-dial outcome with its distinct variant surfaced
|
||||
- [ ] `check_server_key` calls the injected policy; `RejectAll`
|
||||
connects to nothing; pin-mismatch hard-declines; no accept-all
|
||||
built-in
|
||||
- [ ] The client Handler skeleton declines server-initiated opens
|
||||
cleanly (fail-closed default pinned by wire rows)
|
||||
- [ ] The authenticated connection handle lands; authentication is
|
||||
publickey-only from the key source
|
||||
- [ ] The in-process server double lands in `tests/common`; the
|
||||
contract rows above green; the smoke posture decided + recorded
|
||||
- [ ] `cargo test` (+ per-feature + `--all-features`), clippy ×2
|
||||
(`-D warnings`), fmt, `cargo doc --no-deps` pass
|
||||
|
||||
## References
|
||||
|
||||
- docs/architecture/client.md §The dial surface, §Correctness facts
|
||||
(the POC-recorded mechanics), §What the crate does *not* include
|
||||
- docs/architecture/decisions/002-russh-pin.md (the client russh
|
||||
surface), docs/architecture/decisions/003-feature-graph.md (§4 the
|
||||
client-side fail-closed acceptor default this skeleton pins),
|
||||
docs/architecture/decisions/005-key-sources.md (decrypt-at-dial)
|
||||
- docs/research/russh.md §Client surface (`client::Handler` inventory
|
||||
— `check_server_key` defaults to refuse; the
|
||||
`server_channel_open_*` hook set; `client::Config` knobs),
|
||||
docs/research/reverse-runner.md (lessons 3/5 — keepalives
|
||||
load-bearing, the ephemeral-fleet policy posture)
|
||||
- docs/research/poc-1-auth-bridge.md §6/§7 (the client-side plumbing
|
||||
facts), docs/research/poc-2-vault-keys.md (the dial lifecycle)
|
||||
- src/config.rs (`DialConfig`), src/host_key.rs, src/key_source.rs
|
||||
(the wave-1 seams consumed)
|
||||
|
||||
## Notes
|
||||
|
||||
> To be filled by implementation agent
|
||||
|
||||
## Summary
|
||||
|
||||
> To be filled on completion
|
||||
@@ -0,0 +1,78 @@
|
||||
---
|
||||
id: w4-git-exit-close
|
||||
name: Wave 4 — git row hardening (status-before-close by construction — wave-3 gate booked item 1)
|
||||
status: pending
|
||||
depends_on: [w3-git-mapping]
|
||||
scope: narrow
|
||||
risk: medium
|
||||
impact: component
|
||||
level: implementation
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
Close the wave-3 gate's only structural booked finding
|
||||
(`review-wave-3` §Booked findings item 1): the git row's
|
||||
status-before-close is honored **by scheduling, not by construction**.
|
||||
Today `serve_duplex_resolved` splits the channel stream internally and
|
||||
its session halves drop when the serve returns — russh's
|
||||
`ChannelCloseOnDrop` rx-half emits a server→client `CHANNEL_CLOSE`
|
||||
*inside* the seam call, before `serve_git_row`'s explicit
|
||||
`exit_status_request`. The ordering currently holds (the task's
|
||||
continuation queues the report in the same poll, ahead of the drop's
|
||||
send) and every wire row passes, but a full `Sender<Msg>` buffer at
|
||||
the serve tail could order the implicit close first and silently drop
|
||||
the report (russh no-ops `exit_status_request` for an already-closed
|
||||
channel) — a silent exit-report loss on a published row.
|
||||
|
||||
The fix wants a seam/shape change — **decide + record which**:
|
||||
|
||||
- split the channel halves in `serve_git_row` and pass them into the
|
||||
seam (the report then rides a half the seam never closes), or
|
||||
- queue the exit report while the stream is alive (the report's send
|
||||
ordered before any close-contracting drop by construction), or
|
||||
- another shape that makes the ordering structural rather than
|
||||
scheduler-luck — the gate's reasoning and the two named options are
|
||||
the starting points, not the bounds.
|
||||
|
||||
Constraints: every existing git wire row stays green (no behavior
|
||||
change on the happy path — this is ordering hardening, not semantics
|
||||
change); the spawn-and-return constraint (ADR-006 decision 4) holds —
|
||||
the reporting still rides the spawned task; the tty rows are NOT in
|
||||
this class (their report precedes any close-contracting drop in one
|
||||
task — the gate verified; don't touch them). New wire row(s) pinning
|
||||
the construction: a saturated `Sender<Msg>` buffer at the serve tail
|
||||
(the failure scenario made real) still delivers the exit report —
|
||||
the by-construction property proven on the wire, not by reasoning.
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
- [ ] The shape decided + recorded (module docs carry the reasoning);
|
||||
the git row's status-before-close holds by construction, not
|
||||
scheduling
|
||||
- [ ] Every existing git wire row green (no happy-path behavior
|
||||
change); the spawn-and-return constraint intact
|
||||
- [ ] New wire row(s): the saturated-buffer scenario delivers the
|
||||
exit report (the by-construction property pinned)
|
||||
- [ ] `cargo test` (+ per-feature + `--all-features`), clippy ×2
|
||||
(`-D warnings`), fmt, `cargo doc --no-deps` pass
|
||||
|
||||
## References
|
||||
|
||||
- tasks/implementation/review-wave-3.md §Booked findings item 1 (the
|
||||
full finding + the two named fix shapes)
|
||||
- docs/architecture/mapping.md (the git exec row — exit-status both
|
||||
directions), docs/architecture/decisions/006-mapping-bridge-and-dispatch.md
|
||||
(decision 4 — spawn-and-return)
|
||||
- src/git_map.rs, src/serve.rs (`report_exit_status_then_close` — the
|
||||
wave-2 reporting mechanics this hardening composes with), alkgit
|
||||
0.1.0 `serve_duplex_resolved` (the seam whose half-ownership is the
|
||||
question)
|
||||
|
||||
## Notes
|
||||
|
||||
> To be filled by implementation agent
|
||||
|
||||
## Summary
|
||||
|
||||
> To be filled on completion
|
||||
@@ -0,0 +1,69 @@
|
||||
---
|
||||
id: w4-lifecycle
|
||||
name: Wave 4 — lifecycle primitives (cancellable handles + clean disconnect)
|
||||
status: pending
|
||||
depends_on: [w4-op-rows, w4-reverse-listen]
|
||||
scope: narrow
|
||||
risk: low
|
||||
impact: component
|
||||
level: implementation
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
Land the OQ-12 resolution in code (`client.md` §Lifecycle
|
||||
primitives): the primitives that make assembly-side reconnect/restore
|
||||
policy *possible* — and nothing more:
|
||||
|
||||
- **Explicitly cancellable handles** — every listener/handle the
|
||||
client direction returns (the `-R` listener handles from
|
||||
`w4-reverse-listen`, the connection handle from `w4-dial`) carries
|
||||
an explicit cancellation/clean-close path; no handle relies on drop
|
||||
timing alone for wire-visible state.
|
||||
- **Clean disconnect** — `Disconnect::ByApplication` (russh's client
|
||||
`Handle::disconnect`): the explicit-close path leaves no half-open
|
||||
channels, observed clean by the peer (row-pinned through the
|
||||
server double).
|
||||
- **No reconnect or restore policy owned by the crate** — the
|
||||
module-doc posture: reconnect-and-restore (rebinding persisted
|
||||
tunnel ports — the alkminer flow, reverse-runner fact 4) is
|
||||
assembly policy; the crate provides bound ports, handles,
|
||||
cancellation, clean close. The docs state the boundary explicitly
|
||||
so no later task drifts into baking policy in.
|
||||
- **Contract rows through the server double** — cancel a listener
|
||||
mid-connection (the server's forward is torn down, the bound port
|
||||
released); clean disconnect observed by the peer; the
|
||||
connection-handle cancellation path; the no-policy posture is a
|
||||
docs-honesty item for the gate, not a row.
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
- [ ] Every client-direction listener/handle is explicitly
|
||||
cancellable; no drop-timing-only wire state
|
||||
- [ ] `Disconnect::ByApplication` clean-close lands; the peer
|
||||
observes a clean disconnect, no half-open channels (row-pinned)
|
||||
- [ ] The no-reconnect-policy boundary stated in module docs (the
|
||||
primitives-not-policy posture)
|
||||
- [ ] Contract rows green through the server double
|
||||
- [ ] `cargo test` (+ per-feature + `--all-features`), clippy ×2
|
||||
(`-D warnings`), fmt, `cargo doc --no-deps` pass
|
||||
|
||||
## References
|
||||
|
||||
- docs/architecture/client.md §Lifecycle primitives (the OQ-12
|
||||
resolution), docs/architecture/open-questions.md (OQ-12 — resolved:
|
||||
primitives only)
|
||||
- docs/research/reverse-runner.md (fact 4 — reconnect-and-restore is
|
||||
assembly policy, kept in view so the crate never bakes it in)
|
||||
- docs/research/russh.md §Client surface (`Handle::disconnect`, the
|
||||
forward cancel forms)
|
||||
- src/tunnel_map.rs (the server-side teardown posture — the cancel
|
||||
semantics mirror)
|
||||
|
||||
## Notes
|
||||
|
||||
> To be filled by implementation agent
|
||||
|
||||
## Summary
|
||||
|
||||
> To be filled on completion
|
||||
@@ -0,0 +1,105 @@
|
||||
---
|
||||
id: w4-op-rows
|
||||
name: Wave 4 — the client op rows (exec/pty, direct-tcpip, git exec, sftp decline)
|
||||
status: pending
|
||||
depends_on: [w4-dial]
|
||||
scope: moderate
|
||||
risk: medium
|
||||
impact: phase
|
||||
level: implementation
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
Land the op rows — family op → vanilla translation per
|
||||
`client.md`'s table, pointed upstream at a vanilla server (the
|
||||
reverse-runner shape: the hub drives every channel open). Type
|
||||
symmetry at the seam is the contract: family-protocol types in,
|
||||
vanilla wire ops out, no vanilla-concept leaks up. The POC-3 §D.3
|
||||
mechanics are the body of knowledge this task lands in code:
|
||||
|
||||
- **Session exec/pty rows** (under `session`) — `channel_open_session`
|
||||
+ the exec/pty-req/shell-req translations onto the alktty session
|
||||
types (pipe vs PTY; exit semantics): `Channel::exec()` — **no
|
||||
`request_exec` at this pin**; Success/Failure arrive on the channel
|
||||
queue via `wait()`-drain, **not** as `Err` from the request call
|
||||
(drain until the expected message, `continue` past the rest);
|
||||
`window_change`/`signal` are fire-and-forget and must be sent
|
||||
**before** `into_stream()` (the Channel is consumed by conversion);
|
||||
**never consume the channel queue before `into_stream()`** (a
|
||||
`wait()` on a fresh channel breaks it — POC-1 §6, both sides). The
|
||||
data pump + the exit consumption mirror the server direction's
|
||||
derivation contract (the pipe-mode pid-only signal caveat rides the
|
||||
alktty exit-chunk semantics — `mapping.md`'s contract, consumed not
|
||||
re-derived).
|
||||
- **`direct-tcpip` typed dial** (under `tunnel`) — the row the `-L`
|
||||
leg and the alksocks `DialFn` compose over: `channel_open_direct_tcpip`
|
||||
resolves to `Err(ChannelOpenFailure)` on server reject (POC-1 §6) —
|
||||
the typed open, no `wait()` before `into_stream`; **the connection
|
||||
stays usable after a declined open** (pinned by row). The
|
||||
`direct-streamlocal` twin with the runtime-Unix gate (OQ-11
|
||||
symmetry: universal compile, clean off-Unix decline).
|
||||
- **Git exec consumption** (under `git`) — the same exec grammar the
|
||||
server side parses (`git-{upload,receive}-pack '/repo'`),
|
||||
**reused, not forked** — pointed upstream at a vanilla `sshd`:
|
||||
parse → exec → the pack duplex pumped → exit-status consumed. The
|
||||
per-length limits and service-only decline classes ride the shared
|
||||
grammar's verdicts.
|
||||
- **The sftp subsystem placeholder decline** (every build, OQ-13) —
|
||||
the client-side half of the tested decline placeholder: a
|
||||
`subsystem "sftp"` open declines cleanly in every build (the
|
||||
feature exists, the route doesn't until alkfs lands). The
|
||||
server-side rows already landed in wave 2; this lands the client
|
||||
mirror so both directions are contract-tested.
|
||||
- **Contract rows through the server double, per build** — the
|
||||
`w4-dial` fake-`sshd` vehicle grown with the op-row surfaces:
|
||||
featured + working rows (exec exit-status, pty window-change
|
||||
ordering, direct-tcpip pump, git exec round trip), decline rows
|
||||
(server-rejected opens, the sftp decline, off-Unix streamlocal),
|
||||
the connection-stays-usable row after each decline class. The
|
||||
real-tool smoke posture decided + recorded (a real `sshd` exec is
|
||||
cheap where tooling permits — check-gated like the wave-3 smokes;
|
||||
else named wave-5 e2e territory explicitly).
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
- [ ] Exec/pty rows land under `session` with the POC-3 §D.3
|
||||
mechanics pinned in code + rows (`exec()` not `request_exec`;
|
||||
`wait()`-drain Success/Failure; `window_change`/`signal` before
|
||||
`into_stream`; queue never consumed before conversion)
|
||||
- [ ] `direct-tcpip` (+ streamlocal twin, runtime-Unix gate) lands
|
||||
under `tunnel`: typed open, reject → `Err(ChannelOpenFailure)`,
|
||||
the connection stays usable after a decline (row-pinned)
|
||||
- [ ] Git exec consumption lands under `git` over the shared grammar
|
||||
(no forked parser); the pack duplex pumps; exit-status consumed
|
||||
- [ ] The sftp subsystem placeholder declines in every build (the
|
||||
client-side OQ-13 mirror)
|
||||
- [ ] Per-build contract rows green through the server double; the
|
||||
smoke posture decided + recorded
|
||||
- [ ] `cargo test` (+ per-feature + `--all-features`), clippy ×2
|
||||
(`-D warnings`), fmt, `cargo doc --no-deps` pass
|
||||
|
||||
## References
|
||||
|
||||
- docs/architecture/client.md §Op rows (the table), §Correctness
|
||||
facts, §What the crate does *not* include (job supervision is
|
||||
assembly work — the crate gives channels + exit semantics)
|
||||
- docs/architecture/mapping.md §The exit derivation contract (the
|
||||
mirror the client consumes), docs/architecture/decisions/003-feature-graph.md
|
||||
(the feature rows), docs/architecture/decisions/006-mapping-bridge-and-dispatch.md
|
||||
(exec-only git, both directions)
|
||||
- docs/research/poc-3-mapping-bakeoff.md §D.3 (the client mechanics —
|
||||
the body of knowledge), docs/research/poc-1-auth-bridge.md §6 (the
|
||||
open-call `Err` semantics), docs/research/reverse-runner.md (the
|
||||
per-connection surface table — the consumer evidence)
|
||||
- src/git_map.rs (the grammar reused), src/tty_map.rs (the exit-chunk
|
||||
semantics consumed), russh 0.65 `client::Handle` (`channel_open_session`
|
||||
/ `channel_open_direct_tcpip` / `channel_open_direct_streamlocal`)
|
||||
|
||||
## Notes
|
||||
|
||||
> To be filled by implementation agent
|
||||
|
||||
## Summary
|
||||
|
||||
> To be filled on completion
|
||||
@@ -0,0 +1,109 @@
|
||||
---
|
||||
id: w4-reverse-listen
|
||||
name: Wave 4 — the client -R leg (forward requests + the acceptor registry)
|
||||
status: pending
|
||||
depends_on: [w4-op-rows]
|
||||
scope: moderate
|
||||
risk: high
|
||||
impact: phase
|
||||
level: implementation
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
Land the client direction's `-R` leg (`client.md` §Op rows row 4 +
|
||||
§The acceptor): the hub asks the vanilla server to listen, then
|
||||
**accepts the server-initiated `forwarded-tcpip` opens** and routes
|
||||
each to the family handler. The acceptor row is unavoidable and its
|
||||
disposition is ours (reverse-runner fact 1) — russh's client default
|
||||
is accept-and-drop, a half-capability the family forbids; the
|
||||
`w4-dial` skeleton pins the fail-closed default, this task grows the
|
||||
registry that serves:
|
||||
|
||||
- **The forward request rows** — `tcpip_forward(address, port) ->
|
||||
bound` (russh's client `Handle` returns the actually-bound `u32`;
|
||||
`port = 0` → the server-picked number surfaced — the lifecycle
|
||||
primitive `client.md` §Lifecycle pins), `cancel_tcpip_forward`,
|
||||
`streamlocal_forward` + `cancel_streamlocal_forward` with the
|
||||
**runtime-Unix gate** (OQ-11 symmetry: universal compile, clean
|
||||
off-Unix decline — the dial-call-witness posture the server side
|
||||
landed).
|
||||
- **The acceptor registry** — the assembly-mutable, per-channel-type
|
||||
registry the client Handler routes server-initiated opens through:
|
||||
served **only** under a registered acceptor **for that type and
|
||||
under its feature** (`forwarded-tcpip` / `forwarded-streamlocal`
|
||||
under `tunnel` is the real case); unregistered/unfeatured accepted
|
||||
opens **declined cleanly** — never silently dropped after accept
|
||||
(ADR-003 §4). Decide + record the registry's exact shape (per-type
|
||||
slots vs a typed map; the acceptor trait signature — what an
|
||||
accepted open hands the assembly: the channel duplex + the open's
|
||||
originator fields) against the harness's `ForwardedAcceptor`
|
||||
vocabulary (`w3-tunnel-listen` landed the first shape client-side
|
||||
in tests — grow it into the crate's real surface, don't fork it).
|
||||
- **Late-install across a live connection** (the alkminer
|
||||
`install_forward_sink` dynamic — reverse-runner fact 2): acceptors
|
||||
install/uninstall while the connection is up; consumers turn
|
||||
capabilities on/off per connection. An open arriving before its
|
||||
acceptor installs is declined (fail-closed at arrival time —
|
||||
decide + record; no buffering of unclaimed opens).
|
||||
- **The data leg** — a registered acceptor's open: the channel duplex
|
||||
handed to the assembly handler (the pump is the acceptor's, per the
|
||||
family handler shape); bounded bookkeeping (no unbounded registry,
|
||||
no unbounded per-connection listener map — the `Limits` cap
|
||||
relation decided + recorded, the server-side listener-counter
|
||||
posture as the template).
|
||||
- **Contract rows through the server double, per build** — the fake
|
||||
`sshd` grown with the server-initiated-open surfaces: featured +
|
||||
registered → served; featured + unregistered → clean decline;
|
||||
unfeatured → clean decline (per-build matrices, the symmetry rule);
|
||||
late-install rows (decline-before-install, serve-after); the
|
||||
bound-port out-param row (port 0 mint); the cancel rows; the
|
||||
off-Unix streamlocal rows; the bounded-bookkeeping rows. The
|
||||
real-tool smoke posture decided + recorded (a real `ssh -R`-shaped
|
||||
round trip against the double is the in-process form; real-`sshd`
|
||||
`-R` is wave-5 e2e — never silently dropped).
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
- [ ] `tcpip_forward` / `streamlocal_forward` (+ cancel forms) land
|
||||
with the actually-bound port surfaced (the port-0 mint row);
|
||||
the runtime-Unix gate compiles everywhere, declines cleanly
|
||||
off-Unix
|
||||
- [ ] The acceptor registry lands (shape decided + recorded): served
|
||||
only under a registered acceptor under `tunnel`;
|
||||
unregistered/unfeatured server-initiated opens declined cleanly
|
||||
(never accept-and-drop) — wire rows per build
|
||||
- [ ] Late-install works across a live connection; a pre-install
|
||||
arrival is declined (decided + recorded, no unclaimed buffering)
|
||||
- [ ] The data leg hands accepted opens to the assembly handler;
|
||||
bookkeeping bounded (the cap relation decided + recorded)
|
||||
- [ ] Per-build contract rows green through the server double; the
|
||||
smoke posture decided + recorded
|
||||
- [ ] `cargo test` (+ per-feature + `--all-features`), clippy ×2
|
||||
(`-D warnings`), fmt, `cargo doc --no-deps` pass
|
||||
|
||||
## References
|
||||
|
||||
- docs/architecture/client.md §Op rows (row 4), §The acceptor
|
||||
(fail-closed client-side), §Lifecycle primitives
|
||||
- docs/architecture/decisions/003-feature-graph.md (§4 the
|
||||
client-side fail-closed acceptor — the rule this task implements),
|
||||
docs/architecture/decisions/006-mapping-bridge-and-dispatch.md
|
||||
(route 1, both legs)
|
||||
- docs/research/reverse-runner.md (facts 1–2 — the acceptor row's
|
||||
disposition is ours; the late-install dynamic; lesson 1), docs/research/russh.md
|
||||
§Client surface (the `server_channel_open_*` hook set; the
|
||||
`tcpip_forward -> u32` signature)
|
||||
- docs/research/poc-3-mapping-bakeoff.md §C.4 (the fabric recipe —
|
||||
the family-side half the accepted opens feed)
|
||||
- tests/common/harness.rs (`ForwardedAcceptor` — the vocabulary to
|
||||
grow, not fork), src/tunnel_map.rs (the server-side listener
|
||||
bookkeeping posture as the template)
|
||||
|
||||
## Notes
|
||||
|
||||
> To be filled by implementation agent
|
||||
|
||||
## Summary
|
||||
|
||||
> To be filled on completion
|
||||
@@ -0,0 +1,63 @@
|
||||
---
|
||||
id: w4-router-collision
|
||||
name: Wave 4 — ListenRouter token-collision pre-check (wave-3 gate booked item 4)
|
||||
status: pending
|
||||
depends_on: [w3-tunnel-listen]
|
||||
scope: single
|
||||
risk: low
|
||||
impact: isolated
|
||||
level: implementation
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
Close the wave-3 gate's booked finding item 4
|
||||
(`review-wave-3` §Booked findings): a **defensive `ListenRouter`
|
||||
token-collision pre-check**. `register` silently *replaces* on token
|
||||
collision today; safety rests on the fabric's ADR-008 mint discipline
|
||||
(collision "can't happen" upstream). The gate's disposition: a
|
||||
fail-closed decline-the-listen-on-collision is a cheap wave-4
|
||||
addition — the same posture the gate landed inline for duplicate
|
||||
`tunnel/forwarded` op registration (`install_forwarded_surface` now
|
||||
refuses with `AdapterError::ListenRegistration` instead of trusting
|
||||
the silent-replace).
|
||||
|
||||
- The pre-check: a `register` on an already-held token **refuses**
|
||||
(the listen declines fail-closed) instead of silently replacing the
|
||||
held route — a collided mint can never steal a live adapter's
|
||||
arrival routes.
|
||||
- The error surface: reuse/extend the `AdapterError::ListenRegistration`
|
||||
class (decide + record — a distinct variant only if the gate's
|
||||
variant can't carry the distinction honestly).
|
||||
- Wire row(s): the collision constructible (two registers on one
|
||||
token — the test forces what the fabric's discipline prevents) →
|
||||
the second refuses, the first route still serves (the steal is
|
||||
impossible, row-pinned); the existing arrival/cancel/teardown rows
|
||||
stay green.
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
- [ ] `ListenRouter::register` refuses on token collision (fail-closed,
|
||||
no silent replace); the error surface decided + recorded
|
||||
- [ ] Wire row(s): the second register refuses, the first route still
|
||||
serves; existing rows green
|
||||
- [ ] `cargo test` (+ per-feature + `--all-features`), clippy ×2
|
||||
(`-D warnings`), fmt, `cargo doc --no-deps` pass
|
||||
|
||||
## References
|
||||
|
||||
- tasks/implementation/review-wave-3.md §Booked findings item 4 (and
|
||||
§Minor issues — the `install_forwarded_surface` pre-check this
|
||||
mirrors)
|
||||
- src/tunnel_map.rs (the `ListenRouter`), src/error.rs (the
|
||||
`AdapterError` class), docs/architecture/decisions/ (the fabric's
|
||||
ADR-008 mint discipline — the upstream guarantee the pre-check
|
||||
defends in depth against)
|
||||
|
||||
## Notes
|
||||
|
||||
> To be filled by implementation agent
|
||||
|
||||
## Summary
|
||||
|
||||
> To be filled on completion
|
||||
@@ -0,0 +1,63 @@
|
||||
---
|
||||
id: w4-tty-coverage
|
||||
name: Wave 4 — tty coverage minors (wave-3 gate booked item 3)
|
||||
status: pending
|
||||
depends_on: [w4-channel-not-held-rows]
|
||||
scope: narrow
|
||||
risk: low
|
||||
impact: component
|
||||
level: implementation
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
Close the wave-3 gate's booked finding item 3
|
||||
(`review-wave-3` §Booked findings — "minors, one ticket's worth"):
|
||||
|
||||
- **The `channel_close`-fired per-channel cancel path is
|
||||
unexercised on the wire** — only the teardown signal is. Land the
|
||||
wire row: a channel closing (client-side close on a served tty
|
||||
row) fires the per-channel cancel path (the cap release + the
|
||||
per-channel state teardown observed — the disjoint release sites
|
||||
the gate verified per row, now wire-witnessed for this path).
|
||||
- **The Closed/Break no-exit-chunk close shape has no wire row** —
|
||||
land the row(s): a session ending via the Closed/Break close shape
|
||||
emits **no exit chunk** (the derivation contract's close-shape
|
||||
class — the absent-report posture pinned, not just the happy
|
||||
paths).
|
||||
- **The tty stdout `write_all` carries no deadline** — record as a
|
||||
**known limit only** (module docs): backpressure is bounded, liveness
|
||||
is covered by the inactivity timeout; no code change (the gate's
|
||||
recorded disposition — a deadline here is a behavior change, not a
|
||||
coverage minor).
|
||||
|
||||
Depends on `w4-channel-not-held-rows` to keep `tests/tty_mapping.rs`
|
||||
single-writer (that task's rows land first; these land into the
|
||||
settled file).
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
- [ ] Wire row: the `channel_close`-fired per-channel cancel path
|
||||
(cap release + state teardown observed on the wire)
|
||||
- [ ] Wire row(s): the Closed/Break no-exit-chunk close shape
|
||||
- [ ] The stdout `write_all` no-deadline limit recorded in module
|
||||
docs (no code change)
|
||||
- [ ] `cargo test` (+ per-feature + `--all-features`), clippy ×2
|
||||
(`-D warnings`), fmt, `cargo doc --no-deps` pass
|
||||
|
||||
## References
|
||||
|
||||
- tasks/implementation/review-wave-3.md §Booked findings item 3
|
||||
- docs/architecture/mapping.md §The exit derivation contract (the
|
||||
close-shape classes; the PTY-path rule-5 limit — the docs-posture
|
||||
template), src/tty_map.rs, src/channel_cap.rs (the release sites
|
||||
the rows witness)
|
||||
- tests/tty_mapping.rs (the per-build matrix the rows join)
|
||||
|
||||
## Notes
|
||||
|
||||
> To be filled by implementation agent
|
||||
|
||||
## Summary
|
||||
|
||||
> To be filled on completion
|
||||
Reference in new issue
Block a user