ADR-036 : Typage statique du cœur, vérifié en CI¶
Statut¶
Accepté, Forge 1.0.0-beta.17 (ticket ADR-CORE-STATIC-TYPING-001).
Issu d'un retour de tests terrain (ADR-009) : en mode strict, Pylance/Pyright
noie l'utilisateur sous des reportUnknown* sur les symboles du cœur, car
celui-ci est partiellement typé et n'expose pas ses types (pas de py.typed).
Date¶
2026-06-16
Contexte¶
Le cœur de Forge est partiellement typé : ~66 % des fonctions de core/
portent une annotation de retour, mais beaucoup de conteneurs sont nus
(dict au lieu de dict[str, X]), des paramètres ne sont pas annotés, et
aucun vérificateur n'est exécuté. Mesure initiale : pyright core/ en mode
basic relève 41 erreurs (dont 32 reportArgumentType).
Deux conséquences :
- Côté utilisateur. Aucun paquet ne ship le marqueur
py.typed(PEP 561).
Pyright (via Pylance) traite doncforge-mvccomme non typé : même les
annotations existantes sont ignorées dans un projetforge new. D'où le bruit
reportUnknown*en mode strict (mitigé provisoirement par un override dans le
squelette,SKELETON-VSCODE-STRICT-NOISE-001). - Côté cœur. Les 41 erreurs basic révèlent de vraies incohérences de type
non détectées (le contrat public n'est pas vérifié par machine).
Pour un framework, les types font partie du contrat public (principe 10 :
« une API publique est un contrat de complétude »).
Décision¶
Le cœur de Forge est typé et vérifié statiquement en intégration continue.
- Vérificateur : Pyright. C'est le moteur de Pylance : la CI voit exactement
ce que voit l'éditeur de l'utilisateur (cohérence terrain). Configuration dans
pyproject.toml([tool.pyright]). - Marqueur
py.typedajouté à chaque paquet distribué exposant une API
importée par le code utilisateur (core,integrations, et les douze
forge_mvc_*), et inclus dans les wheels (PEP 561). Sans lui, typer le cœur
ne sert pas l'utilisateur. - Stricte par cliquet, pas en big-bang. On adopte d'abord un niveau qui passe
au vert (baselinebasicsurcore/, après correction des 41 erreurs), puis
on bascule module par module en strict (# pyright: stricten tête de
fichier, en commençant par la surface publiquecore/http), avec une règle
de non-régression en CI (aucune nouvelle erreur). - Critère de sortie partiel. Quand la surface publique (
core/http,
BaseController) est stricte etpy.typedpublié, l'override
reportUnknown*du squelette est retiré (la cause étant traitée).
Conséquences¶
Positives :
- Contrat public vérifié par machine ; meilleure DX (autocomplétion, détection
d'erreurs dans le code utilisateur) ; bugs de type du cœur attrapés. - Boucle terrain bouclée : l'override
reportUnknown*deviendra inutile.
Coûts :
- Effort de finition d'annotations + correction des 41 erreurs basic, puis du
delta strict module par module (chantier multi-tickets, étalé sur la b17). - Dépendance de développement supplémentaire (
pyright), exécutée en CI.
Plan (tickets b17)¶
CORE-TYPING-PYRIGHT-BASELINE-001, config[tool.pyright](basic),
py.typed(core + integrations + 12 opt-ins, + package-data), correction des
41 erreurs basic decore/, gate CI (job pyright), garde-fou méta.CORE-TYPING-STRICT-HTTP-001,core/httpen# pyright: strict, conteneurs
paramétrés ; puis retrait de l'overridereportUnknown*du squelette.- Tickets suivants, cliquet strict sur les autres modules de
core/
(mvc,database,sessions,security,forms,auth…).
ADR liés¶
- Retour terrain encadré par l'ADR-009 (consolidation bêta, tests terrain).
- Concrétise le principe 10 (contrat de complétude) appliqué aux types.
- Remplace à terme la mitigation
SKELETON-VSCODE-STRICT-NOISE-001.
Charte appliquée¶
- Principe 1, explicite, testable, durable (types = contrat vérifié).
- Principe 10, une API publique est un contrat de complétude.
- Principe 2, petits tickets : cliquet module par module, pas de big-bang.
- Règle A, retirer la cause (cœur non typé), pas seulement le symptôme
(override de diagnostics).