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:
@@ -175,6 +175,22 @@ Option 1 is preferred if a test can be written for it (feed a handler
|
||||
a schema-bypassing value directly — the unit-testable seam is
|
||||
`make_tty_open_handler` with a hand-built `input`).
|
||||
|
||||
**Resolution (2026-09-05)**: option 1 implemented. `make_tty_open_handler`
|
||||
accepts the channel's `BiStream` and writes a `malformed_negotiation`
|
||||
error frame via the shared `crate::adapter::send_negotiation_error`
|
||||
(now `pub(crate)`) before returning — all three post-open failure
|
||||
classes are client-visible through the unchanged M1 peek. The
|
||||
review's reachability analysis is refined by the R5 fail-fast
|
||||
discovery: `TtySession::open_via_channels` parses `params` locally
|
||||
before opening, so the consumer never sends a value it can't parse
|
||||
itself — but the producer-side handler is still the reachable seam for
|
||||
direct `ChannelClient` callers (the schema is deliberately partial; a
|
||||
schema-valid `cwd: 42` fails the typed parse). Tests: the
|
||||
`make_tty_open_handler` seam test (channels.rs) and a real-registry
|
||||
end-to-end test via `ChannelClient::open_channel` (testing.rs). ADR-009
|
||||
amended (§"Parse-failure error frame"); `tty-adapter.md` error table
|
||||
updated.
|
||||
|
||||
---
|
||||
|
||||
### R5. `open_via_channels` fail-fast surfaces as `NegotiationSerialize`
|
||||
@@ -257,13 +273,14 @@ version-skew note in ADR-009); option 1 at that point.
|
||||
| R1 | ADR-009 never written | write the ADR | small | none | ✅ resolved (`37ae07a`) |
|
||||
| R2 | stale docs from the L1 redesign | align with ADR-009 | trivial | none | ✅ resolved (`37ae07a`) |
|
||||
| R3 | install-time identity snapshot | accepted design (hub-proxy rationale) | none | none | ✅ closed as intended |
|
||||
| R4 | silent death on parse-failure path | error frame or documented asymmetry | small | low | ⬜ open (deferred) |
|
||||
| R4 | silent death on parse-failure path | error frame or documented asymmetry | small | low | ✅ resolved (option 1) |
|
||||
| R5 | `NegotiationSerialize` mislabel on fail-fast | additive variant (with `#[non_exhaustive]` decision) | small | medium (semver) | ⬜ open (deferred) |
|
||||
|
||||
R4/R5 are deferred deliberately: both touch the consumer-facing error
|
||||
surface, both are cheap, and neither is reachable-by-design today.
|
||||
Batch them with the first post-1.0 API decision rather than churning
|
||||
the error enum before a consumer exists.
|
||||
R5 is deferred deliberately: it touches the consumer-facing error
|
||||
surface, it is cheap, and the producer-side parse-failure arm (R4's
|
||||
concern) is now client-visible regardless. Batch it with the first
|
||||
post-1.0 API decision rather than churning the error enum before a
|
||||
consumer exists.
|
||||
|
||||
## Notes
|
||||
|
||||
|
||||
Reference in New Issue
Block a user