fix: channels parse-failure path writes the negotiation error frame (R4)
Review #002 R4 — a NegotiateRequest parse failure of the open op's schema-validated input died silently (log + return, channel teardown, consumer observed NoExitChunk — indistinguishable from a crashed producer), while the other post-open failure classes (unknown backend, allocate_failed, ownership denial) wrote the 0x00-prefixed error frame. - make_tty_open_handler now accepts the channel's BiStream and writes a malformed_negotiation frame via the shared crate::adapter::send_negotiation_error (now pub(crate)) before returning; the consumer's M1 peek surfaces NegotiationRejected unchanged - the frame type and layout are unchanged (ADR-001 wire-stable contract); no new frame type, no wire change - tests: make_tty_open_handler seam test with a hand-built schema-bypassing input (cwd: 42) + a real-registry end-to-end test via ChannelClient::open_channel (bypasses open_via_channels's local fail-fast parse — R5's path — so it exercises the producer handler) - docs: ADR-009 amended (Parse-failure error frame section); tty-adapter.md malformed_negotiation row covers both paths; session.rs post-open failure lists updated; review #002 R4 resolved Note: the review's "unreachable end-to-end" premise was refined — open_via_channels parses params locally (fail-fast) so a TtySession consumer never hits the producer-side parse failure, but direct ChannelClient callers do; the schema is deliberately partial so a schema-valid value (cwd typed as a number) reaches the handler. Verification: cargo test 95 lib (default) / 138 (--all-features); clippy -D warnings native + wasm clean; fmt clean; doc 0 warnings.
This commit is contained in:
@@ -6,6 +6,11 @@ Accepted (2026-09-05). Resolves review #001 L1. Prerequisites: alkcall
|
||||
0.4.0 (call-time `input_schema` enforcement) and alkcall 0.4.1
|
||||
(early-arrival parking for un-adopted channels).
|
||||
|
||||
Amended 2026-09-05 (review #002 R4): a `NegotiateRequest` parse failure
|
||||
of the schema-validated `input` is now a client-visible
|
||||
`malformed_negotiation` error frame, not a silent teardown — see
|
||||
§"Parse-failure error frame (R4 amendment, 2026-09-05)".
|
||||
|
||||
## Context
|
||||
|
||||
Before this ADR, the channels path carried the negotiation twice. The
|
||||
@@ -73,9 +78,10 @@ so the consumer's ADR-001 §5 disambiguation read applies unchanged.
|
||||
A `NegotiateRequest` parse failure of the schema-validated `input`
|
||||
(the schema is deliberately partial — `carriage`/`backend`/`cmd`
|
||||
required, `tty`/`cwd`/`env`/backend-params free-form so the opaque
|
||||
ADR-053 params pass through) is a handler-side failure; the handler
|
||||
logs and returns without writing an error frame (the channel is torn
|
||||
down by the wrapper's teardown task).
|
||||
ADR-053 params pass through) was originally a handler-side failure
|
||||
logged and returned without an error frame. The R4 amendment below
|
||||
makes it a client-visible error frame like every other post-open
|
||||
failure.
|
||||
|
||||
### Consumer side
|
||||
|
||||
@@ -102,6 +108,37 @@ prefix starting `0x00`) from a raw chunk (`stream_type` in `{1, 2, 4}`).
|
||||
This ADR removes a frame from the channels data stream; it does not
|
||||
change any frame that remains.
|
||||
|
||||
### Parse-failure error frame (R4 amendment, 2026-09-05)
|
||||
|
||||
All three post-open failure classes on the channels path are now
|
||||
client-visible through the same `0x00` error-frame peek
|
||||
(`from_halves_raw`):
|
||||
|
||||
1. **Semantic validation failures** (`carriage != "raw"`, empty `cmd`,
|
||||
unknown backend, `allocate_failed`, ownership denial) — error
|
||||
frames from `validate_and_allocate` (unchanged).
|
||||
2. **`NegotiateRequest` parse failure of schema-valid `input`** — the
|
||||
open handler accepts the channel's `BiStream` and writes a
|
||||
`malformed_negotiation` frame
|
||||
(`{"error":"malformed_negotiation","message":"..."}`) via the shared
|
||||
`crate::adapter::send_negotiation_error`, then returns. Reachable
|
||||
despite the registry's schema check because the schema is
|
||||
deliberately partial: e.g. `cwd` typed as a number passes the schema
|
||||
(unknown-key/type fields pass through for the opaque ADR-053
|
||||
params) but fails the typed parse.
|
||||
3. **Schema-invalid `input`** — rejected at dispatch by the registry
|
||||
(alkcall 0.4) before any handler runs: a `CallError` on the open
|
||||
op, no channel allocated, no error frame (unchanged).
|
||||
|
||||
This replaces the original behavior (log + return, channel teardown,
|
||||
consumer observes `NoExitChunk` — indistinguishable from a crashed
|
||||
producer). The error-frame layout is unchanged (ADR-001's wire-stable
|
||||
contract); no new frame type, no new stream type. Tests: the
|
||||
`make_tty_open_handler` seam test (hand-built `input` bypassing the
|
||||
schema, `channels.rs::tests`) and the real-registry end-to-end test
|
||||
(`testing.rs`); both assert the consumer-side `0x00` peek observes
|
||||
`malformed_negotiation`.
|
||||
|
||||
## Consequences
|
||||
|
||||
**Positive:**
|
||||
|
||||
Reference in New Issue
Block a user