- `app/services/http_client.py` : `async with shared_client(timeout=15)
as client:` remplace les 49 créations `async with httpx.AsyncClient(`
de 14 fichiers (gitea ×21, providers oidc/oauth ×11, calendar ×4,
automations ×3…) — le pool de connexions est réutilisé au lieu d'être
recréé à chaque appel. __aexit__ no-op (le client partagé ne se ferme
pas à la sortie).
- Cache par (boucle d'event, kwargs) en WeakKeyDictionary : un
AsyncClient n'est JAMAIS partagé entre deux loops (piège des tests
« Event loop is closed ») — une boucle par test = client propre
collecté avec la boucle. Clé = kwargs triés, repr() pour les valeurs
non hashables (`headers=` dict → TypeError rattrapé par la suite).
- Laissés délibérément : github_adapter (transport MockTransport
injecté), webhook_outbound (client « own_client » fermé par la
fonction).
- Tests : `test_http_client_shared_and_loop_scoped` (réutilisation mêmes
kwargs / cloisonné kwargs / cloisonné loop) ; le stub des webhooks
patche aussi la fabrique `http_client.httpx` + purge du cache (avant :
webhook_outbound.httpx patché mais la fabrique partagée créait un vrai
client → réseau réel dans les tests).
suite **1091/1091** · ruff OK · docs à jour
- A26 — champ `standalone` mort supprimé (0 lecteur) ; `sqlite:////data/…` ne
produit plus de chemin UNC sous Windows ; `.env.example` ne promet plus
PostgreSQL ; raise au boot si `APP_SECRET_KEY` vaut la valeur par défaut
- A33 — rate limit : préfixes manquants (`/scim/v2/`, `/workspace/`, `/db/` +
non-GET sur `/s/` et `/f/`), limite lue dans `settings.rate_limit_requests`
(60 annoncés / 100 codés en dur), clé `X-Forwarded-For` seulement derrière un
proxy local (anti-spoof), `_store` épuré (mémoire bornée) + test dédié
- A34 — `_spawn()` : les 10 schedulers loggent leur exception et redémarrent
après 10 s au lieu de mourir en silence ; 2 `logger.debug` de scheduler → warning
- A35 — OpenAPI régénéré 439 → 511 chemins (`info.version 7.3.9`), README
(était v6.7.0), compteur API_GUIDE, titre dupliqué retiré du ROADMAP ;
le drift Python 3.12/3.13 reste noté (rebuild d'image à valider)
- A36 — 4 dépendances mortes purgées de requirements.txt (aiosqlite, slowapi,
loguru, packaging = 0 import) ; pyproject reste sans [project] : Docker et la
CI installent requirements.txt, dupliquer les 22 deps créerait 2 sources
- A43 — 15 `datetime.utcnow()` → `now(UTC).replace(tzinfo=None)` (format ISO
naïf identique, warnings de dépréciation divisés : 2374 → 926)
suite **1029/1029** · `ruff check app tests` OK · docs/ROADMAP/CHANGELOG/WORKLOAD à jour