-
-
Notifications
You must be signed in to change notification settings - Fork 0
Comparing changes
Open a pull request
base repository: modern-python/httpware
base: 0.16.0
head repository: modern-python/httpware
compare: 0.16.1
- 19 commits
- 189 files changed
- 2 contributors
Commits on Jul 19, 2026
-
docs: interactive resilience demo pages (#109)
* docs: add resilience demo engine and circuit-breaker page * docs: make demo breaker OPEN-state results no-ops to match httpware * docs: gate circuit-breaker recovery callout on observed state * docs: show faithful-model disclaimer before interaction * docs: add retry + budget demo page * docs: make demo tour stops scenario-specific * docs: fix demo mount crash and align RetryBudget with budget.py * docs: add bulkhead demo page * docs: hide bulkhead pool stat by default and hold slot across retries * docs: add timeout demo page * docs: count breaker once per retry sequence, skip timed-out Move brk.res() from unconditional-per-landing to the terminal (non-retried) branch only, and skip it when the landing was cut short by AsyncTimeout. Per architecture/resilience.md the breaker sits outside retry and counts one outcome per fully-exhausted retry sequence, not per attempt; a deadline-cancelled operation surfaces as the outer TimeoutError and the inner breaker never sees a counted outcome for it. Only observable once a page combines circuitBreaker with retry/timeout (task 5's full-stack page); verified byte-for-byte no regression on circuit-breaker.md/retry.md/bulkhead.md/timeout.md. * docs: add full-stack compose demo page Composes all four resilience patterns (timeout, circuitBreaker, bulkhead, retry) on one page against a 16s multi-phase incident. Deviates from the drafted scenario in one number: hard-down fault latency 3.0s -> 0.3s. At 3.0s every hard-down attempt already exceeds the 2.0s AsyncTimeout on attempt 0, so (per the composition fix) the breaker never receives a real outcome and never trips -- the page's own third and fourth coach stops become unreachable. 0.3s lets real failures land and trip the breaker within the hard-down window; see .superpowers/sdd/task-5-report.md for the full numeric trace. * docs: add resilience demos landing page and cross-links Flesh out the demos index with links to all five pages and a faithful-model note, cross-link resilience.md to the demos, and correct the circuit-breaker comment describing how the async breaker handles an outer-timeout cancellation (it does see the CancelledError via except BaseException, it just doesn't count it as an outcome). * chore: gitignore .superpowers scratch (plans, ledger, brainstorm) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * docs: make demo latB reflect bounded latency and outage bar cover the spike phase latB was a hardcoded 40ms even on timeout/full-stack pages, contradicting elapsedB as it climbed toward the deadline. Derive it from the same bounded-latency measure as elapsedB when a timeout is configured; leave it untouched (40ms) elsewhere. computeOutageWindow only flagged hard failures, so full-stack's decorative outage bar silently dropped the 2-5s latency-spike phase. Also flag high-latency ok samples (ms >= 1.0, matching updateUI's existing `slow` threshold), falling back to a generic "degraded" label when a window mixes down and slow phases. * docs: distinguish demo dots by shape and de-dup breaker stat write * docs: defer demo mount() to DOMContentLoaded so engine.js loads first * docs: fix demo rendering and first-paint state from browser QA - inherit mkdocs-material font instead of a hardcoded system stack - fill the content column (drop 760px centering that left a stray margin) - keep the timeline outage label on one line (nowrap + vertical center) - plain-client p99 reflects the in-flight latency tail, so it stays high in a brownout instead of flickering to 40ms and contradicting the callout - derive the flow-diagram breaker/pool/elapsed boxes from the scenario chain and auto-select the first scenario, so first paint shows a coherent state Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Configuration menu - View commit details
-
Copy full SHA for 10b9e8c - Browse repository at this point
Copy the full SHA 10b9e8cView commit details -
docs: make retry demo failure count the visible payoff (#110)
The retry page's whole point is that httpware surfaces fewer failures to the caller, but the failed count was rendered in the muted label style, the blip stops spotlighted the in-flight counters (while the copy said 'compare the ✗ counts'), and the 1.2s blip left httpware recovering only ~5 of ~14 — a weak contrast. - ✗ failed is now a prominent stat (bold number, red when >0), wrapped in a spotlightable element, on all pages - retry blip stops spotlight the ✗ counts (plain climbing vs httpware ~0) - tighten the blip to 0.4s so httpware recovers all of it: plain ✗6, httpware ✗0 Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Configuration menu - View commit details
-
Copy full SHA for 2168d98 - Browse repository at this point
Copy the full SHA 2168d98View commit details -
docs: thundering-herd demo view + resilience-demo enrich pass (#111)
* docs(planning): spec resilience demo herd view + enrich pass Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * refactor(demos): extract shared makeTour guided-tour driver Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * feat(demos): add faithful herd simulation and rate-strip renderer Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * feat(demos): add thundering-herd macro view to the retry page One client retrying a blip is invisible; twenty clients retrying a sustained outage is a traffic weapon unless retries are spread out and capped. Adds mountHerd (composing Task 1's tour driver and Task 2's simulateHerd/renderRateStrip) as a stacked "Now 20 clients" section below the existing single-client retry demo, with a mini-tour spotlighting the measured peak-load gap: naive ~70x (unbounded, still growing) vs httpware ~3x (capped by each client's own max_attempts, never a shared budget). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * feat(demos): add countdown ring and jagged failure shape to lane pages CSS-driven conic countdown ring on the timeout and circuit-breaker pages' fixed lane-B elements (elapsedB / brkB), showing the multi-second deadline and reset-timeout waits. Retry and bulkhead get no ring per the fidelity decision (backoff is sub-tick at this demo's time compression). All pages gain a jagged clip-path failure dot so outcomes read without color. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * feat(demos): add full-stack macro strip; finalize change summary Reuse renderRateStrip on the full-stack page: a small backend call-rate strip below the lanes, revealed each tick with the single-client run, labeled with the active incident phase. Gated by config.macroStrip so every other lane page is unaffected. Finalizes the change summary with the realized herd multipliers (naive ~70x vs httpware ~3x). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * docs(planning): reconcile herd change-file body to measured ~70x/~3x The design body still carried the pre-measurement ~10x/~1.2x placeholders and a stale retry-ring mention. Align with the shipped model: httpware is bounded near its max_attempts cap (~3x) at demo scale, naive ~70x; ring is CB+timeout. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(demos): flapping herd outage for faithful spikes-with-gaps The herd's sustained outage produced a smooth rising ramp for independent, un-synchronized naive clients, not the spikes-with-gaps the tour copy claims. Switch the model input to a flapping backend (down/recover x3) so unbounded retries genuinely surge on each dip and clear on recovery -- real spikes-with-gaps, faithfully sourced from backend recovery rather than client synchronization. The existing simulateHerd algorithm already produces this shape once the fault flaps; this is a scenario + rendering + copy change. Add computeOutageBands so the rate strip shades each dip separately, leaving recovery gaps visibly unshaded between them (falls back to the single outage window for callers that don't pass bands, so the full-stack macro strip is unaffected). Re-measure the naive multiplier against the flapping scenario (~18x, down from the sustained model's ~70x since each dip is shorter) and reconcile retry.md's copy and the change-file's motivation/design/payoff language to match. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * docs(demos): drop stale "sustained" wording from herdTemplate default intro The only mountHerd caller now passes an explicit flapping-aware intro; the shared default's "sustained" was dead but a latent trap for future callers. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Configuration menu - View commit details
-
Copy full SHA for 48c0492 - Browse repository at this point
Copy the full SHA 48c0492View commit details -
docs(demos): drop redundant sustained scenario from single-client ret…
…ry demo (#112) Retry can't help the caller on a sustained outage, so its counters read plain ✗82 vs httpware ✗81 — "no difference" at a glance, against the suite's one-number-that-pops design, and its stop overclaimed a "fails fast" that never showed (budget-exhausted requests count as ✗, not fast-fail). Keep only the Brief blip scenario (clear ✗6 vs ✗0 win); the herd view below now owns the sustained/amplification story with numbers that pop (~18x vs ~3x). Preserve the "retry rescues blips, not outages" point as one line of the herd intro. Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Configuration menu - View commit details
-
Copy full SHA for 2c72d63 - Browse repository at this point
Copy the full SHA 2c72d63View commit details
Commits on Jul 27, 2026
-
chore: adopt ruff 0.16.0 (#114)
ruff 0.16.0 stabilized CPY001 (missing-copyright-notice) out of preview, so `select = ["ALL"]` now picks it up. Ignore it, matching modern-di. 0.16.0 also formats Python code blocks inside Markdown; reformat the 51 affected file(s). No .py file changed.
Configuration menu - View commit details
-
Copy full SHA for 4c008ad - Browse repository at this point
Copy the full SHA 4c008adView commit details
Commits on Sep 6, 2026
-
Configuration menu - View commit details
-
Copy full SHA for dd5f185 - Browse repository at this point
Copy the full SHA dd5f185View commit details -
Configuration menu - View commit details
-
Copy full SHA for cad5aaa - Browse repository at this point
Copy the full SHA cad5aaaView commit details -
chore(context7): drop the stale planning/ exclusion (#119)
* chore: migrate off the planning/ convention Replaces planning/ (127 files) and architecture/ (10 pages) with CONTEXT.md, docs/adr/, and two named invariant tests. Nothing was lost: - planning/releases/ (24 files) — the published GitHub Releases are the record. Spot-checked 0.16.0 (byte-identical), 0.13.0 and 0.8.1 (both drifted locally after publication: reformatted code comments, and internal paths rewritten by later directory reorgs). The Release bodies are the authoritative copies. - planning/changes/ (96), audits/ (8), retros/ (3) — git history is the record. - planning/decisions/ was empty; the rejected alternatives were buried in the change files, the retros and the architecture pages, and are rescued below. - planning/agents/ moved to docs/agents/, with domain.md repointed at CONTEXT.md and docs/adr/. Twelve ADRs were already drafted on this branch and are kept as 0001-0012. Five more were extracted from planning/ and architecture/ in this pass: - 0013 httpx2 is the public surface — the 0.2.0 withdrawal of httpware's own Request/Response/Transport/ClientConfig layer (changes/2026-06-03.02, retros/2026-06-04) - 0014 decoders claim broadly, list order resolves ties, can_decode must not raise (retros/2026-06-10, changes/2026-06-10.01) - 0015 the body cap counts decoded bytes; Content-Length rejects but never admits; stream() iteration is uncapped (changes/2026-06-23.03) - 0016 stream() bypasses the middleware chain (changes/2026-06-05.04) - 0017 shared sync/async logic stays a module-local function, not a class and not _internal/ (the 2026-07-13 extraction series) Two candidates were declined because the fact already has a durable home and an ADR would rot beside it: the msgspec type_info/CustomType probe (the rationale is the _probe_can_decode docstring) and MockTransport-over-respx (docs/testing.md "Why not respx?"). The circuit breaker's raw-read `state` property was declined as too narrow. Invariant tests. ADRs 0003, 0009 and 0011 as drafted cited two tests that did not exist — tests/test_client_parity.py and a _KeywordReduceMixin precondition check. Both are now written, so the ADRs cite real enforcement: - tests/test_client_parity.py — public methods, per-method parameters, the constructors, and the resilience suite's sync/async pairing with Timeout as the sole named exception. - tests/test_errors.py — the mixin's __dict__-mirrors-__init__ precondition, and a census so a seventh mixin class cannot be added unchecked. Every assertion was verified by breaking it: an async-only public method, a keyword added to AsyncClient.get alone, a constructor keyword on one world, an async-only resilience middleware, a seventh mixin subclass, and an attribute stored beyond the init keywords each turn the matching test red, and green again on restore. Each was also confirmed not to trip on a benign narrowing — an annotation tightened on one world only, a private helper added to one client, and a field annotation narrowed from float to int all stay green. Only parameter names, order and kind are compared, because the two worlds legitimately annotate middleware and every return type differently. Everything else in architecture/ was mechanism prose with nothing enforceable and is simply deleted; no test was invented to justify keeping the directory. Glossary audit. All four _Avoid_ entries survived — each has a real synonym in use. Two source edits were forced: - docs/errors.md called a third-party decoder an "adapter", the one spelling the Decoder entry rejects. Fixed; user-visible docs prose, no API change. - ResponseTooLargeError.limit is a public field naming the cap, which contradicts the Cap entry's _Avoid_: limit. It cannot be renamed without a breaking change, so CONTEXT.md now records it as the one sanctioned exception rather than asserting a rule the public API violates. Stale cross-references into the deleted directories were removed from seven module docstrings, three test docstrings, and seven docs pages. The docs ones were absolute github.com URLs, which lychee --offline excludes — they would have rotted silently past the new gate. release.yml. Copied from modern-di verbatim (only the PyPI project name differs). This retires the mandatory-curated-release-notes policy: the "Require curated release notes" gate is gone and the body now comes from generate_release_notes. Without this, deleting planning/releases/ would have broken every future stable release, since the gate read planning/releases/${GITHUB_REF_NAME}.md. justfile loses the index and check-planning recipes and the planning/index.py line from lint-ci. _checks.yml gains the offline lychee links job. mkdocs.yml excludes docs/adr/ and docs/agents/ from the site build, as modern-di does — without it, --strict fails on the new unnavigated pages. Verification: just lint-ci clean; just test 811 passed, 100% coverage; just docs-build clean under --strict; lychee --offline 56 errors before, 0 after (all 56 were inside planning/). Deferred items with no ADR home — 3.13t free-threaded CI, blocked on a msgspec cp313t wheel, and the circuit breaker's force_open/force_closed — are drafted to DRAFT-ISSUES-httpware.md, not opened. * chore(context7): drop the stale planning/ exclusion The sibling commit on this branch deletes planning/, so the exclusion now names a directory that does not exist. Dropping the key rather than leaving an empty list: Context7's schema (https://context7.com/schema/context7.json) declares no required properties and gives excludeFolders a default of [], so an absent key and an empty list are equivalent. Absent matches the repos that never carried the line.Configuration menu - View commit details
-
Copy full SHA for 51474c7 - Browse repository at this point
Copy the full SHA 51474c7View commit details -
chore(ci): check absolute self-links in the offline link gate (#120)
--offline excludes every external URL, and an absolute https://github.com/modern-python/<this repo>/blob/main/<path> link back into this repo is external by that rule. So a relative link to a deleted file failed the gate while the same link written absolutely passed forever. The modern-python/.github#67 migration produced two live instances; both were caught by a human reading the diff, neither by CI. --remap resolves those URLs against the checkout instead. The target is in the repo, so it needs no network: the gate stays fully offline and deterministic, and modern-python/.github#66's decision to keep the external check weekly and .github-only is untouched. The pattern derives the repo from github.repository and the checkout root from github.workspace, so the block is byte-identical in all 26 repos and carries no repo-specific parameter. That closes the question modern-python/.github#82 left open: neither a reusable workflow nor a guard test is needed, because the copy-paste error they were meant to guard against cannot occur. The cost is that the interpolated repo name is not regex-escaped, so in .github the dot is a wildcard -- over-broad, never under-broad, and inert. The issue proposed file://$PWD/$1, which does not work. lychee-action runs `eval lychee $ARGS`, so $PWD stays literal inside the single quotes the remap pair needs, and double quotes make $1 an unbound positional under the entrypoint's `set -u`. github.workspace is substituted by Actions before the shell sees it. Verified against lychee 0.24.2, the version lychee-action@v2 pins, over a clean `git archive HEAD` export of each repo: every tree is green, an injected broken absolute self-link is caught, and tag-pinned and cross-repo URLs stay excluded. Swept all 26 repos for pre-existing absolute self-links first -- 31 of them, all resolving -- so no gate goes red on adoption. Known gap: the glob is still '**/*.md', so the docstring URL in faststream-concurrent-aiokafka that the issue cites as user-visible on PyPI stays unchecked. Widening the glob would not reach it either, because that URL is split across two adjacent Python string literals. Refs modern-python/.github#82
Configuration menu - View commit details
-
Copy full SHA for b928dc9 - Browse repository at this point
Copy the full SHA b928dc9View commit details
Commits on Sep 7, 2026
-
docs(agents): drop the fact-placement convention from AGENTS.md (#121)
* docs(agents): drop the fact-placement convention from AGENTS.md Where a fact goes, its admission check and the ADR convention all go; doc placement is a domain-modeling convention. The INVARIANT: docstring shape stays. * docs(agents): drop inline ADR citations from AGENTS.md The remaining docs/adr/ references were citations inside architecture prose, or an index of them. The prose stays; the pointers go.
Configuration menu - View commit details
-
Copy full SHA for d20032d - Browse repository at this point
Copy the full SHA d20032dView commit details -
Configuration menu - View commit details
-
Copy full SHA for 681a420 - Browse repository at this point
Copy the full SHA 681a420View commit details
Commits on Sep 12, 2026
-
Configuration menu - View commit details
-
Copy full SHA for 10775df - Browse repository at this point
Copy the full SHA 10775dfView commit details -
chore(coverage): declare the gate in [tool.coverage.report] and measu…
…re only in test-ci (#124)
Configuration menu - View commit details
-
Copy full SHA for 8831c1d - Browse repository at this point
Copy the full SHA 8831c1dView commit details
Commits on Sep 13, 2026
-
docs(agents): drop the retired paragraphs and keep the canonical ones…
… verbatim (#125) modern-python/.github#93: the three retired conventions (real work not scheduled, the INVARIANT: docstring shape, the # ty: ignore note) are process notes the standard no longer carries. modern-python/.github#92: section 9 of the standard fixes the justfile and README-links paragraphs word for word.
Configuration menu - View commit details
-
Copy full SHA for 16ee837 - Browse repository at this point
Copy the full SHA 16ee837View commit details
Commits on Sep 15, 2026
-
Configuration menu - View commit details
-
Copy full SHA for 293467e - Browse repository at this point
Copy the full SHA 293467eView commit details -
Configuration menu - View commit details
-
Copy full SHA for e4ed89b - Browse repository at this point
Copy the full SHA e4ed89bView commit details
Commits on Sep 19, 2026
-
docs(adr): compress to 10 records in the domain-modeling format (#128)
* docs(adr): compress to 10 records in the domain-modeling format Drop 0001, 0010, 0011, 0017; merge 0009 into 0003, 0014 into 0006, 0008 into 0007; record the #118 manual-control decline in 0005. Each record is now title plus one paragraph, without the Decision / rationale / Revisit-trigger sections. Numbers stay stable, with gaps. * docs(adr): renumber the surviving records 0001-0010
Configuration menu - View commit details
-
Copy full SHA for f96e832 - Browse repository at this point
Copy the full SHA f96e832View commit details -
Configuration menu - View commit details
-
Copy full SHA for e164843 - Browse repository at this point
Copy the full SHA e164843View commit details -
docs: restore the agent artifacts to the setup skill's templates (#130)
Brings this repo's agent artifacts back to the setup skill's seed templates. They had drifted — sections renamed and reordered, wording rephrased, and in places an older copy of the template than the skill now ships (for example `issue-tracker.md` predates the wayfinding section). Template-owned wording is restored verbatim. Anything the template has no slot for is kept: sections this repo added are carried over unchanged, and repo-specific facts inside template sections are preserved rather than reset away. `AGENTS.md` moves from a bulleted variant to the block the skill writes: one `###` sub-block per artifact, each a one-line summary and a pointer.
Configuration menu - View commit details
-
Copy full SHA for 72fe4ba - Browse repository at this point
Copy the full SHA 72fe4baView commit details
This comparison is taking too long to generate.
Unfortunately it looks like we can’t render this comparison for you right now. It might be too big, or there might be something weird with your repository.
You can try running this command locally to see the comparison on your machine:
git diff 0.16.0...0.16.1