ADR 0023 – Workflow de statuts : transitions vérifiées à l'écriture, options bornées dans le formulaire
Accepté (2026-10-15, mode autonome)
ADR 0023 – Workflow de statuts : transitions vérifiées à l’écriture, options bornées dans le formulaire
Statut : Accepté (2026-10-15, mode autonome)
Décisions prises en mode autonome. Aurélien a demandé, le 2026-10-15, de choisir les options les plus simples, sûres et réversibles.
Contexte
La clé workflow d’un aspect était déjà lue et validée de façon superficielle (field doit exister), mais rien ne l’appliquait. Elle déclare un champ d’état et, pour chaque état, les états atteignables :
workflow:
field: statut
transitions:
a-faire: [en-cours, fait, abandonne]
en-cours: [a-faire, fait, abandonne]
fait: [a-faire]
abandonne: [a-faire]
Décision
Au chargement
workflow.field doit désigner un champ de premier niveau, requis (required) et à énumération (enum). Sans required, une valeur vide permettrait de contourner le contrôle (fait → vide → en-cours).
Chaque état de l’énumération doit être une clé de transitions. [] déclare un état sans sortie. Un état oublié serait sinon toléré sans signal. Les clés et les états cibles doivent tous appartenir à l’énumération. Toute faute rend l’aspect invalide (ADR 0002).
À l’écriture (Workflows.check, appelé par PUT /api/nodes/{id}/aspects/{aspect})
La règle ne s’applique qu’au remplacement des données d’un aspect déjà porté. La première valeur, à la création, à l’ajout de l’aspect, au changement de type ou depuis un modèle, est libre parmi l’énumération : on peut créer une tâche directement « fait ».
Ne sont jamais refusés :
- une valeur inchangée, ce qui permet de modifier les autres champs d’une tâche terminée ;
- un champ retiré, laissé à la seule validation du JSON Schema (
required) ; - un état ancien, devenu hors énumération après une modification du YAML : on peut en sortir vers tout état. C’est le principe « valider à l’écriture, tolérer à la lecture ».
Sinon, la réponse est 400 avec errors["aspects.<aspect>.<champ>"] = ["Transition interdite : fait → en-cours (autorisées : a-faire)"], sans aucune écriture.
L’événement reste aspect.updated avec l’avant/après du champ, déjà journalisé. Aucun type d’événement n’est ajouté.
Dans le formulaire
Pour une section d’aspect déjà enregistrée, AspectFormBuilder ne propose dans la liste d’état que la valeur enregistrée et ses transitions déclarées (restrictWorkflow). La référence est la valeur lue au chargement, pas la saisie en cours. Elle le reste quand l’aspect est retiré puis ré-ajouté avant l’enregistrement : aucun retrait n’est envoyé, et le serveur juge depuis l’état enregistré. De même, choisir « en-cours » puis « fait » avant d’enregistrer produit un seul passage, de l’état enregistré à « fait », que le serveur juge. Une section nouvelle propose tous les états.
Alternatives écartées
- Vérifier à chaque écriture, création comprise, avec un état initial obligatoire : il empêcherait de saisir une tâche déjà faite ou de créer depuis un modèle un nœud dans un autre état. On pourra déclarer
initialplus tard si le besoin apparaît. - Type d’événement dédié
status.changed:aspect.updatedporte déjà l’avant/après ; un type de plus serait à maintenir dans l’historique et les règles (jalon 3). - Libellés d’état dans le YAML (
x-labels) : utile, mais indépendant ; les valeurs brutes restent affichées pour l’instant.
Conséquences
- Le kanban (jalon 2) devra appliquer la même règle au glisser-déposer, et le serveur la vérifiera de toute façon.
- Un changement de type qui conserve un aspect déjà porté ne touche pas à son état : rien à vérifier.
- Retirer un aspect facultatif, enregistrer, puis l’ajouter de nouveau remet son état à zéro. C’est la règle « première valeur libre », assumée.
- L’enregistrement de l’éditeur envoie chaque section. Si un autre onglet a changé l’état entre-temps, l’enregistrement peut être refusé pour une transition que l’utilisateur n’a pas faite ; il suffit de recharger. N’envoyer que les sections modifiées est reporté.
- Les règles du jalon 3 (
set:sur un champ d’état) passeront par la même vérification : une règle ne pourra pas imposer une transition interdite.