ADR-031 : Découplage complet du mail hors de core.forge¶
Statut¶
Acceptée (mise en œuvre à suivre, ticket MAIL-DECOUPLE-CORE-001).
Complète ADR-022 (extraction de l'email vers
forge-mvc-mail) et renforce ADR-004 (périmètre du
core minimal) ainsi que le principe 8 (noyau minimal, briques opt-in).
Contexte¶
ADR-022 a extrait core/mail/ vers l'opt-in forge-mvc-mail, mais a laissé un
couplage résiduel que l'ADR notait lui-même : « sa seule dépendance core est
core.forge pour la config ». Concrètement, le core et le squelette nu
continuent de connaître le mail :
core/forge.pyréserve 13 clésmail_*dans son registre de configuration ;core.forge.configure()accepte des kwargsmail_*;core/app/app_factory.pylit un blocMAIL_*optionnel depuisconfig.py
(_optional_mail_kwargs()) ;- le squelette nu pré-câble la config mail sur trois fichiers utilisateur :
env/example(blocMAIL_*),config.py(imports + attributs) etapp.py
(13 argumentsmail_*passés àforge.configure).
forge-mvc-mail, quand il est installé, lit ces valeurs via l'API publique
core.forge.get("mail_*").
Deux constats rendent ce couplage injustifié :
- Aucun code du core ne lit
core.forge.get("mail_*"). Seul
forge-mvc-mailles consomme. Le core auth manipule des adresses et des
jetons de vérification (core/auth/email.py,reset.py) mais
n'envoie aucun email. - Un projet nu (
forge new,requirements.txt = forge-mvcseul) transporte
ainsi une cinquantaine de lignes de plomberie mail pour une brique qui n'est
pas installée. C'est contraire au principe 3 (refuser la magie cachée) et au
principe 8.
Le test de légitimité d'ADR-004 (« si je retire cette brique, une application
basique tourne-t-elle encore ? ») est satisfait : le mail doit relever de la
seule responsabilité de son opt-in, registre de config compris.
Décision¶
Toute connaissance du mail quitte le core et le squelette nu.
forge-mvc-mail possède sa configuration de bout en bout, lue depuis
l'environnement.
| Élément | Avant | Après |
|---|---|---|
Slots mail_* dans core/forge.py |
présents (défauts) | supprimés |
core.forge.configure(mail_*=…) |
accepte les kwargs | n'accepte plus |
core/app/app_factory.py _optional_mail_kwargs() |
présent | supprimé |
Source de config de forge-mvc-mail |
core.forge.get("mail_*") |
os.getenv (config interne au module) |
Squelette env/example / config.py / app.py |
pré-câblent MAIL_* |
aucune trace mail |
Défaut de MAIL_ENABLED quand la variable est absente |
True (côté core) |
False (zéro envoi accidentel) |
L'environnement est déjà chargé par config.py (load_dotenv), donc
os.getenv("MAIL_*") fonctionne dès que l'utilisateur ajoute le bloc MAIL_*
à son env/dev. Le mail ne dépend plus du registre core, et le core ne dépend
plus du mail.
Conséquences¶
Positives :
- Le core respecte strictement ADR-004 pour le mail :
grep -ri mail core/ne
retourne plus queEmailField(validation de formulaire) et le module
emailde la bibliothèque standard. - Un projet nu démarre sans aucune plomberie mail.
forge-mvc-maildevient autonome : il lit et porte sa propre config.- Défaut plus sûr (
MAIL_ENABLEDabsent => désactivé).
À assumer :
- Rupture interne de
core.forge.configure(retrait des kwargsmail_*).
Aucun alias ni guide de migration : phase bêta pré-1.0 (convention CLAUDE.md),
forge-mvc-mailest le seul appelant et il est migré dans le même lot.
À signaler auCHANGELOG.md. - Le provisioning de la config mail (le bloc
MAIL_*à ajouter àenv/dev)
est documenté et fourni par le module mail, jamais écrit silencieusement
dans les fichiers utilisateur (principe 9). Mode « Forge affiche ».
Mise en œuvre (ticket MAIL-DECOUPLE-CORE-001)¶
- Gardes d'abord (rouges au départ) :
mailabsent decore/forge.py;
projet nu sansMAIL_*;forge-mvc-mailne référence pluscore.forge
pour le mail. - Module mail :
MailConfig.from_env()(généralise
cli._configure_forge_from_env) remplacefrom_forge(); bascule
mailer.from_config,transports,clidessus ; défaut
mail_enabled=False. - Core : retirer les 13 clés
mail_*decore/forge.pyet
_optional_mail_kwargs()(+ son appel) decore/app/app_factory.py. - Squelette nu : retirer la plomberie mail de
env/example(hook §12, édition
par script),config.pyetapp.py. - Documentation :
docs/features/mail.mddocumente le bloc env côté module ;
mise à jour du parcours welcome-mail. - Recette : un projet nu démarre sans trace mail ; un projet
pip install forge-mvc-mail+ bloc env vérifiemail:test/mail:doctor
(transportlog).
Validations :
python -m pytest -x -q
python -m compileall -q .
ruff check .
mkdocs build --strict
git diff --check
Alternatives rejetées¶
Conserver les slots mail_* inertes dans core.forge (demi-mesure :
ne déplacer que la plomberie du squelette). Rejetée : laisse de la connaissance
mail dans le core, donc ne satisfait pas le principe 4. Le mail doit être de la
seule responsabilité de l'opt-in, registre compris.
Charte appliquée¶
Principe 3 (refuser la magie cachée), principe 4 / ADR-004 (périmètre du core),
principe 8 (noyau minimal), principe 9 (pas d'écriture invisible dans le code
utilisateur), principe 10 (l'API publique reste un contrat clair).