Final review verified: - 103 tests pass (61 alknet-tty + 42 alknet-tty-local), clippy clean, fmt clean - Cross-crate seam: portable_pty only in alknet-tty-local, not in alknet-tty default - ADR-055 exit-chunk-is-last enforced in adapter, validated in integration tests - ADR-056 cancel-cleanup guards in both PTY and pipe exit futures, integration tests confirm no orphans - REQ-TTY-01 three-thread bridge, REQ-TTY-02 process-group signal forwarding - No bollard/russh/alknet-call deps; alknet-tty depends on alknet-core only - Deviation: local feature re-export deferred to assembly layer (cargo cycle constraint)
7.2 KiB
id, name, status, depends_on, scope, risk, impact, level
| id | name | status | depends_on | scope | risk | impact | level | |
|---|---|---|---|---|---|---|---|---|
| tty/review-tty-final | Final review of alknet-tty + alknet-tty-local for merge readiness | completed |
|
broad | low | project | review |
Description
Final review of the alknet-tty and alknet-tty-local crates for merge
readiness. This is the last quality checkpoint before the tty work is
considered complete. It validates the two crates as a unit: the core crate's
wire format + adapter + trait, the local backend's PTY/pipe implementations,
the feature-gate seam, and the end-to-end integration tests.
Review Checklist
-
Cross-crate seam:
alknet-ttylocalfeature gate activatesdep:alknet-tty-localalknet_tty::local::LocalTtyBackendre-export works with--features localcargo tree -p alknet-tty(default) does NOT includeportable_ptycargo tree -p alknet-tty --features localincludesportable_ptyviaalknet-tty-localLocalTtyBackendimplementsalknet_tty::TtyBackend(the trait compiles against the impl)- Assembly-layer pattern documented
-
Wire format invariants (ADR-052):
- Two-carriage model: JSON negotiation frame, then raw chunks
- Fixed channel set (0-3), no extension escape hatch
MAX_CHUNK_LEN= 16 MiB shared between negotiation and raw chunks- Framing disambiguation: error frame first byte
0x00, raw chunk first byte 1-3 - Self-contained framing (no alknet-call dependency, ADR-057)
-
Exit-chunk ordering (ADR-055):
- Exit chunk is the last chunk before stream close
- Adapter waits for stdout/stderr pumps AND exit_code resolve before exit chunk
- Exit error →
{"type":"exit","code":-1} - Integration test confirms exit-chunk-is-last with a real backend
-
Cancel-cleanup (ADR-056):
LocalExitFuture(PTY) has kill-on-Drop guard (ChildKiller::kill(SIGHUP))PipeExitFuture(pipe) has kill-on-Drop guard (Child::start_kill())- Both disarm on resolve (no-op Drop on happy path)
- Neither is a bare
oneshot::Receiver/ bareChild::wait() - Integration tests confirm no orphaned processes on connection drop (PTY + pipe)
-
REQ-TTY-01 (backends need not be natively async):
- PTY mode uses three std threads (reader, writer, waiter) + tokio mpsc/oneshot
- Trait's adapter-facing types (
AsyncWrite,Stream,BoxFuture,TtyControl) are the contract - Documented as a supported implementation strategy
-
REQ-TTY-02 (signal forwarding to process group):
- PTY mode:
kill(-pgid, sig)withkill(pid, sig)fallback portable_ptychild is a session leader (controlling_tty = true)- Pipe mode:
kill(pid, sig)only (documented limitation, no process group) - Unknown names fall back to default kill (SIGHUP for PTY, SIGKILL for pipe)
- Integration test confirms process-group targeting (shell's child receives signal)
- PTY mode:
-
Access control (ADR-050):
- Scope-gate at negotiation (
tty:openor deployment-configured) - Ownership check via
backend.resource_id()+OwnershipProvider::owns()when wired LocalTtyBackend::resource_id()returnsNone(creates its own resource)
- Scope-gate at negotiation (
-
Dependency hygiene:
alknet-ttydepends onalknet-coreonly (noalknet-call, no backend deps)alknet-tty-localdepends onalknet-tty+portable_pty+libc- No
alknet-coredirect dep inalknet-tty-local - No
bollard/russh(future backend crates)
-
Pattern consistency:
thiserrorfor error enums (TtyError,RawError,NegotiationError)async-traitforTtyBackendandProtocolHandlertracingfor structured loggingTtyControlHandle::new(Arc::new(...))wrapping (OQ-43)#[cfg(unix)]gates onlibc::killpaths#[non_exhaustive]onTtyError
-
Test coverage:
- Wire codec unit tests (round-trip, error cases)
- Control message unit tests (round-trip, signal_from_name)
- Negotiation unit tests (round-trip, serde(flatten), error framing, disambiguation)
- Backend trait unit tests (MockBackend, TtyControlHandle delegation)
- Adapter unit tests (MockBackend over duplex, all scenarios)
- PTY mode integration tests (happy, interactive, resize, signal, process-group, cancel, exit-last)
- Pipe mode integration tests (happy, stderr, signal, cancel, resize)
- Negotiation error integration tests (unknown_backend, malformed, allocate_failed)
- All tests pass with
--features local
-
Documentation:
- Crate-level doc comments on both crates
- Module-level doc comments
- ADR-056 kill-on-Drop contract documented on
TtyHandle.exit_code - REQ-TTY-01 documented on
TtyBackendtrait - REQ-TTY-02 documented on
PtyControl::signal - Pipe-mode signal limitation documented on
PipeControl::signal - Assembly-layer pattern documented
Acceptance Criteria
- Cross-crate seam:
localfeature re-export works;portable_ptygated correctly - Wire format invariants (ADR-052) hold: two-carriage, fixed channels, disambiguation
- Exit-chunk-is-last (ADR-055) enforced in adapter, validated in integration tests
- Cancel-cleanup (ADR-056) guards present in both PTY and pipe exit futures; integration tests confirm no orphans
- REQ-TTY-01 satisfied: PTY mode uses three-thread bridge; trait contract documented
- REQ-TTY-02 satisfied: PTY process-group signal forwarding; integration test passes
- Access control (ADR-050): scope-gate + ownership check wired
- Dependency hygiene: alknet-tty lean (core only); alknet-tty-local has portable_pty/libc
- Pattern consistency: thiserror, async-trait, tracing, TtyControlHandle wrapping, cfg(unix)
- All unit + integration tests pass with
--features local cargo fmt --checkpasses for both cratescargo clippypasses with no warnings for both crates (default +--features local)- Documentation complete (crate, module, contract doc comments)
References
- docs/architecture/crates/tty/README.md
- docs/architecture/crates/tty/overview.md
- docs/architecture/crates/tty/tty-wire.md
- docs/architecture/crates/tty/tty-backend.md
- docs/architecture/crates/tty/tty-adapter.md
- docs/architecture/crates/tty/tty-local.md
- docs/architecture/decisions/052-alknet-tty-wire-format-and-two-carriage.md
- docs/architecture/decisions/053-ttybackend-trait-and-ttyhandle.md
- docs/architecture/decisions/054-local-tty-backend-sibling-crate.md
- docs/architecture/decisions/055-exit-code-on-control-chunk.md
- docs/architecture/decisions/056-backend-cleanup-on-session-cancel.md
- docs/architecture/decisions/057-alknet-tty-no-alknet-call-dep.md
Notes
This is the final review before the tty work is considered complete. It validates the two crates as a unit — the core crate's invariants (exit-chunk-is-last, kill-on-Drop, framing disambiguation) must hold with the real local backend, not just the MockBackend. The cancel-cleanup integration tests are the most critical: an orphaned process on connection drop is the bug ADR-056 exists to prevent. The process-group signal test (REQ-TTY-02) is the other critical validation — Ctrl-C must reach a shell's children. If any invariant fails, document and fix before merge.
Summary
To be filled on completion