docs(http): pre-decomposition sanity check fixes — /subscribe POST, direct-call cleanup, from_mcp output handling

Three issues found in the http crate spec sanity check that would have
caused problems during task decomposition, now fixed:

C1 — /subscribe GET→POST: the gateway's /subscribe is an invoke endpoint
carrying { operation, input } in the body, but was listed as GET (which
has no body). Flipped to POST with Accept: text/event-stream negotiating
the SSE response, consistent with /call's flat-JSON-body invariant.
Browsers using EventSource can't POST but use WebSocket for the
bidirectional path; the HTTP gateway's /subscribe is for non-browser
HTTP clients (fetch + ReadableStream). Touches ADR-042, ADR-047,
ADR-048, http-adapters.md, http-server.md.

C2 — stale direct-call references: three spots contradicted ADR-047
(which removed the POST /{service}/{op} direct-call surface) and
ADR-046 §3 (which states /{service}/{op} is no longer reserved).
Cleaned up in http-server.md (custom-routes intro + collision list) and
ADR-046 §6 (default-surface list).

W2 — from_mcp output handling: the spec's fallback for tools without
outputSchema was Type.Unknown(), but the correct fallback is the MCP
ContentBlock union (text|image|audio|resource|resource_link) — a
well-defined MCP type, not Unknown. Fixed http-mcp.md with the full
structuredContent-preferred-over-content-blocks logic (matching the TS
adapter and rmcp SDK), enriched references with specific rmcp source
files. Also added shared-dispatch-spine notes to http-mcp.md and
http-adapters.md cross-referencing the new research findings.

Research (docs/research/alknet-http-gateway-factoring/findings.md):
to_mcp and to_openapi share a dispatch spine (resolve → invoke → map).
Recommendation: extract a thin shared struct now, not a GatewayDispatch
trait — the server-integration layers (axum routes vs rmcp
StreamableHttpService) and wire-framing stay per-gateway. A third
gateway is not on the horizon; if one appears its server-integration
needs its own shape anyway.

Minor: WS route precedence note (websocket.md), OpenAPISpec
shared-type-not-shape clarification (http-adapters.md), date bumps.
This commit is contained in:
2026-07-01 05:41:07 +00:00
parent 3edc42e3b4
commit e0c6f61e6a
9 changed files with 770 additions and 31 deletions

View File

@@ -1,6 +1,6 @@
---
status: draft
last_updated: 2026-06-30
last_updated: 2026-07-01
---
# WebSocket — the Browser Bidirectional Path
@@ -122,10 +122,16 @@ mechanism of ADR-046, but a deployment that passes no custom routes gets
`/alknet/call`). The path must not collide with the reserved
gateway/`/healthz`/`/openapi.json`/MCP/custom-route paths per ADR-046's
collision rule; `/alknet/call` namespaces away from the reserved set
naturally. The upgrade runs over HTTP/1.1 (the standard `Upgrade: websocket`
header, RFC 6455) or HTTP/2 (the extended CONNECT protocol, RFC 8441);
axum/hyper supports both, and the handler does not branch on which — the
WS frame stream is the same once the upgrade completes.
naturally. A deployment that builds a custom REST projection with
`POST /{service}/{op}` routes (ADR-047 §4) coexists with the WS upgrade
at `/alknet/call` — axum's `Router::merge` prioritizes specific routes
over wildcards, so the WS upgrade's exact `/alknet/call` path wins over
any `/{service}/{op}` wildcard a custom route projection might
register, and the two do not collide. The upgrade runs over HTTP/1.1
(the standard `Upgrade: websocket` header, RFC 6455) or HTTP/2 (the
extended CONNECT protocol, RFC 8441); axum/hyper supports both, and
the handler does not branch on which — the WS frame stream is the same
once the upgrade completes.
### Framing: `EventEnvelope` over binary WS messages