Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,14 @@ Guidance for AI agents (Claude Code, etc.) working in this repository.

- [`architecture/`](architecture/) (repo root) — the per-capability living truth (overview, client, middleware, decoders, errors, resilience, optional extras, testing); the promotion target on every ship. **Read the relevant file before changing that capability.**
- [`planning/README.md`](planning/README.md) — the planning conventions (two axes, change bundles, three lanes, frontmatter) + the change Index.
- [`planning/changes/{active,archive}/<YYYY-MM-DD.NN-slug>/`](planning/changes/) — per-change bundles (`design.md` + `plan.md`, or `change.md` for the lightweight lane).
- [`planning/changes/<YYYY-MM-DD.NN-slug>/`](planning/changes/) — per-change bundles (`design.md` + `plan.md`, or `change.md` for the lightweight lane).
- [`planning/audits/`](planning/audits/) — findings reports + `scripts/` tooling.
- [`planning/retros/`](planning/retros/) — retrospectives.
- [`planning/releases/`](planning/releases/) — per-version release notes (also published on GitHub Releases).
- [`planning/deferred.md`](planning/deferred.md) — review-surfaced, not-yet-actionable items.
- [`planning/_templates/`](planning/_templates/) — design/plan/change templates.

**Per-feature workflow:** brainstorming → `design.md` in `planning/changes/active/<id>/` → writing-plans → `plan.md` in the same bundle → executing-plans (or subagent-driven-development) → requesting-code-review → finishing-a-development-branch. On ship, promote the conclusions into the affected `architecture/<capability>.md` by hand and move the bundle to `planning/changes/archive/`. Topic slugs are kebab-case descriptions (`msgspec-decoder-adapter`), not story IDs.
**Per-feature workflow:** brainstorming → `design.md` in `planning/changes/<id>/` → writing-plans → `plan.md` in the same bundle → executing-plans (or subagent-driven-development) → requesting-code-review → finishing-a-development-branch. On ship, promote the conclusions into the affected `architecture/<capability>.md` by hand and set `status: shipped` + `pr` + `outcome` in the implementing PR — there is no folder move. The change listing is generated: run `just index`. Topic slugs are kebab-case descriptions (`msgspec-decoder-adapter`), not story IDs.

## Commands

Expand Down
4 changes: 4 additions & 0 deletions Justfile
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,10 @@ lint-ci:
uv run ruff check --no-fix
uv run ty check

# Print the planning change index (grouped by status) to stdout.
index:
uv run python planning/index.py

test *args:
uv run --no-sync pytest {{ args }}

Expand Down
97 changes: 20 additions & 77 deletions planning/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,33 +7,35 @@ the repo root; this directory records *how it got there*.
## Conventions

> This section is the portable convention — identical across the
> modern-python repos. The Index below is repo-specific. To adopt elsewhere,
> modern-python repos. The generated change listing (`just index`) and the `## Other` pointers below are repo-local. To adopt elsewhere,
> copy this section plus [`_templates/`](_templates/) and point that repo's
> `CLAUDE.md` Workflow + truth home at it.

### Two axes, never mixed

- **`architecture/` (repo root) — the present.** One file per capability,
living prose, updated whenever a change ships. The truth home.
living prose, updated in the same PR that ships the change. The truth home.
- **`planning/changes/` — the past-and-pending.** One folder per change,
frozen once shipped.
kept in place after ship.

Shipping a change **promotes** its conclusions into the affected
`architecture/<capability>.md` by hand, then archives the bundle. That
hand-edit is what keeps `architecture/` true; the archived bundle carries the
*why*.
A change **promotes** its conclusions into the affected
`architecture/<capability>.md` by hand **in the implementing PR, alongside the
code** — the edit rides in the same diff and is reviewed with it, never applied
as a separate post-merge step. That hand-edit is what keeps `architecture/`
true; the bundle stays in `changes/` as the *why*.

### Change bundles

A change is a folder `changes/active/YYYY-MM-DD.NN-<slug>/`:
A change is a folder `changes/YYYY-MM-DD.NN-<slug>/`:

- `YYYY-MM-DD` — proposal date; `.NN` — zero-padded intra-day counter
(`.01`, `.02`, …) that breaks same-date ties so the timeline sorts stably.
- `<slug>` — kebab-case description, not a story ID.

On merge the folder moves to `changes/archive/` with `status: shipped`, `pr:`,
and `outcome:` filled, and its line moves from **Active** to **Archived** in
the Index below.
`summary` is written when the change is created (it is the change's
one-liner). The implementing PR then sets `status: shipped` and fills `pr`
and `outcome` **in the branch**, alongside the code and the `architecture/`
promotion — no post-merge bookkeeping, no folder move.

### Three lanes

Expand Down Expand Up @@ -62,75 +64,16 @@ Templates live in [`_templates/`](_templates/).
### Frontmatter

`design.md` / `change.md`: `status` (draft|approved|shipped|superseded),
`date`, `slug`, `supersedes`, `superseded_by`, `pr`, `outcome`.
`plan.md`: `status`, `date`, `slug`, `spec`, `pr`. Files in `architecture/`
carry **no** frontmatter — living prose, dated by git.
`date`, `slug`, `summary` (single line), `supersedes`, `superseded_by`, `pr`,
`outcome`. `plan.md`: `status`, `date`, `slug`, `spec`, `pr`. Files in
`architecture/` carry **no** frontmatter — living prose, dated by git.

## Index

### Active

_None._

### Archived (shipped)

- **[delta-audit-followups](changes/archive/2026-06-16.04-delta-audit-followups/change.md)** (#71, 2026-06-16) — Closed the [2026-06-16 delta audit](audits/2026-06-16-delta-audit.md) Low findings: rate-mode HALF_OPEN probe-failure re-open test + document-as-intended note. Tests + doc only, no release.

- **[circuit-breaker-state](changes/archive/2026-06-16.03-circuit-breaker-state/design.md)** (#70, 2026-06-16) — Read-only `state` property + public `CircuitState` enum on the circuit breaker. Shipped 0.14.0; closed the read-only-state half of the deferred CircuitBreaker introspection item.

- **[circuit-breaker-rate-mode](changes/archive/2026-06-16.02-circuit-breaker-rate-mode/design.md)** (#69, 2026-06-16) — Added an opt-in time-based failure-rate trip mode to the circuit breaker (classic stays default). Shipped 0.13.0; closed deferred item "CircuitBreaker v2 (a)".
- **[per-verb-with-response](changes/archive/2026-06-16.01-per-verb-with-response/design.md)** (#68, 2026-06-16) — Added `get_with_response` … `request_with_response` siblings (required `response_model`, returns `(Response, T)`) to both clients. Shipped 0.12.0; closed the deferred "Per-verb-with-response siblings" item.
- **[custom-decoder-guide](changes/archive/2026-06-15.01-custom-decoder-guide/change.md)** (#67, 2026-06-15) — Docs: a "write your own `ResponseDecoder`" guide for Seam B, mirroring `docs/middleware.md`. Closed deferred item G6.
- **[audit-doc-fixes](changes/archive/2026-06-14.06-audit-doc-fixes/change.md)** (#66, 2026-06-14) — Closed the [deep-audit](audits/2026-06-14-deep-audit.md) doc-accuracy findings: `Client.stream()` docs, terminal-call attribution, the four auto-raise sites, the pydantic upper bound, and root import paths.
- **[audit-test-quality](changes/archive/2026-06-14.05-audit-test-quality/change.md)** (#65, 2026-06-14) — Closed 11 [deep-audit](audits/2026-06-14-deep-audit.md) test-quality findings: sync-terminal + CookieConflict coverage, the `StatusError.__init__` invariant, missing status constructions, sync mirrors, typing overloads, a deterministic bulkhead barrier, a pinned budget clock, an observability assertion, and the `TimeoutError` circuit trigger.
- **[audit-correctness](changes/archive/2026-06-14.04-audit-correctness/change.md)** (#64, 2026-06-14) — Closed 8 [deep-audit](audits/2026-06-14-deep-audit.md) correctness + public-API findings: RetryBudget token ordering, two `OverflowError` crashes, the redaction triple-slash, the msgspec guard, streaming-body symmetry, the RetryBudget docstring caveat, and `middleware/__all__`.
- **[security-hardening](changes/archive/2026-06-14.03-security-hardening/design.md)** (#63, 2026-06-14) — Closed the [deep-audit](audits/2026-06-14-deep-audit.md) security cluster: URL secret redaction centralized at the `_emit_event` chokepoint (userinfo + sensitive query/fragment keys), opt-in `max_error_body_bytes` + `ResponseTooLargeError` bounding the `stream()` error pre-read, and `trust_env` + header-reachability docs. Non-streaming hard body cap deferred.
- **[pydantic-import-isolation](changes/archive/2026-06-14.02-pydantic-import-isolation/change.md)** (#62, 2026-06-14) — Guarded the pydantic import so the decoder module loads without the extra and the `architecture/extras.md` Seam-C invariant holds for pydantic. Closed the [deep-audit](audits/2026-06-14-deep-audit.md) High + 2 folded Mediums.
- **[deep-audit](changes/archive/2026-06-14.01-deep-audit/design.md)** (2026-06-14) — Full-codebase deep audit (perf/security/supply-chain gaps + correctness/concurrency/refactoring/test quality). [Report](audits/2026-06-14-deep-audit.md): 35 confirmed; all remediated across #62–#66 (non-streaming hard body cap deferred).
- **[docs-ux-restructure](changes/archive/2026-06-14.01-docs-ux-restructure/design.md)** (#60, 2026-06-14) — Thin README front-door + canonical `docs/index.md` (G3), why-httpware hook (G1), runnable jsonplaceholder example (G4), nav reorder + architecture links, base-client scrub. G2 dropped; G6 (decoder guide) still open.
- **[docs-audit-followups](changes/archive/2026-06-13.05-docs-audit-followups/change.md)** (#58, 2026-06-13) — Second docs-audit batch: corrected the overstated invariant-enforcement claims in `CLAUDE.md` + `architecture/overview.md` (only `print()`/blanket-`type: ignore` are machine-checked), readability findings R1–R3, and documented the public `STATUS_TO_EXCEPTION` (G5).
- **[docs-accuracy-fixes](changes/archive/2026-06-13.04-docs-accuracy-fixes/change.md)** (f203821, 2026-06-13) — Fixed 5 verified factual errors from the [docs audit](audits/2026-06-13-docs-audit.md): RetryBudget formula, modern-di 2.x recipe, contributing-doc CI/grep claim, `just lint` comment, middleware stable-contracts list (+ AsyncTimeout non-finite wording).
- **[portable-planning-convention](changes/archive/2026-06-13.03-portable-planning-convention/design.md)** (#55, 2026-06-13) — Adopt the portable two-axis convention: per-capability `architecture/` truth files + `changes/` bundles, full history backfill, byte-identical Conventions.
- **[circuit-breaker-and-timeout](changes/archive/2026-06-13.02-circuit-breaker-and-timeout/design.md)** (#51, 2026-06-13) — Shipped 0.10.0 — CircuitBreaker + AsyncTimeout
- **[msgspec-nested-customtype-fix](changes/archive/2026-06-13.01-msgspec-nested-customtype-fix/design.md)** (#43, 2026-06-13) — Shipped 0.9.1 — nested-CustomType guard
- **[delta-audit](changes/archive/2026-06-12.01-delta-audit/design.md)** (#43, 2026-06-12) — 0.9.0 delta audit; closed via 0.9.1
- **[decoder-instance-cache](changes/archive/2026-06-10.02-decoder-instance-cache/design.md)** (#42, 2026-06-10) — Shipped 0.9.0 — per-instance decoder cache
- **[multi-decoder](changes/archive/2026-06-10.01-multi-decoder/design.md)** (#41, 2026-06-10) — Shipped 0.9.0 — multi-decoder routing
- **[readme-link-cleanup](changes/archive/2026-06-08.08-readme-link-cleanup/design.md)** (#39, 2026-06-08) — README link cleanup
- **[mkdocs-gh-pages-migration](changes/archive/2026-06-08.07-mkdocs-gh-pages-migration/design.md)** (#38, 2026-06-08) — Docs host -> GitHub Pages
- **[test-mop-up](changes/archive/2026-06-08.06-test-mop-up/design.md)** (#37, 2026-06-08) — Shipped 0.8.6 — test-only audit findings
- **[small-fixes-mop-up](changes/archive/2026-06-08.05-small-fixes-mop-up/design.md)** (#36, 2026-06-08) — Shipped 0.8.5 — 4 small audit findings
- **[otel-partial-install](changes/archive/2026-06-08.04-otel-partial-install/design.md)** (#35, 2026-06-08) — Shipped 0.8.4 — OTel partial-install guards
- **[post-080-doc-sweep](changes/archive/2026-06-08.03-post-080-doc-sweep/design.md)** (#34, 2026-06-08) — Post-0.8.0 doc sweep
- **[retry-budget-cluster](changes/archive/2026-06-08.02-retry-budget-cluster/design.md)** (#34, 2026-06-08) — Shipped 0.8.3 — 7 RetryBudget findings
- **[send-with-response](changes/archive/2026-06-08.01-send-with-response/design.md)** (#33, 2026-06-08) — Shipped 0.8.2 — send_with_response
- **[deep-audit](changes/archive/2026-06-07.03-deep-audit/design.md)** (#32, 2026-06-07) — Deep audit; findings closed across 0.8.1-0.8.6
- **[decoder-error](changes/archive/2026-06-07.02-decoder-error/design.md)** (#32, 2026-06-07) — Shipped 0.8.1 — DecodeError at seam B
- **[sync-client](changes/archive/2026-06-07.01-sync-client/design.md)** (#31, 2026-06-07) — Shipped 0.8.0 — sync Client + Async* rename
- **[modern-di-recipe](changes/archive/2026-06-06.01-modern-di-recipe/design.md)** (#29, 2026-06-06) — modern-di DI recipe doc
- **[v0.7-docs-expansion](changes/archive/2026-06-05.07-v0.7-docs-expansion/design.md)** (#28, 2026-06-05) — Shipped 0.7.0 — first-cut user docs
- **[extension-slot-docs](changes/archive/2026-06-05.06-extension-slot-docs/design.md)** (#28, 2026-06-05) — Shipped 0.7.0 — middleware docs
- **[observability](changes/archive/2026-06-05.05-observability/design.md)** (#27, 2026-06-05) — Shipped 0.6.0 — logging + OTel events
- **[streaming](changes/archive/2026-06-05.04-streaming/design.md)** (#26, 2026-06-05) — Shipped 0.5.0 — stream()
- **[docs-sync-0.4](changes/archive/2026-06-05.03-docs-sync-0.4/design.md)** (#25, 2026-06-05) — 0.4 docs sync
- **[bulkhead](changes/archive/2026-06-05.02-bulkhead/design.md)** (#23, 2026-06-05) — Shipped 0.4.0 — Bulkhead
- **[retry-and-retry-budget](changes/archive/2026-06-05.01-retry-and-retry-budget/design.md)** (#22, 2026-06-05) — Shipped 0.4.0 — Retry + RetryBudget
- **[v0.2-retro-and-housekeeping](changes/archive/2026-06-04.02-v0.2-retro-and-housekeeping/design.md)** (#21, 2026-06-04) — Post-0.2 retro + housekeeping
- **[pydantic-optional-extra](changes/archive/2026-06-04.01-pydantic-optional-extra/design.md)** (#21, 2026-06-04) — Shipped 0.3.0 — pydantic moves to an extra
- **[thin-httpx2-wrapper](changes/archive/2026-06-03.02-thin-httpx2-wrapper/design.md)** (#20, 2026-06-03) — Shipped 0.2.0 — the thin-wrapper pivot
- **[input-validation-pass](changes/archive/2026-06-03.01-input-validation-pass/design.md)** (#19, 2026-06-03) — Input-validation hardening
- **[project-hygiene-tidy](changes/archive/2026-06-02.02-project-hygiene-tidy/design.md)** (#18, 2026-06-02) — Repo hygiene pass
- **[docs-reorg-and-mkdocs](changes/archive/2026-06-02.01-docs-reorg-and-mkdocs/design.md)** (#17, 2026-06-02) — Docs reorg + mkdocs scaffolding
- **[auth-coercion](changes/archive/2026-06-01.01-auth-coercion/design.md)** (#16, 2026-06-01) — Shipped (Epic 2); removed by the v0.2 pivot *(superseded by thin-httpx2-wrapper)*
- **[release-0.1.0-prep](changes/archive/2026-05-31.09-release-0.1.0-prep/design.md)** (#14, 2026-05-31) — 0.1.0 released
- **[recordedtransport](changes/archive/2026-05-31.08-recordedtransport/design.md)** (#13, 2026-05-31) — Shipped in 0.1.0; removed by the v0.2 pivot *(superseded by thin-httpx2-wrapper)*
- **[asyncclient](changes/archive/2026-05-31.07-asyncclient/design.md)** (#12, 2026-05-31) — Shipped in 0.1.0; rewritten by the v0.2 pivot *(superseded by thin-httpx2-wrapper)*
- **[msgspec-decoder-via-extras](changes/archive/2026-05-31.06-msgspec-decoder-via-extras/design.md)** (#11, 2026-05-31) — Shipped in 0.1.0; carry-forward decoder
- **[request-immutability-helpers](changes/archive/2026-05-31.05-request-immutability-helpers/design.md)** (#10, 2026-05-31) — Shipped in 0.1.0; removed by the v0.2 pivot *(superseded by thin-httpx2-wrapper)*
- **[phase-shortcut-decorators](changes/archive/2026-05-31.04-phase-shortcut-decorators/design.md)** (#9, 2026-05-31) — Shipped in 0.1.0; survived the v0.2 pivot
- **[middleware-protocol-and-chain](changes/archive/2026-05-31.03-middleware-protocol-and-chain/design.md)** (#8, 2026-05-31) — Shipped in 0.1.0; survived the v0.2 pivot
- **[shipped-work-review](changes/archive/2026-05-31.02-shipped-work-review/design.md)** (#7, 2026-05-31) — 0.1.0-era review of shipped stories
- **[bmad-to-superpowers-transition](changes/archive/2026-05-31.01-bmad-to-superpowers-transition/design.md)** (#6, 2026-05-31) — Bootstrapped the planning workflow
The change listing is **generated**, not maintained — run `just index` to
print it (grouped by `status`: In progress / Shipped / Superseded). The
frontmatter in each bundle is the single source of truth; there is no
committed copy to drift.

## Other

Expand Down
1 change: 1 addition & 0 deletions planning/_templates/change.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
status: draft
date: YYYY-MM-DD
slug: my-change
summary: One line — shown in the generated index. Fill at ship time.
supersedes: null
superseded_by: null
pr: null
Expand Down
1 change: 1 addition & 0 deletions planning/_templates/design.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
status: draft
date: YYYY-MM-DD
slug: my-change
summary: One line — shown in the generated index. Fill at ship time.
supersedes: null
superseded_by: null
pr: null
Expand Down
Loading