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
This commit is contained in:
@@ -105,17 +105,17 @@ implementation agents.
|
||||
`STREAM_CTRL_IN=3`, `STREAM_CTRL_OUT=4`. Zero-length data chunks are
|
||||
sentinels (zero-length stdin = EOF from client; zero-length stdout
|
||||
= "drained" from server); control chunks are never zero-length.
|
||||
Changing the header, the stream-type values, or the sentinel
|
||||
semantics breaks all peers. See ADR-052 (ported as alktty ADR-001).
|
||||
- **Negotiation frame** (4-byte BE length prefix + UTF-8 JSON
|
||||
`NegotiateRequest` body) — self-contained per ADR-057 (ported as
|
||||
alktty ADR-008), not reused from alkcall's `EventEnvelope` framing
|
||||
Changing the header, the stream-type values, or the sentinel
|
||||
semantics breaks all peers. See ADR-052 (ported as alktty ADR-001).
|
||||
- **Negotiation frame** (4-byte BE length prefix + UTF-8 JSON
|
||||
`NegotiateRequest` body) — self-contained per ADR-057 (ported as
|
||||
alktty ADR-006), not reused from alkcall's `EventEnvelope` framing
|
||||
(the payload is `NegotiateRequest`, not `EventEnvelope`; alkcall's
|
||||
`FrameFramedReader` is hardcoded to deserialize `EventEnvelope`).
|
||||
The `NegotiateRequest` JSON shape is wire-stable once consumers
|
||||
exist.
|
||||
|
||||
Per ADR-093 (ported as alktty ADR-007): TTY always uses its 5-byte
|
||||
Per ADR-093 (ported as alktty ADR-008): TTY always uses its 5-byte
|
||||
format, even inside channels. The channels layer strips its 8-byte
|
||||
header and hands TTY the payload transparently — the same `wire.rs`
|
||||
code runs in both direct (`alk/tty`) and channels (`alk/channels`)
|
||||
@@ -243,35 +243,48 @@ wasm-clean" invariant — run it whenever a non-`local` module changes.
|
||||
|
||||
- `docs/plans/project-setup.md` — the current plan (phases 0–5). Phase 0
|
||||
(scaffold hygiene), Phase 1 (port core types), Phase 2 (channels
|
||||
integration + `TtySession`), and Phase 3 (`local` backend) are landed.
|
||||
Phase 4 (architecture docs + BAST schema + renumbered ADRs) and
|
||||
integration + `TtySession`), Phase 3 (`local` backend), and Phase 4
|
||||
(architecture docs + BAST schema + renumbered ADRs) are landed.
|
||||
Phase 5 (tests, including integration tests in `tests/` at the crate
|
||||
root) are not yet done.
|
||||
- `docs/architecture/` does not exist yet — it's created in Phase 4.
|
||||
The ADRs referenced below (alknet ADR-052, 053, 054, 055, 056, 057,
|
||||
077, 093) will be ported and renumbered into alktty's ADR range
|
||||
(001..008) at that time. Until then, the alknet originals at
|
||||
`/workspace/@alkdev/alknet/docs/architecture/decisions/` are the
|
||||
authoritative source — read them before non-trivial changes to the
|
||||
wire format, trait, or adapter.
|
||||
- Key ADRs that inform this crate's design (alknet numbers → planned
|
||||
alktty numbers):
|
||||
root) is not yet done.
|
||||
- `docs/architecture/` is created in Phase 4. The alknet ADRs
|
||||
referenced below (052, 053, 054, 055, 056, 057, 077, 093) are ported
|
||||
and renumbered into alktty's ADR range (001..008) in
|
||||
`docs/architecture/decisions/`. The alknet originals at
|
||||
`/workspace/@alkdev/alknet/docs/architecture/decisions/` remain the
|
||||
authoritative source for any ADR not yet ported — read them before
|
||||
non-trivial changes to the wire format, trait, or adapter.
|
||||
- Key ADRs that inform this crate's design (alknet numbers → ported
|
||||
alktty numbers, 1:1 in the order the plan lists them):
|
||||
- ADR-052 → 001 — two-carriage wire format (JSON negotiation + raw
|
||||
chunks); the 5-byte chunk header
|
||||
chunks); the 5-byte chunk header. The Phase 7 control-channel
|
||||
split (`STREAM_CTRL_IN` = 3 client→server, `STREAM_CTRL_OUT` = 4
|
||||
server→client) is an amendment inside this ADR, mirroring alknet
|
||||
(it was not a standalone ADR there either).
|
||||
- ADR-053 → 002 — `TtyBackend` trait (the inversion point between
|
||||
wire-format adapter and backend crates)
|
||||
- ADR-050 → 003 — dynamic resource ownership for runtime-spawned
|
||||
terminal sessions
|
||||
- ADR-054 → 004 — single crate with `local` feature (resolves the
|
||||
alknet cyclic-dep workaround; the local backend is folded in)
|
||||
- ADR-055 → 005 — exit-code reporting (`{"type":"exit","code":N}` on
|
||||
- ADR-054 → 003 — local backend placement. Ported with the
|
||||
single-crate rewrite: alktty folds the local backend in behind a
|
||||
`local` feature (resolves the alknet cyclic-dep workaround that
|
||||
motivated the original sibling-crate decision; the ADR records both
|
||||
the original alknet decision and the alktty consolidation).
|
||||
- ADR-055 → 004 — exit-code reporting (`{"type":"exit","code":N}` on
|
||||
`ctrl_out`; `code: -1` on wait-failure)
|
||||
- ADR-056 → 006 — control-message split (`ctrl_in` client→server,
|
||||
`ctrl_out` server→client; the bidirectionality fix)
|
||||
- ADR-093 → 007 — TTY always uses its 5-byte format inside channels
|
||||
(reverses ADR-077; channels strips its 8-byte header transparently)
|
||||
- ADR-057 → 008 — negotiation framing is self-contained (not reused
|
||||
- ADR-056 → 005 — backend cleanup on session cancel (drop of
|
||||
`exit_code` future kills the session target; the
|
||||
kill-on-`Drop` contract on the `TtyBackend` trait)
|
||||
- ADR-057 → 006 — negotiation framing is self-contained (not reused
|
||||
from alkcall's `EventEnvelope` framing)
|
||||
- ADR-077 → 007 — TTY inside channels (reversed by ADR-093/008; kept
|
||||
for historical context with its reversal notice pointing to 008)
|
||||
- ADR-093 → 008 — TTY always uses its 5-byte format inside channels
|
||||
(reverses ADR-077; channels strips its 8-byte header transparently)
|
||||
- ADR-050 (dynamic resource ownership) is an alkcall/alknet-core ADR,
|
||||
not a tty-specific one — it is not ported into alktty's ADR range.
|
||||
The access-control work that declares against the ADR-050 model
|
||||
(scope-gate at negotiation, backend-driven `resource_id()` ownership
|
||||
check) is described in `tty-adapter.md` and the ADR-001/002 ported
|
||||
docs, which reference ADR-050 by its alknet number.
|
||||
- If a TODO references a "Phase 7" note or a design direction that an
|
||||
ADR has since decided against, the TODO is stale — remove it and
|
||||
align with the ADR. Do not implement the rejected design.
|
||||
|
||||
Reference in New Issue
Block a user