Aller au contenu

ADR-051 : Insertion d'une méthode dans le contrôleur des pages publiques

Statut

Proposé, Forge 1.0.0-beta.17 (ticket ARCH-USER-CONTROLLER-REWRITE-ADR-001).

ADR distinct annoncé par l'ADR-030, qui a
explicitement laissé hors de son périmètre la réécriture in-place d'un
contrôleur utilisateur (« question distincte, à trancher séparément, principe
4 »). Tant que cet ADR n'est pas accepté par le mainteneur, le comportement
décrit reste celui livré, sans modification de la charte.


Date

2026-06-26


Contexte

forge make:public-page <Nom> génère une page publique : un template, une route
(via le mécanisme d'injection cadré par l'ADR-030) et une méthode de
contrôleur
. Cette dernière est posée par _ensure_controller_method
(cli/public/public_page.py), qui écrit dans
mvc/controllers/public_pages_controller.py, un fichier sous contrôle de
l'utilisateur.

Trois cas se présentent :

  1. Fichier absent : Forge crée public_pages_controller.py avec la classe
    PublicPagesController et la méthode. C'est une écriture write-if-new,
    conforme au principe 9 (création d'un fichier nouveau).
  2. Méthode déjà présente : un test (def <méthode>() détecte la méthode ;
    Forge ne touche à rien. Comportement idempotent.
  3. Fichier présent sans la méthode : Forge insère la méthode dans la
    classe PublicPagesController existante. L'insertion repère la fin de la
    classe par analyse AST (ast.parse, ClassDef.end_lineno), insère le
    bloc de méthode, puis réécrit le fichier.

Le cas 3 est une modification in-place d'un fichier sous contrôle
utilisateur
, ce que le principe 4 (préserver le code utilisateur) et le
principe 9 (pas d'écriture invisible) encadrent strictement. C'est le point que
l'ADR-030 a renvoyé à un ADR dédié.

Garde-fous déjà présents dans le code livré :

  • Explicite : l'écriture résulte d'une commande tapée par l'utilisateur
    (make:public-page), jamais d'un autre flux.
  • Idempotent : la présence de la méthode est détectée avant insertion (cas
    2), ré-exécuter ne duplique pas.
  • Fail-safe : si le fichier ne parse pas (SyntaxError) ou si la classe
    PublicPagesController est absente, Forge n'écrit pas et renvoie un
    avertissement invitant à compléter le contrôleur manuellement.
  • Ciblé : l'insertion ne vise qu'un fichier et une classe de nom
    conventionnel Forge (public_pages_controller.py / PublicPagesController),
    jamais un contrôleur métier arbitraire.
  • Annoncé : make_public_page renvoie un résultat (controller_changed,
    avertissement éventuel) affiché à l'utilisateur.

Limite assumée : si l'utilisateur a écrit lui-même une classe
PublicPagesController dans ce fichier conventionnel, Forge y insérera quand
même la méthode. Le nom est conventionnel et le fail-safe couvre les cas
ambigus, mais l'écriture reste in-place.


Décision (proposée)

Acter le comportement du cas 3 comme conforme, en étendant au seul
contrôleur des pages publiques les quatre conditions de l'ADR-030 (explicite,
idempotent, délimité/visible, annoncé), renforcées par deux garanties propres au
code utilisateur :

  1. Fail-safe : aucune écriture si le fichier ne parse pas ou si la classe
    cible est absente ; un avertissement oriente vers une complétion manuelle.
  2. Ciblé sur un nom conventionnel Forge : seul
    mvc/controllers/public_pages_controller.py / PublicPagesController est
    modifié, jamais un contrôleur métier arbitraire.

La charte (principe 4) sera précisée, après validation du mainteneur, pour
distinguer la réécriture in-place silencieuse et arbitraire (interdite) de
l'insertion explicite, idempotente, fail-safe et ciblée sur un fichier
conventionnel généré par Forge (autorisée pour make:public-page).


Conséquences

  • make:public-page conserve sa valeur (câbler une page publique d'un geste)
    sans dégrader l'UX vers un mode « affichage » pur.
  • Le périmètre reste étroit : aucune autre commande n'est autorisée à modifier
    in-place un contrôleur ; les contrôleurs métier restent intouchables.
  • Un test de garde-fou pourra verrouiller les invariants (fail-safe sur fichier
    non parsable, idempotence, ciblage sur PublicPagesController).

Alternatives écartées

  • Re-scoper make:public-page en mode affichage (comme make:crud) :
    respecterait le principe 4 à la lettre mais imposerait à l'utilisateur de
    coller la méthode lui-même, dégradant une commande dont la valeur est le
    câblage d'un geste. Corriger ce symptôme plutôt que préciser la portée du
    principe 4 contredit la règle d'évolution A (retirer la cause, pas le
    symptôme).
  • Laisser la contradiction implicite : expose à un reproche d'audit
    récurrent sur un principe non négociable, comme c'était le cas avant
    l'ADR-030.

Référence

  • ADR-030 : injection de routes par commande
    explicite, qui a renvoyé ce point à un ADR distinct.
  • cli/public/public_page.py (_ensure_controller_method).
  • Charte : CHARTE_DOC.md (principe 4, préserver le code utilisateur ; principe
    9, pas d'écriture invisible ; règle d'évolution A).