Plan for working through the 13 GitHub issues from AUDIT.md. Issues are grouped into waves; each wave is a set that can be worked in parallel without conflict, and waves are sequential.
Mapping to filed issues: https://github.com/recost-dev/middleware-python/issues
| Wave | Issues | Parallel? | Reason for order |
|---|---|---|---|
| 1 — Foundation | #1, #5 | Yes (different files) | Both zero-risk for blocking other work. |
| 2 — Threading primitive | #4 | — | Adds module-level RLock that Wave 3 / 4 depend on. |
| 3 — Lifecycle | #6 then #7 | Sequential (both touch _transport.py) |
#6 fixes dispose semantics first; #7 builds on clean dispose. |
| 4 — Fork-safety + body sizing | #3, #8 | Yes (different files) | #3 builds on Wave 3's clean dispose; #8 is _interceptor.py-only. |
| 5 — P2 cleanup + types + tests | #10, #11, #12, #13, #2, #9 | Mostly yes | Small surface; fold tests into each fix; mypy sweep cleans up residual. |
Two independent tracks, fully parallel.
-
#1 — Aggregator thread-safety (P0) Files:
recost/_aggregator.py,tests/test_aggregator.pyWrapingest/flush/would_overflowinthreading.RLock. Add a regression test that races N threads ingesting against a flush. No dependencies on other issues. -
#5 — Naming + docs reconciliation (P1) Files:
recost/frameworks/flask.py,recost/__init__.py,README.md,CLAUDE.md,tests/test_provider_registry.pyFlask class rename (ReCost→RecostExtension, alias for one release), stripEcoAPI*from CLAUDE.md, fix the provider-count claims (21 → 34), documentflush_interval_ms/max_buckets/shutdown_flush_timeout_ms, markflush_intervaldeprecated. No business-logic risk.
Why this order: both are zero-risk for blocking other work. Naming should land before any test or doc changes elsewhere so subsequent commits don't reference the old names.
- #4 — Module-level state races (P1)
Files:
recost/_init.py,recost/_interceptor.py,tests/test_init.pyIntroduces a module-levelthreading.RLockaroundinit()/dispose()/install()/uninstall(). Must land before Wave 3 because #6 and #3 will both rely on the same lock for their atomicity guarantees.
Both edits live in _transport.py, so do them sequentially to avoid merge conflicts:
-
#6 — Process lifecycle: atexit + dispose-during-connect FD leak (P1) Files:
recost/_init.py,recost/_transport.py,tests/test_init.pyFixes the dispose path so the loop properly stops and the thread joins. Adds anatexithandler that runs a final flush. Provides the clean-dispose mechanic that #3 reuses. -
#7 — Local-mode WebSocket hardening (P1) Files:
recost/_transport.py,tests/test_transport.py, plus a coordinated change in the extension repo Cap the queue (drop-oldest), give up after N failed reconnects (with a one-shoton_error), add lightweighthello/ackhandshake. Doing this second means rebasing on top of cleaner dispose semantics.
Parallel — different files:
-
#3 — No fork-safety (P0) Files:
recost/_init.py,recost/_transport.py,README.md,tests/test_init.pyRegisteros.register_at_fork(after_in_child=_reinit_after_fork)that re-creates the timer thread and transport thread in the child. Builds on Wave 3's clean dispose because the child needs to fully discard the parent's transport state. -
#8 — Body-size measurement (P1) Files:
recost/_interceptor.py,README.md,tests/test_interceptor.pyaiohttpjson=/FormData, httpx streaming-body materialization, response Content-Length-only caveat. Independent of all lifecycle work.
Most can be done in parallel; finish in any order.
- #10 — Flush loop hygiene (P2) —
recost/_init.py - #11 — urllib3 wrapper maintenance (P2) —
recost/_interceptor.py - #12 — exclude_patterns (P2) —
recost/_init.py,README.md - #13 — Code hygiene (P2) —
recost/_transport.py,recost/_interceptor.py - #2 — 35 mypy strict errors (P0) — touched files were largely fixed during earlier waves; this is the residual sweep. Add
mypy --strictto CI as part of this PR so the strict-clean claim becomes verifiable. - #9 — Test gaps (P1) — fold tests into each PR above. Outstanding items by end of Wave 4: aiohttp interceptor branch coverage (Wave 4 dependency), privacy test, self-instrumentation test, 5xx retry test.
- One issue per PR. Each PR closes one numbered issue. Avoid bundling unrelated fixes.
- Tests with every fix. Don't merge a bugfix without a test that fails before the fix and passes after.
- mypy
--strictmust pass on touched files before merge. This is how #2 closes — incrementally per file. By Wave 5 only residual errors should remain. - Update
CHANGELOG.mdwith each PR (or create one if absent).
Pick one of:
- #5 — low-risk warm-up that unblocks naming.
- #1 — go straight to a P0 with a focused fix.
- #4 — set up the threading primitive that 3 / 6 / 7 depend on (longer chain).
Recommendation: open two branches and do #5 + #1 in parallel as Wave 1, then start #4.