ADR-018 : Extraction du traitement d'image hors du core : forge-mvc-images¶
Statut¶
Acceptée, Forge 1.0.0-beta.x (ticket IMAGES-EXTRACT-RENAME-001). Extraction livrée : forge-mvc-images est publié.
Décision validée sur le principe (option « B, renommer + rapatrier »).
Périmètre figé ci-dessous ; exécution par étapes.
Date¶
2026-06-03
Contexte¶
Forge distribue un opt-in forge-mvc-media dont la responsabilité réelle, à ce
jour, est la gestion applicative des médias : un repository SQL
(attach_media_to_entity, create_media_record, rôles, positions, alt_text)
et des helpers de galerie/couverture (get_media_gallery, get_cover_media,
media_url). Le traitement d'image proprement dit (génération de variantes
et de miniatures, save_image, vérification de contenu, garde anti-bombe de
décompression, constantes ALLOWED_IMAGE_*/IMAGE_VARIANT_SIZES) vit lui dans
le core, à core/uploads/image.py, et Pillow est une dépendance runtime
du core.
Depuis l'arrivée de forge-mvc-video (qui possède toute la chaîne vidéo :
ingest, stockage, transcodage, lecture), le nom « media » est devenu trompeur, un développeur attend de forge-mvc-media qu'il gère aussi la vidéo, ce qui
n'est pas le cas. Par ailleurs, embarquer Pillow et le traitement d'image dans
le core contredit le principe de noyau minimal (charte principe 8, ADR-004) :
le core devrait fournir l'upload brut sûr, pas le pipeline d'images.
Couplage existant à dénouer¶
core/uploads/manager.py:save_upload(category="images", variants=True)
appelleverify_image_content,generate_image_variants,
image_variant_relative_paths, le chemin d'upload générique du core
porte donc un comportement spécifique aux images.core/uploads/__init__.pyréexporte l'API image.forge_mvc_media/media_gallery.pydépend decore.uploads.image
(ALLOWED_IMAGE_EXTENSIONS,image_variant_relative_paths).make:crudgénèrefrom forge_mvc_media import ...
(cli/entities/crud/controller_builder.py), idemcli/public/public_list.py
et le catalogue opt-incli/optins/catalog.py(entrée « media »).- CI (
.github/workflows/tests.yml), README,docs/reference/api.md,
docs/install/core-dev.md, CONTRIBUTING. - Garde-fous qui imposent Pillow dans le core :
tests/meta/test_packaging.py
(test_pillow_dans_pyproject,test_pillow_dans_requirements_txt),
tests/release/test_release_compat.py,tests/meta/test_app_default_no_mfa_001.py.
Décision¶
Créer un opt-in forge-mvc-images (forge_mvc_images) qui remplace
forge-mvc-media et devient l'unique propriétaire de tout l'image :
- Traitement d'image (déplacé du core) :
save_image, génération de
variantes/miniatures,verify_image_content, garde anti-bombe, constantes
ALLOWED_IMAGE_*/IMAGE_VARIANT_SIZES,MediaRecord. Pillow devient
dépendance deforge-mvc-images, retirée du core. - Couche applicative (déplacée de
forge-mvc-media) : repository SQL,
galerie, couverture,alt_text.
Le core ne garde que l'upload brut générique : save_upload (sans
le cas particulier images), save_bytes, validation générique, storage,
rate-limit, exceptions. save_upload devient purement générique ;
forge-mvc-images fournit le chemin d'upload image-aware (validation de
contenu + écriture + variantes).
Conformément à la convention pré-1.0 (pas d'utilisateurs externes), le
renommage se fait sans alias déprécié ni guide de migration : forge-mvc-media
est supprimé, les réexports image du core sont retirés.
Hors périmètre¶
- La vidéo (
forge-mvc-video) reste inchangée. - Aucune nouvelle fonctionnalité image n'est ajoutée dans les tickets
d'extraction (1 à 7) : ils déplacent le comportement existant tel quel.
L'extension (ci-dessous) fait l'objet de ticketsIMAGES-FEATURE-*
ultérieurs, sur la base propre obtenue après extraction.
Fonctionnalités cibles (ambition « étendre »)¶
Existant déplacé tel quel (figé, non configurable)¶
- Deux variantes en dur :
medium(1280×1280 max),thumbnail(300×300
max), +originalconservé.IMAGE_VARIANT_SIZESnon extensible sans éditer
le code. - Redimensionnement : ratio préservé, réduction seule (jamais d'agrandissement),
rééchantillonnage LANCZOS. - Formats :
jpg, jpeg, png, webp; la variante garde le format de l'original. - Rangement :
<dossier>/medium/…,<dossier>/thumbnail/…. - Sécurité :
verify_image_content, garde anti-bombe de décompression. - Galerie :
url,medium_url,thumbnail_urlpar média.
Cibles d'extension (tickets IMAGES-FEATURE-*)¶
| Capacité | Aujourd'hui | Cible |
|---|---|---|
| Profils de variantes | 2 tailles en dur | profils configurables (noms + dimensions arbitraires) via la config forge_mvc_images |
| Conversion de format | format = celui de l'original | générer une variante dans un format choisi (ex. WebP) tout en conservant l'original |
| Recadrage | réduction « fit » seule | mode crop (ratio fixe : carré avatar, bannière) avec point focal optionnel |
| Qualité / compression | défaut Pillow | qualité réglable par variante (poids vs. netteté) |
| Dimensions sémantiques | medium / thumbnail | profils nommés cover / og-image prêts pour les métadonnées sociales |
| Idempotence / régénération | génération à l'upload | commande/CLI pour régénérer les variantes après changement de profil |
Ces capacités restent opt-in et explicites (pas de magie cachée, charte
principe 3) : un profil non déclaré n'est pas produit ; aucune conversion n'a
lieu sans demande. Chaque capacité = un ticket IMAGES-FEATURE-* séparé
(charte principe 2 : une responsabilité par ticket).
Conséquences¶
Positives¶
- Nom honnête et responsabilité nette :
forge-mvc-images= tout l'image,
forge-mvc-video= toute la vidéo,core/uploads= upload brut sûr. - Core minimal : Pillow quitte le runtime du core (principe 8, ADR-004).
- Une seule façon officielle de traiter une image (principe 11).
Coûts / ruptures¶
- Renommage d'un paquet publié (PyPI
forge-mvc-media1.0.0b13) → nouveau
paquetforge-mvc-images. Acceptable en bêta pré-1.0. - Les garde-fous « Pillow dans le core » s'inversent (Pillow absent du core,
présent dansforge-mvc-images). make:crudgénère désormaisfrom forge_mvc_images import ...; tests de
snapshot de génération à mettre à jour.- Deux fichiers protégés par le hook doivent être modifiés par le mainteneur,
pas par un agent :pyproject.tomlracine (retrait de Pillow) etCLAUDE.md
(§1, §3, §9 décrivantforge-mvc-media).
Plan d'exécution (tickets)¶
IMAGES-PKG-SCAFFOLD-001, squelettepackages/forge-mvc-images
(pyproject.tomlavec Pillow,forge_mvc_images/__init__.py).IMAGES-MOVE-PROCESSING-001, déplacer le traitement image du core vers
forge_mvc_images; rendrecore/uploads/save_uploadgénérique ; exposer un
chemin image-aware dans le module.IMAGES-MOVE-APPLICATIVE-001, déplacer repository + galerie depuis
forge-mvc-mediaversforge_mvc_images.CORE-DROP-PILLOW-001, retrait de Pillow du core (pyproject racine :
mainteneur),requirements.txt, inversion des garde-fous packaging.CLI-CRUD-IMAGES-RENAME-001,controller_builder,public_list,
optins/catalog→forge_mvc_images; mise à jour des tests de génération.CI-DOCS-IMAGES-RENAME-001, matrice CI, README,docs/reference/api.md,
docs/install/core-dev.md, CONTRIBUTING ; CLAUDE.md : mainteneur.REMOVE-MEDIA-PKG-001, suppression depackages/forge-mvc-media; entrée
CHANGELOG.IMAGES-FEATURE-*(ultérieur), extension des fonctionnalités image.
Alternatives écartées¶
- A, renommer seulement :
media → imagessans bouger le traitement. Rejeté :
le nom « images » resterait un abus de langage tant que le traitement vit dans
le core, et le couplagesave_upload/Pillow/core subsisterait. - C, statu quo : garder
forge-mvc-mediacomme couche générique
d'attachement multi-type. Rejeté : ambiguïté persistante vis-à-vis de
forge-mvc-video, et Pillow reste dans le core.
Références¶
- Charte principes 8 (noyau minimal), 11 (une seule façon officielle).
- ADR-004 (périmètre du core minimal strict).
- ADR-005 (packaging hybride monorepo + multi-distributions PyPI).