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

1.8 KiB

OQ-43: TtyControl as a Clone trait object

  • Origin: 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, /workspace/alknet-tty-poc/src/local_pty.rs (PtyControl)