96692d3b6a67cec443cee34086f3211073ab54f5
4
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
96692d3b6a |
feat: channels path carries the negotiation in the open op (L1, L3)
The channels path no longer carries a second negotiation frame on the channel's data stream (ADR-009). The open op's registry-validated input IS the negotiation: - producer: make_tty_open_handler parses the open op's input into a NegotiateRequest and drives the new drive_session_pre_negotiated (same three-pump driver as drive_session, minus the wire-frame negotiation phase; validate/allocate factored into validate_and_allocate, shared by both paths). Post-open failures (unknown backend, allocate_failed, ownership denial) still go to the client as a 0x00-prefixed negotiation error frame, so the consumer's M1 disambiguation read applies unchanged. The tty:open scope gate is enforced by the registry's AccessControl (not re-checked in the handler). - consumer: open_via_channels parses params locally (fail-fast before a channel is allocated), opens the channel, and starts raw-chunk mode directly (from_halves_raw — no negotiation write; the 0x00-error-frame peek retained). - tty_open_spec's input schema is now the partial NegotiateRequest shape (carriage/backend/cmd required; backend params stay free-form — raw JSON Schema is permissive on unknown keys). Prerequisites landed upstream: alkcall 0.4.0 enforces OperationSpec.input_schema at dispatch (the registry check this design leans on never existed before); alkcall 0.4.1 parks early-arrival chunks for un-adopted channels instead of dropping them — the open response / producer's-first-write race was silently losing the first chunks (found by L3's test; the session never resolved). L3: open_via_channels + from_bidi_stream_via now covered end-to-end (5 session tests + pre-negotiated adapter test + shared-harness tests in the new crate::testing module; the channels harness moved there so session tests share it). Verification: cargo test 93 lib (default), 116 lib + 19 integration (--all-features); clippy -D warnings native + wasm clean; fmt clean; doc 0 warnings; wasm check clean. |
||
|
|
99441530ab |
fix: address code review #001 findings (M1, M2, L2, L4, L5, N1-N3, N5)
- M1: TtySession now handles the negotiation-rejection error frame. from_halves peeks the first response byte (ADR-052 §5 disambiguation) and returns NegotiationRejected on a 0x00-prefixed error frame; ChunkReader gains peek_stream_type/read_chunk_after_peek. - M2: wait() now surfaces MalformedExitChunk instead of collapsing it to NoExitChunk. The exit watch channel carries a cloneable ExitOutcome enum; MalformedExitChunk carries a String. - L2: add EmittingBackend + recv_stdout_and_stderr_route_backend_data test covering the consumer read-pump stdout/stderr routing. - L4: MockBackend/MockControl/MockStdinSink are now #[cfg(test)] pub(crate), removing them from the public API. - L5: cargo fmt (the BAST drift test was unformatted). - N1: fix all 9 rustdoc intra-doc links. - N2: fix stale doc paths (crates/tty/ and docs/research/). - N3: amend AGENTS.md §14 to accurately describe the local module's libc::kill unsafe blocks. - N5: consolidate nanos_seed into tests/common/mod.rs. Verification: cargo test (84), cargo test --all-features (107), clippy clean (native + wasm), fmt clean, doc clean, wasm check clean. Coverage: session.rs 79.71% -> 87.43%, total 90.74% -> 91.47%. |
||
|
|
e1610c2825 |
phase 3: port alknet-tty-local behind the local feature
Folds the alknet-tty-local crate into alktty as a feature-gated local
submodule (the single-crate + local-feature design from ADR-054; the
cyclic-dep workaround the mono-repo needed doesn't apply to one crate).
src/local/mod.rs:
- Module root, re-exports LocalTtyBackend, declares backend/pipe/pty
- Doc-commented as feature-gated + non-wasm by design (portable-pty +
tokio::process need a real OS; enabling local on a wasm target is a
build error by design)
src/local/backend.rs:
- LocalTtyBackend: implements crate::backend::TtyBackend
- allocate() dispatches on params.terminal: Some -> pty::allocate_pty,
None -> pipe::allocate_pipe (ADR-054)
- 7 tests: PTY/pipe dispatch, empty-cmd rejection (both modes),
resource_id returns None, new() constructs
src/local/pty.rs:
- portable_pty + 3 std threads (reader/writer/waiter) feeding tokio
mpsc/oneshot (REQ-TTY-01 blocking->async bridge)
- PtyControl: resize + REQ-TTY-02 process-group signal forwarding
(libc::kill(-pgid, sig) + kill(pid, sig) fallback + ChildKiller::kill
for unknown names)
- LocalExitFuture: ADR-056 kill-on-Drop guard (ChildKiller on cancel,
Option::take disarms on resolve)
- StdinSink: AsyncWrite over mpc::Sender<StdinCmd> with in-flight
reserve+send parking for full-channel backpressure
- 7 tests incl. process-group reach (bash -c sleep), cancel-cleanup,
unknown-signal fallback
src/local/pipe.rs:
- tokio::process::Command + tokio_util::io::ReaderStream
- PipeControl: no-op resize, libc::kill(pid, sig) with SIGKILL
fallback for unknown names (pid-only, no process group - documented
limitation of the runner case)
- PipeExitFuture: in-place Child::wait() poll (avoids self-referential
borrow) + ADR-056 kill-on-Drop guard (start_kill on cancel)
- BytesStream: wraps ReaderStream, strips io::Error to EOF
- 9 tests incl. separate stderr, SIGTERM=-15, SIGKILL=-9 fallback,
cancel-cleanup pid probe (kill(pid,0) returns ESRCH)
Import migration: alknet_tty::backend::{...} -> crate::backend::{...},
alknet_tty::control::signal_from_name -> crate::control::signal_from_name.
Cargo.toml: no changes needed (portable-pty + tokio-util optional, libc
under cfg(unix), and the local feature wiring tokio/process +
tokio/rt-multi-thread were already in the scaffold per Phase 0).
Also fixes pre-existing rustfmt drift in adapter.rs/channels.rs/
session.rs/lib.rs left by the phase 2 commit (cargo fmt --check without
--features local reported 28 diffs; cargo fmt does not accept
--features, so the earlier 'clean' check was a false negative — the
check errored on the unknown flag and grepped an empty stdout). Lesson:
run cargo fmt --check with no feature flags; cargo fmt doesn't gate on
features.
Verification:
- cargo test --features local -> 103/103 pass (was 80 at phase 2 end;
+23 new tests across the 3 local modules: 7 backend, 7 pty, 9 pipe)
- cargo test (no features) -> 80/80 pass (local module not compiled)
- cargo clippy --features local --all-targets -> clean
- cargo clippy --all-targets (no features) -> clean
- cargo check --target wasm32-unknown-unknown -> clean (default crate
stays wasm-clean; local is feature-gated and non-wasm by design)
- cargo fmt --check -> clean
|
||
|
|
29dfa1a6af |
phase 1: port core types from alknet-tty
Port wire.rs, control.rs, negotiation.rs, backend.rs, adapter.rs from
alknet-tty (the protocol half of the alknet mono-repo split) into the
single alktty crate. All 65 unit tests pass; cargo check on
wasm32-unknown-unknown is clean (no features); clippy is clean.
Migration changes:
- adapter.rs imports: alknet_core::{auth, ownership, types} →
alkcall::core::{auth, ownership, types + Connection/HandlerError/
ProtocolHandler/StreamError re-exported at the crate root}
- adapter.rs TtyAdapter::alpn(): b"alknet/tty" → b"alk/tty"
- backend.rs BoxFuture type alias: Pin<Box<dyn Future + Send +
'static>> → futures::future::BoxFuture<'static, T> (matches alkcall
convention; alktty declares futures = 0.3 directly)
- doc comments: alknet/tty → alk/tty, alknet-tty → alktty, the
alknet-tty-poc and findings.md cross-references trimmed to local
docs
Test shape: alkcall's Identity.resources is HashMap<String, Vec<String>>
(was HashMap<String, String> in alknet-core); the tests construct with
StdHashMap::new() and infer the new shape from the struct, so no test
edits were needed. alkcall's OwnershipStore::record is 3-arg (no
action) and OwnershipProvider::owns is 4-arg (with action) — both
already match what the ported code calls.
Cargo.lock committed (matches alkcall/alktype convention; still
excluded from the published package via Cargo.toml's exclude list).
|