Merge branch 'wt/review-002-gw16-status-drift'

# Conflicts:
#	src/gateway/dispatch.rs
This commit is contained in:
2026-08-31 01:52:37 +00:00
7 changed files with 191 additions and 77 deletions
+3 -1
View File
@@ -545,7 +545,9 @@ adapter contract from alkcall ADR-022 faithful on the error axis — no
silent dropping of error contracts. The `/batch` endpoint documents no
HTTP 500 (review-002 PRJ-21): every per-entry dispatch failure is an
in-band `results[]` entry; the only HTTP error status is the
request-level cap failure (400). `BatchResultEntry.error` references
request-level cap failure (422; GW-16 unified it with the
`INVALID_INPUT → 422` mapping — it was hand-rolled as 400 before
review-002). `BatchResultEntry.error` references
the `BatchError` component (review-002 PRJ-16b): the serialized
`CallError` as a oneOf over the six protocol-code envelopes plus a
generic arm carrying the operation-declared codes. See alkcall ADR-016.
+11 -7
View File
@@ -217,7 +217,7 @@ the response is `text/event-stream` (negotiated via
check before dispatch. The two methods diverge only on the return shape
(stream vs single envelope). Streaming invokes set `deadline: None`
subscriptions are unbounded by contract, unlike the 30 s gateway
deadline on Once-op invokes (see Error Mapping below).
deadline on Once-op and sink invokes (see Error Mapping below).
- For each `ResponseEnvelope` the stream yields, writes an SSE `data:` frame:
`Ok(value)``data:` frame with the output serialized as JSON; `Err`
SSE error event with the `CallError` serialized, then close (an `Err` is
@@ -358,7 +358,7 @@ Schemas") map to HTTP status codes:
|-------------|-------------|-------|
| `NOT_FOUND` (operation not registered, or Internal op) | `404` | |
| `FORBIDDEN` (insufficient scopes, or unauthenticated) | `401` (no token) / `403` (token present) | |
| `INVALID_INPUT` (schema mismatch) | `422` | |
| `INVALID_INPUT` (input-data fault) | `422` | one status for every trigger: schema mismatch, `/publish` NDJSON framing faults (empty body, malformed first line, missing `operation`/`chunk`, per-line cap, body-read failure), and the `/batch` over-cap reject (`INVALID_INPUT` is an input-data fault wherever it fires — GW-16 unified the formerly hand-rolled 400s onto this row) |
| `INVALID_OPERATION_TYPE` (wrong dispatch path for the op's type) | `422` (token present) / `401` (no token) | consistent across `/call`, `/batch`, `/publish` — a client fault, never a server fault |
| `TIMEOUT` | `504` | `retryable: true` |
| `INTERNAL` | `500` | |
@@ -379,11 +379,15 @@ ADR-016) and `from_openapi`-imported codes are prefixed `HTTP_<status>`
to avoid collision with protocol codes.
**Per-endpoint dispatch deadline.** Once-op invokes (`/call`, `/batch`
entries, `/search`, `/schema`, and the `/publish` final envelope) are
bounded by a 30 s gateway deadline (`GatewayDispatch::invoke` wraps the
registry invoke in `tokio::time::timeout`); a hung handler surfaces as
a `TIMEOUT` error (`504`, `retryable: true`), not an indefinitely-held
HTTP request. Streaming invokes (`/subscribe`) are unbounded —
entries, `/search`, `/schema`) and sink invokes (the `/publish` final
envelope) are bounded by the 30 s gateway deadline
(`GatewayDispatch::invoke` and `GatewayDispatch::invoke_sink` wrap the
registry invoke in `tokio::time::timeout`, GW-17); a hung handler —
Once or sink — surfaces as a `TIMEOUT` error (`504`, `retryable:
true`), not an indefinitely-held HTTP request. The sink wrapper bounds
the whole dispatch (chunk pacing included), so the final envelope
always arrives, or the deadline trips, within the window. Streaming
invokes (`/subscribe`) are unbounded —
subscriptions are long-lived by contract (alkcall ADR-021 sets
`deadline: None` for the streaming branch). The same time/bytes split
governs the **outbound** half of an imported subscription: