ADR-024 : Bootstrap par squelette dédié et dépendance core via pip¶
Statut¶
Accepté, Forge 1.0.0-beta.13 (ticket BOOTSTRAP-SKELETON-ADR-024).
Date¶
2026-06-07
Contexte¶
forge new <NomProjet> doit produire un projet Forge nu (ADR-023 :
forge new ne construit aucun starter). Or l'implémentation actuelle ne
produit pas un projet nu.
État avant la décision¶
-
Le squelette est le dépôt entier.
_clone_skeletonexécute
git clone --branch v1.0.0-beta.13 --depth=1 https://github.com/caucrogeGit/Forge.git.
Le projet généré hérite donc de tout le monorepo :core/,
cli/,integrations/,packages/,tests/,docs/,
mkdocs.yml,pyproject.toml, et lemvc/de dogfooding (l'application
de démonstration qui sert à développer Forge). -
Le
mvc/livré est l'app de démo. Même simvc/routes.pyest neutre
(une seule route/, garde-foutests/test_skeleton_routes_neutral_001.py),
les fichiers de démonstration arrivent sur le disque sans être routés :
auth_controller.py,mfa_challenge_controller.py,welcome_controller.py,
entities/media/,mail/templates/,models/auth_model.py, et surtout
views/landing/index.html(la landing marketing de Forge, source de
forge sync:landing). -
Le projet importe le
core/cloné, pas le paquet. Lerequirements.txt
du squelette ne dépend pas deforge-mvc: il ne liste que les libs
runtime (mariadb,python-dotenv,jinja2,argon2-cffi,
jsonschema). Le projet généré importe donc lecore/présent localement
grâce au clone, ce qui justifie a posteriori le clone du dépôt entier.
Ce modèle contredit les principes 1 (séparer framework et application
métier) et 8 (noyau minimal, briques opt-in) de la charte : un projet nu ne
devrait contenir ni le framework, ni sa suite de tests, ni sa documentation,
ni son application de démonstration.
Faits techniques établis (audit)¶
- Le paquet
forge-mvcembarque déjàcore,cliet
integrations(pyproject.toml:include = ["core*", "cli*", "integrations*"]).
Un projet sanscore/local fonctionne donc en important le paquet installé. app.pysurchargeviews_dirviaforge.configure(views_dir="mvc/views")
(chemin relatif au répertoire de travail) : aucun couplage au chemin du
dépôt.- Le superviseur d'autoreload ignore proprement un dossier
core/absent
(if not base.is_dir(): continue). - Les layouts (
layouts/base.html, etc.) n'utilisent ni{% extends %}ni
{% include %}: une page d'accueil neutre s'affiche sans layout ni
partial. - Le
mvc/racine du dépôt reste l'application de dogfooding, référencée par
la grande majorité de la suite de tests. Un squelette distinct ne la
touche pas.
Décision¶
-
Squelette dédié et curé. Le squelette de projet vit dans le dépôt sous
cli/skeleton/data/(même modèle quecli/starters/data/).
Il contient un projet Forge minimal :app.py,config.py,
requirements.txt,env/example, unmvc/minimal (route/, contrôleur
d'accueil neutre, vue d'accueil autonome,views/errors/*, paquets vides
forms/validators/entities/models/helpers),static/(avec
tailwind.csscompilé commité) etstorage/vide. -
Dépendance core via pip. Le
requirements.txtdu squelette dépend de
forge-mvc==<version>. Le projet généré importecoredepuis le paquet
installé, pas depuis uncore/local. Le squelette ne contient ni
core/, nicli/, niintegrations/, nipackages/, nitests/,
nidocs/. -
Distribution en package-data. L'arbre
cli/skeleton/data/est
déclaré enpackage-data(pyproject.toml+MANIFEST.in) afin qu'un
forgeinstallé viapipx/pippuisse matérialiser un projet sans
réseau ni git.forge newcopie cet arbre (shutil.copytree) au lieu
de cloner le dépôt. -
Le
git clonedu dépôt est retiré deforge new, ainsi que les
options associées (--ref,_FORGE_REPO,_FORGE_DEFAULT_REF). La
dépendance àgitn'est plus requise pour créer un projet (opensslreste
nécessaire pour les certificats de développement). -
Anti-dérive.
app.pyetconfig.pyexistent en double (racine du
dépôt pour le dogfood, et squelette). Un test de cohérence garantit que la
copie du squelette reste identique à la version racine, ou que toute
divergence est intentionnelle et documentée.
Précision sur « sans réseau »¶
La matérialisation des fichiers du squelette ne requiert ni réseau ni
git. En revanche, l'installation des dépendances (pip install -r
requirements.txt, qui tire forge-mvc depuis PyPI ou un index local) reste
une étape réseau normale, inchangée.
Conséquences¶
Positives¶
- Un projet nu est réellement nu : pas de framework, pas de tests, pas de
docs, pas d'app de démo embarqués (principes 1 et 8). - Plus de landing marketing, d'auth, de MFA, de media ni de mail pré-livrés :
ces capacités passent par leurs starters et opt-ins respectifs, cohérent
avec la trajectoire « un parcours welcome par opt-in » et avec ADR-023. - Création de projet sans
gitni réseau pour la partie fichiers ; plus
rapide et plus robuste (pas de clone--depth=1du dépôt entier). - Le
coredu projet provient du paquet versionné : le projet n'est plus
couplé à une copie locale du framework.
Limites¶
app.py/config.pysont dupliqués (dogfood vs squelette) : géré par le
test de cohérence anti-dérive.- Rupture de l'API CLI :
forge new --ref <branche>disparaît. En phase bêta
pré-1.0, retrait direct, sans alias (convention pré-1.0 deCLAUDE.md). - Le squelette doit être maintenu à la main au fil des évolutions runtime ;
des garde-fous (tests d'inventaire et de neutralité) limitent la dérive.
Alternatives écartées¶
A : Élagage post-clone¶
Conserver le git clone du dépôt, puis supprimer les chemins non désirés et
réécrire un mvc/ minimal.
Rejeté : on clone toujours tout le dépôt (lent, dépend de git/réseau), et la
liste d'élagage dérive à chaque évolution du dépôt. Ne traite pas la cause
(règle A) : le projet importerait encore un core/ local.
B : Amincir le mvc/ racine¶
Rendre le mvc/ racine du dépôt minimal et déplacer l'app de démo ailleurs.
Rejeté : le mvc/ racine est l'application de dogfood référencée par la quasi
totalité de la suite de tests. Le déplacer casse ou déplace des centaines de
tests, pour un bénéfice qu'un squelette distinct obtient sans risque.
C : Squelette sur une branche dédiée clonée¶
forge new clonerait une branche skeleton au lieu du dépôt entier.
Rejeté : conserve la dépendance git/réseau et la fragilité au tag, et empêche
la création hors-ligne. Le package-data est la seule option compatible avec
un forge pipx-installé.
Hors périmètre de cet ADR¶
- Le contenu pédagogique exact de la page d'accueil neutre du squelette.
- La décision « source unique vs copie contrôlée » pour
app.py/config.py
(tranchée à l'implémentation, sous contrainte du test anti-dérive). - L'éventuelle publication d'un index pip local pour les environnements
totalement hors-ligne.
Référence¶
- Tickets d'implémentation (séquence) :
SKELETON-TREE-001,
SKELETON-PKGDATA-001,SKELETON-REGISTRY-001,NEW-MATERIALIZE-001,
NEW-CORE-DEP-001,SKELETON-GUARD-001,NEW-CLI-CLEANUP-001. - Code concerné :
forge.py(cmd_new,_clone_skeleton),
pyproject.toml,MANIFEST.in, futurcli/skeleton/. - ADR-004 Périmètre du core :
docs/adr/004-core-perimeter.md. - ADR-005 Packaging :
docs/adr/005-packaging.md. - ADR-023
forge starter:buildcanonique :docs/adr/023-starter-build-canonical.md. - Garde-fou existant :
tests/test_skeleton_routes_neutral_001.py. - Charte :
CHARTE_DOC.md(principes 1, 2, 8 ; règle A).