main
7
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
49f70d9efb |
feat(fuzz): cargo-fuzz workspace, 3 wire-surface targets; bump alkcall 0.8.1
- Bump alkcall 0.8.0 -> 0.8.1 (duplicate adopt/open channel-state destruction fix from alkcall's fuzz campaign; AGENTS.md stale-pin fix). - Adopt fuzzing per docs/plans/fuzzing.md (mirrors alkcall's docs/research/fuzzing.md as-built layout): fuzz/ workspace with own [workspace] table, nightly pinned for the subtree only, invariant logic in a stable-toolchain shared crate. - Targets: chunk_frame (5-byte chunk codec), negotiation_frame (negotiation framing + NegotiateRequest + error_response_bytes + the cross-codec peek-disambiguation seam), control_json (ControlMessage JSON + signal_from_name). src/local/ out of scope (not wire-attacker-shaped). - 328 committed seeds (deterministic generator); grown corpora gitignored. Corpus replay on stable is the standing fuzz gate (cargo test --manifest-path fuzz/shared/Cargo.toml). - Detached-runner rule (fuzz/run-detached.sh): campaigns never run in the foreground of an agent session - OOM in a target must cost the fuzzer, never the session host. - Root Cargo.toml gained [workspace] members/exclude (MSRV vs fuzz nightly dev-deps footgun) and fuzz/ in the publish exclude. Verification: cargo test (113), cargo test --all-features (156), clippy stable + wasm32-unknown-unknown, fmt, doc, publish dry-run, corpus replay 328 seeds - all green. 10-min detached campaigns on all three targets: 0 crashes/hangs/OOMs/leaks (chunk_frame cov-saturated at 710 edges; negotiation_frame 3.69M execs cov 3157; control_json 8.6M execs cov 2035). Corpus replay caught three harness-model mismatches pre-campaign (payload-slice shape; the negotiation framing layer is length-prefix-only - Json unreachable from read_frame; zero-length frames admitted by the reader, rejected by the adapter's parse). Campaigns recorded in docs/plans/fuzzing.md section 8. |
||
|
|
af9dbaedea |
feat: allocation moves into the channels establisher (alkcall 0.6.0 / review 007 R-01) — bump to 0.3.0
- Bump alkcall 0.5.0 -> 0.6.0 (Establishment plan payload, ADR-049 amendment 2); alktty 0.2.0 -> 0.3.0 (breaking channels-path allocation-failure shape) - make_tty_establisher runs backend.allocate; failure maps to EstablishmentError::DialFailed (channel:open_failed reason dial_failed); the TtyHandle crosses to the pump handler via a private per-open AllocatedHandle one-shot slot (the handle is not Sync — it cannot be the ChannelPlan directly) - make_tty_open_handler gains the Option<ChannelPlan> parameter; a plan drives the new drive_session_pre_allocated (pumps only), a None plan falls back to drive_session_pre_negotiated (inline validate-and-allocate — defense-in-depth for no-establisher registrations) - tty_open_spec's channel:open_failed ErrorDefinition declares dial_failed (four reachable reasons) - Pinned in-band allocate test flipped: allocate_failure_fails_open_as_dial_failed (end-to-end, no channel survives); establisher unit gate for the plan slot + failure mapping - ADR-010 amended (§2A; §2 kept as historical record), tty-adapter.md, session/channels/adapter docs, CHANGELOG, AGENTS.md alkcall pin updated Verification: cargo test (113) + --all-features (137), clippy -D warnings (native + wasm32, default + all-features), fmt, doc, wasm32 check — all clean |
||
|
|
e2fa32b3c7 |
feat: channels-path establisher migration (alkcall 0.5.0 / ADR-049) — bump to 0.2.0
Adopt alkcall 0.5.0's channel-open establishment phase (ADR-049 — review 006 E-01 + N-1) and migrate the channels-path semantic failures per its §5 sequencing (alktty ADR-010). - `register_openable` registers `channels/tty/sub` with an establisher (`register_openable_with_establisher`): full `NegotiateRequest` parse of schema-valid `input`, `carriage == "raw"`, non-empty `cmd`, backend lookup, and the ADR-050 ownership check run before the open reply; rejections are `channel:open_failed` with `details.reason` (`unknown_resource` / `handler_error` / `timeout`) — no phantom channel (the SSH contract holds consumer-visibly) - `backend.allocate` deliberately stays in the pump handler: `Establishment` is payloadless so the `TtyHandle` cannot cross the establisher→handler boundary, and re-allocating would violate ADR-005's kill-on-Drop contract — `allocate_failed` remains the one in-band failure class on the channels path (pinned by test) - `TtySessionError::ChannelsOpen` carries alkcall's typed `ChannelOpenError` (`#[from]`) instead of a flattened `String` — the N-1 fix at alktty's layer (breaking) - channels-path semantic failures change shape from `NegotiationRejected` in-band frames to `channel:open_failed` call errors (breaking); the direct-ALPN path is unchanged - `tty_open_spec()` gains a `description` (review 006 E-02) and an ErrorDefinition for `channel:open_failed` (ADR-016 — disclosed via services/schema) - alkcall = "0.5.0"; version 0.2.0; ADR-010 + ADR-009 amendment + tty-adapter.md + CHANGELOG Verification: cargo test (112 lib + integration), cargo test --all-features (136), clippy --all-targets -D warnings (host + wasm), fmt --check, cargo doc --no-deps clean; wasm32-unknown-unknown check confirms the default crate stays wasm-clean. |
||
|
|
3baa993edb |
fix: local-bridge robustness, scope-gate order, sentinel contract; packaging/docs closeout (P8-P11, P2/P3/P15)
Closes review #003 (prepublish review for v0.1.0). - P8: StdinSink::poll_shutdown parks an inflight reserve+send on a full channel (waker registered) — a stdin blast followed by EOF delivers the EOF instead of stranding it - P9: five poisoned-lock .expect() sites -> unwrap_or_else(into_inner) - P10: three thread-spawn .expect() sites -> TtyError::AllocFailed - P5: tty:open scope gate runs before carriage/cmd/backend-lookup checks (no backend-name enumeration differential for unscoped ids) - P11: recv_stdout terminates on the zero-length drained sentinel; the sentinel is no longer yielded as an item (doc was already the contract); stderr has no sentinel (doc noted) - P2: exclude AGENTS.md + docs/plans/, drop dead Cargo.lock and docs/research/ entries (package list: 42 files, 659.2KiB) - P3: AGENTS.md phase status (all five landed), ADR range 001..009 (+ alktty-native ADR-009 in the mapping), alkcall guidance corrected to v0.4.x / pin "0.4.0"; architecture README ADR-009 row + landed-phase status - P15: backend.rs doc typo; redundant tokio-stream dev-dep removed; NegotiationError::Io arm logs; set_identity failure logs; input_pump.abort() at session end; TtySessionError::Open carries the accept_bi StreamError (no io::Error flattening); borrowing deserialize in open_via_channels (no params.clone()); error_response_bytes guards an "error" key in fields; trivial inline comments promoted/removed; plan-doc test counts + doc front-matter refreshed; session tests that raced session teardown under the abort change use a GatedBackend (exit held until released) Verification: cargo test 104 lib / --all-features 147; clippy (all-targets + wasm32) -D warnings; fmt; wasm check; doc 0 warnings; publish dry-run OK. |
||
|
|
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%. |
||
|
|
b3f50d1836 |
phase 4: architecture docs + BAST schema + renumbered ADRs
Port the alknet-tty architecture docs into alktty and add the BAST document for the alk/tty wire format. Docs-only; no Rust source changes. Spec docs (docs/architecture/, flat layout — single-crate repo): - overview.md — crate purpose, two-carriage model, deps, ALPN, backend location map, feature gates - tty-wire.md — 5-byte chunk codec, control channel split (STREAM_CTRL_IN=3 / STREAM_CTRL_OUT=4), sentinels - tty-backend.md — TtyBackend trait, TtyHandle, TtyControl, REQ-TTY-01 (backends need not be natively async) - tty-adapter.md — TtyAdapter, three-pump driver, exit-chunk ordering (ADR-004), cancel cleanup (ADR-005), access control - tty-local.md — LocalTtyBackend (local feature module), PTY + pipe modes, REQ-TTY-02 (signal forwarding to process group) - README.md — architecture index ADRs (docs/architecture/decisions/, renumbered 001..008 from alknet 052,053,054,055,056,057,077,093 in order): - 001 wire format + two-carriage model (incl. Phase 7 control- channel split amendment) - 002 TtyBackend trait + TtyHandle - 003 local backend placement (records both the alknet sibling- crate decision and the alktty single-crate consolidation behind a local feature) - 004 exit code on a control chunk - 005 backend cleanup on session cancel - 006 self-contained negotiation framing - 007 tty inside channels (reversed by 008; kept for historical context with reversal notice) - 008 channels pure channel multiplexing (reverses 007; TTY always uses its 5-byte format) BAST document (docs/architecture/tty-bast.md): - Normative JSON spec for the alk/tty wire format, conforming to the BAST meta-schema at https://alk.dev/bast/v1/schema - 5-byte chunk header (struct, big-endian: stream_type uint8, length uint32) + StreamType enum (Stdin=0..CtrlOut=4) - ControlMessage union (field-name discriminator on type: resize/signal/eof/exit) with documented deviation that on-wire control payloads are UTF-8 JSON, not BAST's binary union encoding - NegotiationFrame (4-byte BE length + UTF-8 JSON body) + NegotiateRequest / TerminalParams JSON shapes - StreamType enum deviation noted: on-wire uint8, not BAST's standard u32 enum index (chunk header is 5 bytes, not 8) - alktty does not depend on alktype; the hand-rolled wire.rs is the runtime codec, the BAST is the human-readable contract AGENTS.md: fixed the ADR mapping table to match the plan's 8-to-8 mapping (the previous table substituted ADR-050 for 054, relabeled 056 as control-message split, dropped 077, and added a new control-split ADR at 006 — inconsistent with both the plan and the prose). ADR-050 (dynamic resource ownership) is an alkcall/alknet- core ADR, not tty-specific, and is not ported; the Phase 7 control split stays as an amendment inside ADR-001, mirroring alknet. Verification (all pass, no Rust source changed): - cargo test (80 passed) - cargo test --all-features (103 passed) - cargo clippy --all-targets -- -D warnings (clean) - cargo fmt --check (clean) - cargo check --target wasm32-unknown-unknown (clean) - cargo clippy --target wasm32-unknown-unknown -- -D warnings (clean) - cargo doc --no-deps: 9 pre-existing intra-doc-link warnings in src/session.rs and src/channels.rs (untouched by this commit; not introduced here) - BAST JSON parses; StreamType indices match wire.rs constants (0=Stdin..4=CtrlOut) - all markdown cross-reference links resolve |
||
|
|
712e7ae071 |
chore: add .gitignore and AGENTS.md; fix implementation-specialist conventions
The scaffold was missing basic repo hygiene, which led to drift: the implementation-specialist agent's Project Conventions section was a verbatim copy from alkcall (referencing OperationEnv, EventEnvelope, Capabilities, alktype as a dep, call.aborted, vendored core types — none of which exist in alktty), so spawned agents got alkcall conventions injected. - .gitignore: target/, node_modules/, .worktrees/ (matches alkcall/alktype) - AGENTS.md: alktty-specific operating instructions — Git Workflow, 14 Project Conventions (5-byte chunk header wire format, TtyBackend trait one-way door, local feature isolation / WASM invariant, BAST-as-doc-not-dep, 3-arg/4-arg ownership shapes, alknet ADR→alktty ADR renumbering plan), Verification Commands (incl. wasm checks), Architecture Context (phase status, ADR index, port origin) - .opencode/agents/implementation-specialist.md: replace stale alkcall Project Conventions section with alktty-appropriate rules that mirror AGENTS.md Verification: cargo test (80 passed), cargo fmt --check, cargo build --target wasm32-unknown-unknown all clean. No source changes. |