Roadmap : 1.0.0-beta.13¶
Thème : dernière beta fonctionnelle, slugs, gel du contrat public,
production-readiness. Finalité :RELEASE-BETA13-001.Principe directeur : finir et figer ce qui existe ; une seule vraie
feature (slugs). beta.14 sera la beta de stabilisation (doc + field-fixes,
zéro feature), jalon séparé, hors de cette roadmap.
État de l'existant (BETA13-EXISTING-AUDIT-001)¶
Beaucoup de « net-new » supposé est en réalité déjà présent, partiel. Cet
audit dimensionne la roadmap : 🔨 construire · 🔧 compléter/durcir · 📋 auditer/documenter.
| Domaine | Existe déjà | Manque | Verdict |
|---|---|---|---|
| slug (type) | type slug dans le contrat d'entité (cli/entities/validation.py) + SlugField CRUD + _slugify (validateur strict, rejette les accents) + slugify_migration_name |
vrai slugify (translittération accents), find_by_slug, routing public {slug}, gestion d'unicité ; 3 fonctions slug à consolider (§11) |
🔧 + 🔨 |
| forge doctor | 11 checks (db, env, ssl, migrations, structure, modules…) | checks prod-sécurité (debug off, cookies sécurisés, session non-mémoire, secret présent, perms storage/) |
🔧 étendre |
| /health | documenté (GET /health → {"status":"ok"}) |
vérifier l'implémentation réelle + option --db |
📋 + 🔧 |
| migrations | migration:status/apply/make/diff |
pas de dry-run/confirm/garde-env trouvés sur apply |
🔨 durcir (réel) |
| erreurs prod | gating dev/prod existe (DX-RENDER-ERROR-001, core/http/helpers.py) |
request-id, masquage secrets, audit 403/404/500 | 🔧 compléter |
| forge update | commande existante (détection pipx) | finalisation (pip/pipx/source, jamais d'écrasement projet) | 🔧 finaliser |
| uploads | durci récemment (SEC-UPLOAD-IMAGE-VERIFY-001/002) |
audit final (traversal, MIME, taille configurable) | 📋 auditer |
Conclusion : b13 = auditer → consolider → durcir → geler. Seuls
slug-routing et migration-safety sont du vrai « construire ».
Phase 0 : Mise à niveau (immédiat, dé-risque tout)¶
| Ticket | Type | Objet |
|---|---|---|
BETA13-EXISTING-AUDIT-001 |
📋 | Cet audit (table ci-dessus). Fait dans ce document. |
CHANGELOG-DELTA-B12-B13-001 |
📋 | Section beta.13 documentant les 65 commits post-beta.12. |
Phase 1 : Gel du contrat public (cheap, arrête la valse de renommages)¶
| Ticket | Type | Objet · ⛓️ |
|---|---|---|
CLI-PUBLIC-CONTRACT-FREEZE-001 |
📋 | Commandes publiques vs internes ; verrouiller opt-in:* (enable/disable réels = route-only, final) ET module:* (public-stable, A2) ; codes de sortie ; forge help ↔ cli-commands.md. |
STARTERS-FINAL-CONTRACT-001 |
📋 | Figer les 16 starters (noms, ordre, routes, fichiers). |
DOCS-LINKS-FINAL-AUDIT-001 |
📋 | mkdocs --strict + audit liens/ancres après la réorg docs. |
Phase 2 : Slugs (la seule vraie feature) ⛓️ après Phase 0¶
| Ticket | Type | Objet |
|---|---|---|
ADR-017-SLUG-TYPE |
🔨 | ADR : sémantique du type slug dans le contrat d'entité (lien ADR-013, §10). |
SLUG-CORE-001 |
🔧 | Consolider les fonctions slug en un seul core/http/slug.py canonique. stdlib seul (unicodedata NFKD). |
SLUG-VALIDATION-001 |
🔧 | is_valid_slug officiel, path-safe, message stable. |
SLUG-SQL-CRUD-001 |
🔧 | VARCHAR(180) NOT NULL + UNIQUE ; CRUD : slug depuis champ source, rejet des doublons (suffixe auto → post-1.0). |
SLUG-ROUTING-001 |
🔨 | find_by_slug + route publique /{ressource}/{slug}. |
SLUG-DOCS-001 |
📋 | Usage, génération, unicité, route publique, limites. |
Phase 3 : Production readiness (surtout durcir/documenter) : ✅ complète¶
| Ticket | État | Objet |
|---|---|---|
PROD-DOCTOR-001 |
✅ livré | check Sécurité prod dans forge doctor (séparation DB admin/app, uploads bornés). |
MIGRATION-SAFETY-FINAL-001 |
✅ livré | migration:apply --dry-run (aperçu + SQL sans appliquer). Suivi/checksums/journal déjà présents. |
PRODUCTION-CHECKLIST-DOCS-001 |
✅ livré | docs/deployment/production-checklist.md + guide comptes MariaDB. |
ERRORS-PROD-FINAL-001 |
✅ déjà présent | pages d'erreur sobres 400/403/404/413/422/429/500 + handlers ; stacktrace jamais en HTML, détails en logs (_log_runtime_error). |
HEALTHCHECK-FINAL-001 |
✅ déjà présent | GET /health → {"status":"ok"} (app.py) + test_health_endpoint_001. |
FORGE-UPDATE-FINAL-001 |
✅ déjà présent | forge update --check/--dry-run/--pre + détection pipx + test_forge_update_command_001. |
Audit de clôture Phase 3 : les trois derniers tickets étaient déjà
implémentés et testés dans Forge (vérifiés, pas re-développés). La Phase 3
ajoute le check sécurité-prod du doctor, le dry-run des migrations, et la
documentation de déploiement.
Phase 4 : Field test (dogfooding) ⛓️ après 2+3 : ✅ complète¶
| Ticket | État | Objet |
|---|---|---|
BETA13-DOGFOOD-001 |
✅ livré | Pipeline slug validé bout-en-bout contre une vraie MariaDB, sous forme reproductible : tests/test_e2e_slug_mariadb.py (gated FORGE_E2E_MARIADB). |
Forme retenue : plutôt qu'une app jetable, un test e2e committé qui
rejoue le pipeline réel, entitéArticle(title+slugauto depuis
title) →build_entity_sql→apply_model_sqlsur la base → introspection
(Slug VARCHAR(180)+UNIQUE) → runtimeslugify()→INSERT→lecture par slug
→ rejet du doublon par la contrainte d'unicité. 7/7 verts sur MariaDB ;
SKIPPED proprement sans la base. C'est le go : le slug fonctionne en réel.
Phase 5 : Clôture & release ⛓️ tout vert¶
| Ticket | État | Objet |
|---|---|---|
BETA13-CLOSING-AUDIT-001 |
✅ vert | Batterie complète : pytest 16587 passed / 0 échec + ruff + compileall + mkdocs --strict + git diff --check + sync:landing --check. |
BETA13-BUMP-001 |
✅ livré | Bump b12→b13 sur 23 fichiers (7 pyproject.toml + forge.py + app.py + core/__init__.py + 6 __init__.py opt-in + package.json/package-lock.json + CHANGELOG daté + roadmap + landing resync), pins Alpha ==1.0.0b13 (media, mfa). Cohérence vérifiée (tests/meta/ 6955 passed, forge --version = Forge 1.0.0b13). |
RELEASE-BETA13-001 |
⏸️ en attente | Tag v1.0.0-beta.13, build, publication PyPI (--pre), sync landing publique. Action irréversible/sortante, sur autorisation explicite uniquement. |
BETA13-POST-PUBLISH-VERIFY-001 |
⏸️ en attente | Vérif PyPI publique + install propre. |
Seuil de release : le bump est finalisé et toute la cohérence est verte.
Le tag git et la publication PyPI (étapeRELEASE-BETA13-001) ne sont pas
réalisés, ils relèvent d'une décision explicite (publication = action
publique et irréversible).
Chemin critique¶
Phase 0 ─┬─▶ Phase 1 (gel) ─────────────────┐
└─▶ Phase 2 (slugs) ──┐ ├─▶ Phase 5 (clôture → release)
Phase 3 (prod) ─────┴─▶ Phase 4 ─┘
(dogfood)
Phases 1 et 3 parallélisables avec la 2. Phase 0 débloque tout.
Phase 4 (dogfood) = vrai go/no-go avant clôture.
Définition de « beta.13 prête »¶
- 16 starters + contrat CLI (
opt-in:*/module:*) gelés et documentés. - Slugs complets (type + slugify canonique + validation + SQL/CRUD + routing public + doc) et une app réelle construite avec.
- Production : doctor/migrations/erreurs/health/update durcis + checklist déploiement.
BETA13-CLOSING-AUDIT-001vert, versions bumpées b13, CHANGELOG complet.
Note, opt-in
forge-mvc-videolivré en parallèle. Hors de la
définition initiale ci-dessus, l'opt-inforge-mvc-videov1 (upload,
transcodage MP4 H.264/AAC, lecture HTTP Range, commandesvideo:*) a été
développé et mergé surmainpendant ce cycle, puis publié avec les autres
distributions en1.0.0-beta.13. Audit de clôture :audit-video-closing.md.
Hors périmètre b13 (→ post-1.0)¶
Suffixe slug auto, slug_history/redirections 301, sitemap, recherche avancée,
pagination avancée, admin, multi-SGBD, Docker officiel, monitoring, ORM, SPA,
marketplace.
Annexe : Carte d'implémentation SLUG-SQL-CRUD-001¶
Investigation faite ; design figé ; implémentation reportée (effort
dédié). À reprendre idéalement avec MariaDB pour valider le CRUD généré,
ou via la Phase 4 (dogfood). Cette carte évite de refaire l'investigation.
Décisions figées¶
- Le slug est généré depuis un champ source (ex.
titre), pas saisi. - Slug stable à l'édition (généré une fois à la création ; changer le titre
ne change pas l'URL → pas de redirection 301, reportée post-1.0). - Doublon d'unicité → erreur claire (« ce slug existe déjà »), pas de
suffixe auto-2/-3(ADR-017 D4).
Constat clé du pipeline¶
Forge a un pipeline d'entité multi-couches : JSON utilisateur (type) →
normaliseur → forme canonique (sql_type / form.field) → générateurs.
slug existe au niveau form normalisé (SUPPORTED_FORM_FIELD_VALUES) mais
pas comme type utilisateur (l'enum type de field.schema.json ne le
contient pas). Le SQL est déjà géré (build_entity_sql produit UNIQUE KEY
depuis unique: true).
Changements par couche¶
| Couche | Fichier(s) | Changement |
|---|---|---|
| Schéma (×2 à synchroniser) | schemas/field.schema.json et cli/schemas/field.schema.json |
ajouter slug à l'enum type ; ajouter la propriété source (string) |
| Normaliseur | cli/entities/canonical_model_normalizer.py |
type:slug → sql_type:VARCHAR(180), form.field:slug ; propager source |
| Validation | cli/entities/validation.py |
source dans ALLOWED_FIELD_KEYS ; règle sémantique : source réfère un champ texte existant de l'entité |
| Form | cli/entities/crud/form_builder.py |
exclure du formulaire un champ slug porteur de source (auto-généré) |
| Contrôleur | cli/entities/crud/controller_builder.py |
à la création : data = dict(form.cleaned_data); data["<slug>"] = slugify(data["<source>"]) (via core.http.slug) avant add_…(data) ; envelopper l'INSERT pour capter l'erreur d'unicité → message clair |
| Modèle | cli/entities/crud/model_builder.py |
exclure le champ slug auto de l'UPDATE (stable à l'édition) ; le garder dans l'INSERT |
Garde-fous / validation¶
- Tests structurels façon
test_make_crud: le contrôleur généré contient
slugify(, le slug est absent du formulaire, l'INSERT a la colonne slug,
l'UPDATEne l'a pas. compilealldu CRUD généré (échantillon) pour attraper toute erreur de
syntaxe.- Validation runtime (DB) : déférée au dogfood Phase 4 (les e2e MariaDB
sont gated par la disponibilité d'une base).
Avancement & inconnues résolues (investigation)¶
- ✅ Commit A livré (
af09e7a) :type: slug→VARCHAR(180)+SlugField
(saisie manuelle validée). Le type slug est utilisable dès maintenant. - Commit B (auto-génération depuis
source), ~6 fichiers : schémas ×2
(source), normaliseur (propagesource),validation.py(sourcedans
ALLOWED_FIELD_KEYS+ règle « réfère un champ texte existant »),
crud/utils.py(helper_is_generated(f)= a unsource;_non_pk_fields
est partagé form/model → l'exclusion doit être ciblée),form_builder
(exclure le champ généré),controller_builder
(_data["slug"] = slugify(_data[source])avantadd_…),model_builder
(exclure le slug généré de l'UPDATE). - Doublon : la contrainte
UNIQUEgarantit déjà zéro slug dupliqué.
Le message d'erreur clair (catchDoublonError) est une lacune générale
du générateur (le CRUD généré ne wrappe l'INSERT pour aucun champ
unique aujourd'hui) → sous-chantier séparéCRUD-DUP-HANDLING-001, pas
slug-spécifique.