Guidance for AI agents (Claude Code, etc.) working in this repository.
httpware is a Python HTTP client framework with sync and async clients for building resilient service clients. It ships under the modern-python org and is a thin opinionated wrapper around httpx2: it re-exports httpx2.Request/httpx2.Response, adds a middleware chain, typed response decoding, and a status-keyed exception tree raised automatically on 4xx/5xx.
Where to find what:
architecture/(repo root) — the per-capability living truth, one file per capability (architecture/README.mdis the index). The promotion target on every ship. Read the relevant file before changing that capability.planning/README.md— the planning convention (Quick path + the two-axis convention) and the generated change Index.planning/changes/<YYYY-MM-DD.NN-slug>/— per-change bundles (design.md+plan.md, orchange.mdfor the lightweight lane).planning/decisions/,planning/audits/,planning/retros/,planning/releases/,planning/deferred.md— decisions, findings sweeps, retrospectives, release notes, and deferred items.
Planning follows the portable two-axis convention — architecture/ (repo root) is the living truth home and promotion target; planning/changes/ holds the per-change bundles. Start at the Quick path in planning/README.md to choose a lane (Full / Lightweight / Tiny), create a bundle, and ship — that file is the authoritative spec. Run just check-planning to validate bundles and just index to print the change listing.
This project uses just (task runner) and uv (package manager). just --list is the source of truth; non-obvious notes:
just lintauto-fixes;just lint-ciis the read-only CI variant (and runs the planning validator).just testruns pytest with coverage and forwards extra args:just test tests/test_client.py -k test_name.- Without
just:uv run ruff format . && uv run ruff check . --fix && uv run ty check && uv run pytest.
Quick orientation. The authoritative, code-current account of each capability lives in
architecture/. When a change alters a capability's behavior, update the matchingarchitecture/<capability>.mdin the same PR — that promotion is what keepsarchitecture/true; code that changes without it silently rots the truth home.
httpware is a thin wrapper over httpx2 (httpx2.Request/httpx2.Response are public surface, not abstracted away) built around three documented protocol seams. Invariants that must not break:
- Three protocol seams, crossed only through their protocols. Seam A:
Client/AsyncClient↔ middleware chain, composed at__init__and frozen for the client's lifetime; the internal terminal callshttpx2.*.send, maps exceptions, and raisesStatusErroron 4xx/5xx. Seam B: clients ↔decoders: Sequence[ResponseDecoder] | None— first decoder whosecan_decodeis True runs;MissingDecoderErrorraises before the HTTP call if none claims the model; decoder failures wrap asDecodeError. Seam C:httpware↔ optional extras — each opt-in dependency imported only inside its dedicated module. - Sync/async parity.
ClientandAsyncClientcarry identical features (typed decoding, middleware, resilience,stream()); a change to one surface must mirror to the other. - House invariants (most review-only, not CI-checked): no
httpx2._private API; nofrom __future__ import annotations(3.11+ floor); noprint()(ruffT201); no global logging config (logging.getLogger("httpware")/ namespaced children only); type suppressions use# ty: ignore[<rule>], never# type: ignore. - Errors. Status-keyed
StatusErrorsubclasses take a single positionalresponseand never override__init__; non-statusClientErrorsubclasses (DecodeError,MissingDecoderError,BulkheadFullError,RetryBudgetExhaustedError,CircuitOpenError,ResponseTooLargeError) do.
| Capability | File |
|---|---|
| What httpware is + architectural invariants + module layout | architecture/overview.md |
Sync Client / async AsyncClient parity, stream() |
architecture/client.md |
Seam A — middleware protocol, Next, chain composition |
architecture/middleware.md |
Seam B — ResponseDecoder protocol, pydantic/msgspec resolution |
architecture/decoders.md |
| Status-keyed exception tree, construction invariant, redaction | architecture/errors.md |
| Resilience suite (retry, budget, bulkhead, circuit breaker, timeout) | architecture/resilience.md |
| Seam C — optional extras isolation | architecture/extras.md |
| House code conventions (naming, imports, docstrings) | architecture/conventions.md |
| Testing conventions | architecture/testing.md |
- Check the relevant
architecture/capability file before adding a new module or extension point. - Surface ambiguity as a documentation gap rather than improvising.
- Issue tracker — Issues live in GitHub Issues (
modern-python/httpware), managed via theghCLI; external PRs are not a triage surface. Seeplanning/agents/issue-tracker.md. - Triage labels — Canonical defaults:
needs-triage,needs-info,ready-for-agent,ready-for-human,wontfix. Seeplanning/agents/triage-labels.md. - Domain docs — Single-context: one
CONTEXT.mdat the repo root + ADRs underplanning/adr/. Seeplanning/agents/domain.md.