7.1 KiB
id, name, status, depends_on, scope, risk, impact, level
| id | name | status | depends_on | scope | risk | impact | level | |
|---|---|---|---|---|---|---|---|---|
| tty/review-tty | Review alknet-tty core crate for spec conformance before local backend begins | completed |
|
moderate | low | phase | review |
Description
Review the alknet-tty core crate implementation for spec conformance, pattern
consistency, and correctness before alknet-tty-local begins implementation
against the TtyBackend trait. This is the quality checkpoint at the end of
the core crate phase — the trait shape is a one-way door (ADR-053), and the
local backend builds against it, so any issues here propagate to the sibling
crate.
Review Checklist
-
Wire codec conformance (tty-wire.md §"Phase 2"):
ChunkReader/ChunkWriterwith 5-byte header (1 stream_type + 4 BE length)STREAM_STDIN=0,STREAM_STDOUT=1,STREAM_STDERR=2,STREAM_CONTROL=3MAX_CHUNK_LEN= 16 MiB;ChunkTooLargeon overflowInvalidStreamTypeon stream_type > 3ConnectionClosedon clean EOF (notIo)- Zero-length chunks are sentinels (codec doesn't special-case; adapter interprets)
-
Control messages conformance (tty-wire.md §"Control Channel"):
ControlMessageenum:Resize,Signal,Eof,Exit#[serde(tag = "type", rename_all = "snake_case")]Exit.codeisi32(negative = signal-terminated)signal_from_namefor 9 names,#[cfg(unix)]gated- Unknown
typevalues:from_slicereturns error; adapter ignores (not a schema catch-all)
-
Negotiation conformance (tty-wire.md §"Phase 1", §"Constraints"):
NegotiateRequestwithcarriage,backend,tty,cmd,cwd,env,backend_paramsserde(flatten)captures backend-specific fields intobackend_paramsTerminalParamsWirewithterm,cols,rows,pixel_width,pixel_height,modes- Length-prefixed framing (4-byte BE + JSON body), self-contained (no alknet-call dep, ADR-057)
NegotiationReaderbounds-checks againstMAX_CHUNK_LEN- Error response shape:
{"error":"...","field":"..."} - Framing disambiguation: error frame first byte is
0x00(under 16 MiB invariant)
-
Backend trait conformance (tty-backend.md, ADR-053):
TtyBackendtrait:allocate()(async),resource_id()(defaultNone)TtyError#[non_exhaustive]:AllocFailed,WaitFailed,Io,BackendTtyParams:terminal,cmd,cwd,env,backend_params(opaqueserde_json::Map)TerminalParams:term,cols,rows,pixel_width,pixel_height,modesTtyHandle:stdin(Box<dyn AsyncWrite + Send + Unpin>),stdout(Pin<Box<dyn Stream<Item=Bytes> + Send>>),stderr(Option),exit_code(BoxFuture<Result<i32, TtyError>>),control(Option)TtyControltrait:resize,signal(object-safe, NOTClone)TtyControlHandle:#[derive(Clone)],Arc<dyn TtyControl + Send + Sync>newtype (OQ-43)From<NegotiateRequest> for TtyParams(or constructor)- REQ-TTY-01 documented (backends need not be natively async)
- ADR-056 kill-on-Drop contract documented on
exit_codefield
-
Adapter conformance (tty-adapter.md):
TtyAdapterstruct withbackends(Arc<HashMap<String, Arc<dyn TtyBackend>>>),ownership(Option)impl ProtocolHandlerwithalpn()=b"alknet/tty"handle()loopsaccept_bi, spawnsdrive_sessionper stream- Negotiation errors:
unknown_backend,malformed_negotiation,allocate_failedas JSON in negotiation framing - Three-pump driver: stdout→client, client→backend (stdin + control), exit→exit-chunk
- stderr pump concurrent when
stderrisSome - Exit-chunk-is-last (ADR-055): stdout/stderr pumps complete AND exit_code resolves before exit chunk
- Exit error →
{"type":"exit","code":-1} - Unknown control
typeignored;Exitfrom client ignored - Zero-length stdin chunk AND
eofcontrol both close backend stdin - Scope-gate (
tty:open) at negotiation - Ownership check via
resource_id()+OwnershipProvider::owns()when wired - Connection drop / stream reset drops
TtyHandle(ADR-056 cancel-cleanup) - Client write-half close does NOT trigger cancel-cleanup
-
Dependency constraints:
- No
portable_pty,bollard,russhdependency (lean core) - No
alknet-calldependency (ADR-057 — self-contained framing) alknet-coreis the only alknet dependencylocalfeature gate declared;alknet-tty-localnot yet wired (later task)
- No
-
Pattern consistency:
thiserrorfor error enums,Resultpropagationtracingfor structured logging (debug/warn)async-traitforTtyBackendandProtocolHandlertokioruntime idioms (mpsc, oneshot, spawn)
-
Test coverage:
- Wire codec round-trip + error cases
- Control message round-trip +
signal_from_name - Negotiation round-trip +
serde(flatten)+ error framing MockBackend+TtyControlHandledelegation- Adapter: happy path, exit-chunk-is-last, stdin EOF, control dispatch, unknown control, all 3 negotiation errors, exit error, cancel cleanup, scope gate, ownership check
Acceptance Criteria
- All wire codec types match tty-wire.md §"Phase 2"
- All control message types match tty-wire.md §"Control Channel"
- All negotiation types match tty-wire.md §"Phase 1" + §"Constraints"
- All backend trait types match tty-backend.md (ADR-053)
- Adapter matches tty-adapter.md (session lifecycle, exit ordering, access control)
- Exit-chunk-is-last invariant (ADR-055) enforced in the adapter, not the backend
- ADR-056 kill-on-Drop contract documented on
TtyHandle.exit_code - No
portable_pty/bollard/russh/alknet-calldependency localfeature gate declared,alknet-tty-localnot yet wiredcargo fmt --check -p alknet-ttypassescargo clippy -p alknet-ttypasses with no warnings- All tests pass
MockBackendis reusable for the local backend's integration tests
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/decisions/052-alknet-tty-wire-format-and-two-carriage.md
- docs/architecture/decisions/053-ttybackend-trait-and-ttyhandle.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 review verifies the core crate is spec-conformant before the local backend builds against the
TtyBackendtrait. The trait shape is one-way (ADR-053) — any issues here propagate toalknet-tty-localand the future docker/SSH backend crates. Pay special attention to the exit-chunk-is-last ordering (ADR-055) and the kill-on-Drop contract (ADR-056) — these are the subtle invariants the POC did not fully enforce. If deviations are found, document and fix before proceeding to the local backend tasks.
Summary
To be filled on completion