Files
alknet/docs/architecture/questions/043-ttycontrol-as-a-clone-trait-object.md
T
glm-5.2 25f0f55592 docs(arch): alknet-tty spec sanity-check fixes — TtyControlHandle newtype (C1), backend cleanup contract (W1, ADR-056), clarifications (S1-S5)
C1: Box<dyn TtyControl + Clone> does not compile (Clone is not object-safe).
Replace with a TtyControlHandle newtype: #[derive(Clone)] wrapping
Arc<dyn TtyControl + Send + Sync>. The trait stays object-safe; the
newtype carries the Clone-ability. Updated across tty-backend.md,
ADR-053, OQ-43, and the tty README.

W1: 'Drop is the cleanup, threads exit on channel close' was false for
the local PTY waiter thread (blocked in Child::wait(), does not observe
channel close) — a child that ignores stdin EOF would be orphaned on
session cancel. New ADR-056 commits a backend cleanup contract: dropping
the exit_code future MUST kill the session target. The local backend
implements it via a ChildKiller held in the exit_code future's Drop
guard (disarmed on resolve). Corrected the lifecycle section in
tty-adapter.md, added the mechanism + constraint to tty-local.md, and
referenced ADR-056 from tty-backend.md, overview.md, both READMEs.

S1: error-frame < 16 MiB stated as a wire-format invariant (not an
assumption) so the 0x00-first-byte disambiguation trick is sound by
construction.
S2: carriage field validation (MUST be "raw" in v1, else
malformed_negotiation) specified.
S3: NegotiateRequest + TerminalParamsWire Rust structs added with
serde defaults and validation rules.
S4: modes field annotated 'backends MUST ignore content in v1' in both
tty-backend.md and ADR-053.
S5: AsyncWrite/Stream qualified with crate origins (tokio::io,
futures_core) in the TtyHandle struct definitions.
2026-07-07 10:38:49 +00:00

32 lines
1.8 KiB
Markdown

# OQ-43: `TtyControl` as a `Clone` trait object
- **Origin**: [crates/tty/tty-backend.md](crates/tty/tty-backend.md)
(the `TtyHandle.control` field shape);
`docs/research/alknet-tty/phase-0-findings.md` OQ-TTY-01 (the trait-
shape open question the local-PTY POC resolved).
- **Status**: resolved
- **Door type**: One-way (the `TtyControl` trait shape is part of the
`TtyBackend` API surface — ADR-053), two-way (the concrete
`TtyControlHandle` newtype mechanism)
- **Priority**: medium
- **Resolution**: `TtyHandle.control` is `Option<TtyControlHandle>`.
`TtyControlHandle` is a concrete `#[derive(Clone)]` newtype wrapping
`Arc<dyn TtyControl + Send + Sync>`; the adapter clones the `Arc` to
hand a handle to the spawned control-chunk dispatcher. The
`TtyControl` trait itself is NOT `Clone` — `Clone` is not object-safe
(`fn clone(&self) -> Self` returns `Self`, which forbids `dyn`
dispatch), so `Box<dyn TtyControl + Clone>` does not compile. The
design splits the concerns: the trait stays object-safe (`Send +
Sync`, no `Clone`); the newtype carries the `Clone`-ability. The
local-PTY POC used a concrete `PtyControl` struct (inherently `Clone`
— it held `Arc<Mutex<...>>` fields); the newtype generalizes the POC's
shape so a backend produces its own control type via
`TtyControlHandle::new(Arc::new(MyControl))` without the adapter
knowing the concrete shape. The `Clone` constraint exists because the
adapter's control-chunk dispatcher needs to be handed off to the
spawned pump task (the POC's `session::drive_session` clones
`pty.control` for the client→backend pump). See ADR-053 and
`tty-backend.md`.
- **Cross-references**: ADR-053, [tty-backend.md](crates/tty/tty-backend.md),
`/workspace/alknet-tty-poc/src/local_pty.rs` (`PtyControl`)