A protocol seam is a documented internal boundary. AI agents and contributors must respect it — never cross a seam except through its protocol.
- Where:
src/httpware/client.py↔src/httpware/middleware/. - Contract: the middleware chain is composed once at client construction and frozen for the client's lifetime. Both worlds follow the same contract; the only difference is the per-world type:
AsyncClientcomposesAsyncMiddlewareviacompose_async(the continuation type isAsyncNext), andClientcomposesMiddlewareviacompose(the continuation type isNext). Bothcomposeandcompose_asynclive insrc/httpware/middleware/chain.py. The chain bottom (the "terminal") is internal: it callsself._httpx2_client.send(request), mapshttpx2errors tohttpwareerrors, and raises aStatusErrorsubclass on 4xx/5xx. Same lifecycle rules in both worlds. - Rule: mutating the chain after construction is not supported. Per-request behavior goes through
httpx2.Request.extensionsor throughextensions=kwargs at call sites.
Phase decorators (declared alongside Middleware/AsyncMiddleware, Next/AsyncNext in src/httpware/middleware/__init__.py) let a middleware target a specific phase of the request lifecycle.
httpware deliberately does not ship a separate OTel tracing middleware layer. opentelemetry-instrumentation-httpx already covers transport-level tracing; a separate httpware middleware would duplicate it. Observability that httpware does add lives where it has information httpx2 lacks — the Retry and Bulkhead span events on the active span (see Resilience).