The biggest single-row surface of the spine: pty/shell/pipe-exec serve
through the published alktty seam (drive_session_pre_negotiated) over a
translation bridge, re-landing POC-3 stage D as contract:
- src/tty_map.rs (session feature): the bridge (SSH data → Chunk::stdin
through a bounded mpsc + framing task; SSH EOF → the zero-length
stdin sentinel — verified against alktty's pump semantics; stdout →
data; stderr → extended_data(1); exit chunk → the derivation
contract), the locally-built negotiation (OQ-08: shell argv verbatim,
wrapper ++ [cmd], cwd/env session-side, ttymodes → the reserved {}
modes JSON), and the spawned serving path.
- serve_tty_row: driven chunks→SSH inline so the exit report is the
path's final act (status before close, ADR-006 decision 4); two
cancel signals (per-channel watch fired by channel_close, connection
teardown fired by SessionHandler::Drop) abort the alktty session
task — the ADR-056 kill-on-drop contract is the cancelled session's
cleanup.
- Derivation contract rules 1–6 in derive_exit + the crate-local
signum→name table: −N → exit-signal(name), −1 → exit-status(255),
core_dumped never, unmapped negatives → 255, the PTY-path rule-5
fidelity limit documented; wire rows pin the shapes.
- Negotiation-error frames (alktty's 0x00-length-prefixed error
framing) → negotiation-failed serving decline + exit-status(255) +
close — the one could-not-start claim this door makes (nothing ran).
- Control rows: window_change/signal → the per-channel BridgeSurface
(bounded mpsc, try_send, never awaited on the packet loop); signal
names verbatim (no decline shape — fire-and-forget); pre-serve
resizes update the stored pty params (final size wins).
- Unset-declines (OQ-08) keep the git row's serving-path shape
(failure then close, spawned): shell-unconfigured /
wrapper-unconfigured / seam-unconfigured; pipe-less shell SERVES
pipe-mode (the recorded vanilla-sshd posture); env_request decline
unchanged (OQ-09).
- tests: the hand-rolled TtyBackend shim + ownership witness doubles;
serving rows through the harness, the unset-field decline matrix,
derivation rows on the wire, negotiation-failure rows, the identity
ride asserted live at the seam, the kill witness; the real-tool
smoke — OpenSSH -tt against a real PTY via dev-dep alktty local
(test graph only), skip-with-reason posture. Wave-2 stub rows
migrated to the new decline reasons (dispatch_core, observe).
Verification: cargo test (113) / --features session (149) / git (142) /
tunnel (115) / --all-features (161) — all green; cargo clippy
--all-targets -- -D warnings ×5 feature configs; cargo fmt --check;
cargo doc --no-deps ×3 builds clean; taskgraph validate.
- ServeConfig gains cfg-gated SessionSeam / GitSeam Option fields
(whole-Option struct per seam — the shape decision recorded in the
task notes): unset → the owned rows decline even though the feature
is on, no construction-time validation
- SessionSeam carries the alktty backends map + backend selector +
ownership provider; GitSeam carries the alkgit backend quartet +
caller-owned pipeline semaphore + push_options_negotiated
- optional deps alktty 0.8.1 / alkgit 0.1.0 wired feature-to-dep
(pins verified against crates.io: licenses, MSRV, shared alkcall
resolution); gix-hash 0.28 as dev-dep only (alkgit does not
re-export ObjectId for the trait doubles)
- per-feature construction/unset-declines/Debug rows in config.rs;
accessors session_seam_configured / git_seam_configured; docs state
what each seam handle carries
Verified: cargo test (per-feature + --all-features), clippy ×2
(-D warnings), fmt, cargo doc --no-deps, cargo tree feature isolation
(git-build: no alktty; session-build: no alkgit), taskgraph validate
- HostKeyPolicy trait: async, dyn-compatible (Arc<dyn> handle, the
alkcall injected-handle pattern) — decide(&self, &PresentedHostKey)
-> bool with host/port/fingerprint as the decision input
- Verdict type pinned to the russh 0.65 check_server_key shape:
bool is the trust decision; decline reasons stay with the policy
(declines are dispositions, not errors). Flagged for the wave-1 gate
- Built-in default RejectAll: fail-closed reject-all (russh's default,
kept); no accept-all option exists in the crate
- async-trait 0.1 added for the dyn-compatible async handle
(alkcall-precedented; tree-deduped). Flagged for the wave-1 gate
- Tests: default rejects; custom verdict honored; both through the dyn
handle; the whole presented context reaches the policy
Verified: cargo test (14), cargo test --all-features, cargo clippy
--all-targets -- -D warnings, cargo fmt --check, taskgraph validate
tokio = "1.54.1" (full features) with its lockfile tree; version pin
and feature set to be revisited with phase 1 architecture (committed
now to reduce working-tree noise)
Phase 0 base for the russh-wrapped SSH adapter (family ADR-003):
- Cargo.toml: single-crate alkssh 0.1.0, MIT OR Apache-2.0, lib only,
publish exclude list matching the sibling-crate policy; deliberately
empty [dependencies] — the russh pin is phase 0 research output
(alknet main's 0.49 is a data point, not the decision). [features]
carries the fail-closed posture as a comment until the capability
feature graph lands with the committed architecture
- src/lib.rs: crate-root doc describing the adapter shape and the
fail-closed feature contract
- LICENSE-MIT / LICENSE-APACHE (alktty's standard texts)
- .gitignore (target/, node_modules/, .worktrees/)
- CHANGELOG.md: Keep a Changelog stub, [Unreleased] initialization
- AGENTS.md: full agent operating instructions — git workflow
(conventional commits, commit-and-push), 13 project conventions
(russh owns the wire, fail-closed capability features, key reuse via
alkvault-backed sources, visible-surface=authorized-surface,
ALPN as one-way door), verification commands, taskgraph
task rules, SDD phase 0 lifecycle status, architecture context
Verification: cargo build/test/clippy --all-targets -D warnings and
cargo fmt --check all pass (lib stub, zero deps)