Break the tty spec set (docs/architecture/crates/tty/) into atomic,
dependency-ordered tasks across two crates:
alknet-tty (lean core, depends on alknet-core only — ADR-057):
- crate-init, wire-codec, control-messages, negotiation, backend-trait,
adapter, review-tty
alknet-tty-local (sibling, portable_pty + libc — ADR-054):
- crate-init, pty-mode, pipe-mode, backend-impl, review-tty-local
integration:
- local-feature-reexport, integration-test, review-tty-final
Three review checkpoints at critical points: after the core crate (before
the local backend builds on the one-way-door TtyBackend trait — ADR-053),
after the local backend (validating REQ-TTY-01/02 and ADR-056), and a final
merge-readiness review. The high-risk tasks (backend-trait, adapter,
pty-mode) are flagged; the ADR-056 kill-on-Drop guard the POC lacked is
called out in both pty-mode and pipe-mode.
Validated with taskgraph: 115 tasks, no cycles, 10-generation parallel
structure with 3-way parallelism after crate-init and 2-way after the
backend trait.
4.2 KiB
id, name, status, depends_on, scope, risk, impact, level
| id | name | status | depends_on | scope | risk | impact | level | ||
|---|---|---|---|---|---|---|---|---|---|
| tty-local/backend-impl | Implement LocalTtyBackend (TtyBackend) branching on terminal Some/None | pending |
|
narrow | low | component | implementation |
Description
Implement LocalTtyBackend in src/backend.rs: the TtyBackend implementation
that branches on TtyParams.terminal to dispatch to PTY mode (task
tty-local/pty-mode) or pipe mode (task tty-local/pipe-mode).
LocalTtyBackend
pub struct LocalTtyBackend;
impl LocalTtyBackend {
pub fn new() -> Self;
}
#[async_trait]
impl TtyBackend for LocalTtyBackend {
async fn allocate(&self, params: &TtyParams) -> Result<TtyHandle, TtyError> {
match ¶ms.terminal {
Some(terminal) => pty::allocate_pty(
terminal.clone(),
params.cmd.clone(),
params.cwd.clone(),
params.env.clone(),
).await,
None => pipe::allocate_pipe(
params.cmd.clone(),
params.cwd.clone(),
params.env.clone(),
).await,
}
}
fn resource_id(&self, _params: &TtyParams) -> Option<(&'static str, String)> {
None // local backend creates its own resource (process); no pre-existing resource
}
}
LocalTtyBackend takes no constructor dependencies (unlike
DockerTtyBackend which wraps a bollard::Docker client). The
portable_pty system is process-global. The assembly layer constructs one
LocalTtyBackend and registers it as "local".
Validation
allocate() validates params.cmd is non-empty (the adapter already checks
this at negotiation, but the backend should fail gracefully if called
directly with an empty cmd — return TtyError::AllocFailed). The
backend_params map is ignored by the local backend (it has no
backend-specific selector fields); if a caller passes unexpected
backend_params, they are silently ignored (the local backend doesn't
define a typed params struct).
Default trait methods
resource_id() returns None (the local backend creates its own resource —
a process — so there's no pre-existing resource for the ownership check). The
default impl on TtyBackend already returns None, so this can be omitted
unless an explicit impl is preferred for clarity.
Tests
- PTY dispatch:
allocatewithterminal: Some→ returns aTtyHandlewithstderr: Noneand aPtyControl. - Pipe dispatch:
allocatewithterminal: None→ returns aTtyHandlewithstderr: Someand aPipeControl. - Empty cmd:
allocatewith emptycmd→TtyError::AllocFailed. - resource_id: returns
None. - These can be lightweight (the heavy tests are in
pty-modeandpipe-mode); this task verifies the dispatch wiring.
Acceptance Criteria
LocalTtyBackendstruct withnew()constructor (no deps)impl TtyBackend for LocalTtyBackendwithallocate()branching onparams.terminalterminal: Some→ dispatches topty::allocate_ptyterminal: None→ dispatches topipe::allocate_piperesource_id()returnsNone(or uses the default)- Empty
cmd→TtyError::AllocFailed LocalTtyBackendre-exported fromlib.rs(pub use backend::LocalTtyBackend)- Unit tests: PTY dispatch, pipe dispatch, empty cmd, resource_id
cargo test -p alknet-tty-localsucceedscargo clippy -p alknet-tty-localsucceeds with no warnings
References
- docs/architecture/crates/tty/tty-local.md — §"PTY Mode", §"Pipe Mode", §"LocalTtyBackend takes no constructor dependencies"
- docs/architecture/crates/tty/tty-backend.md —
TtyBackendtrait,resource_id() - docs/architecture/decisions/054-local-tty-backend-sibling-crate.md — ADR-054 (per-session PTY vs pipe choice)
Notes
This is the wiring task — the heavy lifting is in
pty-modeandpipe-mode. The branch onTtyParams.terminalis the per-session choice (ADR-054): oneLocalTtyBackendserves both terminal and runner use cases. The local backend ignoresbackend_params(no backend-specific selector fields); a docker/SSH backend would deserialize its own typed params from it.
Summary
To be filled on completion