Six implementation tasks + the gate under tasks/implementation/, chained sequentially (the mappings replace stubs in the same Handler impl and share the decline-matrix test binaries): - w3-seam-config: the serve-config's per-feature seam handles (alktty backends/ownership/selector; the alkgit quartet/pipeline/ push-options flag) + the optional sibling deps — wave 3's first course correction to the wave-1 config surface - w3-decline-tracing: the wave-2 gate's booked finding — the decline/serve-path tracing vocabulary, landed before the mappings - w3-git-mapping: the full exec grammar replaces the wave-2 probe + the serve_duplex_resolved dispatch + the store-and-take handoff - w3-tty-session: the translation bridge + the exit-derivation contract (rules 1-6, the signum table) - w3-tunnel-direct: the route-1 bridge (accept only after the fabric open succeeds) + set_identity propagation + the capacity-across- fabric-window mechanics - w3-tunnel-listen: the -R listen flow + the forwarded-tcpip data leg (the POC-untested leg; the stop-and-book provision) - review-wave-3: the gate The plan carries the wave-3 decomposition record with four interpretations flagged for the gate (the serve-config course correction, the already-discharged server-side sftp bullet, the w3-tunnel-listen open mechanics, the booked-items' wave-3 homes). taskgraph validate: 26 tasks, clean. Sibling pins verified against crates.io at decomposition time: alktty 0.8.1 / alkgit 0.1.0 / alktunnels 0.4.1 / alkcall 0.10.1.
7.7 KiB
7.7 KiB
id, name, status, depends_on, scope, risk, impact, level
| id | name | status | depends_on | scope | risk | impact | level | |
|---|---|---|---|---|---|---|---|---|
| w3-tty-session | Wave 3 — session → alktty (translation bridge + exit derivation) | pending |
|
moderate | medium | phase | implementation |
Description
Land the session branch — the biggest single-row surface of the spine
(mapping.md's session rows): pty/shell/pipe-exec become a real
serving path through the published alktty seam, as a translation
bridge, not a byte pump (POC-3 §D.1). Everything POC-3 stage D
proved, re-landed as contract:
- The per-channel state + the store-and-take reuse — the channel
store
w3-git-mappinglands (channels.remove(&id)→into_stream()in the spawned serving task) gains the session rows' per-channel state: the PTY params apty_requestdelivered (consumed by the latershell_request/exec_requeston the same channel —ssh -t host "cmd"sends pty-then-exec and must serve PTY-mode; ashell_requestwithout a preceding pty serves pipe-modetty = None— decide whether such a shell serves or declines and record). - Negotiation, built locally, never wire-derived (OQ-08 — the
serve-config is the whole truth):
pty_requestargs →TerminalParamsWire{term, cols, rows, pixel_w/h, modes}(SSH ttymodes → the reserved modes JSON — alktty OQ-44's reservation, deferred there);shell_request→NegotiateRequest{carriage: "raw", cmd: shell_argv (the serve-config shell, verbatim), cwd: session_cwd, env: session_env, backend: the seam selector}→drive_session_pre_negotiated(writer, reader, req, backends, ownership, identity)spawned;exec_request(pipe row) →cmd = wrapper ++ [cmd](the OQ-08 argv append — vanilla quoting semantics; never naively split),tty = Nonepipe mode. Unset-declines (OQ-08):shell_argvunset →shell_requestdeclines;exec_wrapper_argvunset → the pipe row declines — never a fallback. Decide the wire shape per row (capability decline — channel stays usable — vs the serving-path failure+close shape) and record. - The translation bridge — two small adapters around a duplex the
serving task drives (the POC-3 §D.1 shape): SSH
data/EOF →Chunk::stdin(decide the EOF translation — the zero-length stdin-chunk sentinel vs alktty'sControlMessage::EofCTRL_IN — verify what alktty's ChunkReader/backend actually consumes and record); stdout chunks → SSHdata; stderr chunks →extended_data(1)(PTY mode merges upstream — the backend's stderr stream isNonethere, so the translation is universal, no mode-switch); the exit chunk (STREAM_CTRL_OUT) → the derivation contract. No unbounded buffering in either adapter (convention 9). - Control rows —
window_change_request→ControlMessage:: ResizeCTRL_IN;signal→ControlMessage::Signal(alktty's uppercase-name vocabulary; name accepted verbatim — decide the decline shape for names alktty's backend semantics reject and record). Mechanics: the requests arrive on the packet loop after the serving task owns the wire — reach them into the spawned bridge through a per-channel control surface (bounded mpsc — decide + record; a silent unbounded proxy is a bounded-resources bug). - The exit derivation contract (mapping.md rules 1–6,
normative): the alktty exit chunk (
{"type":"exit","code":i32}, exit-chunk-is-last) is the sole fidelity input, never widened:code ≥ 0→exit-status(N); negative−N, N ≥ 2 →exit-signal(name)via the server-side Unix signum→name table (crate-local; not derived from alktty'ssignal_from_nameinverse unless that is public and sound — decide the table's form and record;core_dumpednever asserted, the bool stays false);−1→exit-status(255), never a signal claim (rule 3 — the SIGHUP collision accepted symmetrically, per the mapping.md rationale); non-Unix adds no other negatives (rule 4); the PTY-path rule-5 fidelity limit documented in the module docs (signal- terminated PTY sessions arriveexit-status(1)— portable-pty's exit surface, upstream); unmapped negatives → 255 (rule 6). Sent before close, from the spawned task. env_requeststays declined (OQ-09 — unchanged from wave 2);session_cwd/session_envrideNegotiateRequestonly.- Contract rows through the harness, per build — the wave-2 stub
rows (
pty_row_stub/shell_row_stub/pipe_row_stub) replaced; the decline matrix's unset-field rows — feature on + handle unset → the rows decline; featured+set serve through the alkssh-side TtyBackend shim double (alktty'sMockBackend/testing.rsarepub(crate)+cfg(test)— never importable; alkssh hand-rolls the shim: a backend that recordsTtyParams, emits scripted stdout/stderr/exit chunks and consumes stdin/resizes/signals — per theTtyHandlefield shapes). The identity-landing item (the parked wave-2 note) asserted through the row that observes the identity atdrive_session_pre_negotiated. - The real-tool smoke — one OpenSSH pty session through the
harness against the shim (or against a real PTY via a dev-dep
alkttylocalcomposition — decide which posture and record; the real bash/stty sizefidelity was POC stage D's, wave 5 re-lands it as e2e): the smoke owns the wire rows a plain rustsh-client row can't pin (echo paths,stty size-grade fidelity are wave-5's matrix; here the pty lifecycle rows only).
Acceptance Criteria
- pty/shell/pipe-exec rows serve through
drive_session_pre_negotiated(spawned, handler fast); the store-and-take + per-channel PTY state reused from the git task's handoff; pipe-less shell decided + recorded pty_request→TerminalParamsWire(ttymodes → the reserved JSON),shell_request→ shell argv, exec →wrapper ++ [cmd], cwd/env session-side; unset-declines on both OQ-08 rows with recorded wire shapes- The bridge translated per row (stdin chunks + EOF shape
verified against alktty; stdout→data; stderr→
extended_data(1); resize/signal CTRL_IN reach the serving path through a bounded per-channel surface) - The derivation contract rules 1–6 in code with the signum→name
table;
−1→255;core_dumpednever; PTY-path rule-5 documented; exit-status before close from the spawned task — pinned by rows - The decline matrix per build green through the harness (unset-
field rows included); identity ride asserted; the
env_requestdecline unchanged; skeleton stub rows migrated - The real-tool pty smoke row (posture decided + recorded)
cargo test(+ per-feature +--all-featuresrows), clippy ×2 (-D warnings), fmt,cargo doc --no-depspass
References
- docs/architecture/mapping.md (the
sessionchannel-type row, request-level rows, §Exit chunk → SSH exit reporting — rules 1–6) - docs/architecture/decisions/006-mapping-bridge-and-dispatch.md (decision 4 — spawn-and-return: status before close from the spawned task), docs/architecture/decisions/003-feature-graph.md
- docs/architecture/auth.md §Server direction step 4 (the identity ride), §Dispositions (OQ-03)
- docs/research/poc-3-mapping-bakeoff.md §Stage D (the bridge shape, control rows, the signal/exit fidelity findings, §D.3 mechanics)
- docs/research/reverse-runner.md (pipe-mode pid-only signal caveat — the fidelity note)
- alktty 0.8.1:
adapter.rs:234-241(drive_session_pre_negotiated),negotiation.rs(NegotiateRequest/TerminalParamsWire),wire.rs(chunks, readers/writers),control.rs(ControlMessage),backend.rs(TtyHandle/TtyParams/TtyBackendshapes)
Notes
To be filled at implementation time.