Second Brain documentation

ADR 0065 – Effets des changements d'état

Accepté (2026-10-16, mode autonome)

← Toutes les décisions

ADR 0065 – Effets des changements d’état

Statut : Accepté (2026-10-16, mode autonome)

Item 33 du jalon 3 : quand une tâche passe à « fait », retenir le jour où elle a été terminée ; quand elle est abandonnée, demander pourquoi.

Contexte

  • Le workflow d’un aspect (ADR 0023) ne dit que les transitions permises. Rien n’est écrit en changeant d’état.
  • La vue « Terminées » (ADR 0038) se sert de updatedAt comme jour de fin. Une tâche terminée puis retouchée (une étiquette, un lien) change de jour.
  • Les tâches abandonnées n’ont pas d’histoire : on ne sait plus, en revue, pourquoi on les a laissées.
  • Le domaine vit dans config/ : ni « fait » ni « termineLe » ne doivent apparaître dans le code.
  • On change d’état depuis la fiche, le kanban, une cellule de table, une action en lot et une action rapide. Toutes ces écritures passent par NodeService.writeAspect.

Décision

  • Deux clés de plus dans workflow, enter et leave, par état. Chaque effet a trois clés facultatives :

    • set : champs écrits. Les variables des modèles ({{today}}, {{now}}, avec décalage, ADR 0016) y sont résolues dans le fuseau du serveur.
    • unset : champs retirés.
    • require : champs qui doivent être remplis (texte non blanc), à l’entrée seulement.
    workflow:
      field: statut
      transitions: { … }
      enter:
        fait: { set: { termineLe: "{{today}}" } }
        abandonne: { set: { termineLe: "{{today}}" }, require: [raisonAbandon] }
      leave:
        fait: { unset: [termineLe] }
        abandonne: { unset: [termineLe, raisonAbandon] }
  • Application (Workflows.applyEffects), avant la validation du schéma, quand l’état change de A à B : effets de sortie de A, puis effets d’entrée dans B.

    • Un champ que l’écriture change elle-même (valeur différente de l’enregistrée) n’est ni écrit ni retiré par un effet : la saisie explicite l’emporte. On peut donc corriger la date de fin en même temps qu’on termine.
    • Les champs requis vides donnent un refus 400 par champ, aspects.<aspect>.<champ> : « Requis pour passer à l’état abandonne ». Il vient après le contrôle de la transition, qui reste prioritaire.
    • Sans changement d’état, rien.
  • Naître dans un état, c’est y entrer (création d’un nœud, aspect ajouté, changement de type) : les effets d’entrée écrivent les champs absents. Les champs requis ne sont pas exigés, comme la première valeur du workflow est libre (ADR 0023). Un import, un connecteur ou un modèle peut créer une tâche déjà abandonnée.

  • L’annulation d’un lot (ADR 0041, 0049) remet les champs tels qu’ils étaient, sans effet. Les champs qu’un effet a changés font partie des champs à rétablir : annuler un abandon retire aussi la date de fin et la raison.

  • Récurrence (ADR 0046) : la nouvelle occurrence quitte l’état terminé. Les effets de sortie et d’entrée s’appliquent à sa copie : la date de fin de l’occurrence terminée ne passe pas à la suivante.

  • Contrôle des définitions (fichier ignoré et signalé sinon) :

    • les états sont dans l’énumération ;
    • les champs sont connus et ne sont ni le champ d’état, ni une référence à un nœud, ni un numéro attribué (x-sequence) ;
    • un champ requis par le schéma n’est pas retiré, et un même champ n’est pas à la fois retiré et écrit ou requis ;
    • les valeurs de set ont des variables connues et sont conformes au schéma une fois résolues ;
    • require n’est admis que dans enter.
  • Interface :

    • Dans la fiche, un champ requis par un état devient obligatoire quand le formulaire choisit cet état. Il est masqué tant qu’il est vide et que l’état choisi ne l’exige pas : la raison d’abandon n’encombre pas une tâche ouverte.
    • Hors de la fiche (glisser dans le kanban, cellule de table, statut en lot), un dialogue demande les champs requis avant d’écrire. Annuler laisse la carte ou la cellule où elle était. En lot, la valeur saisie vaut pour tous les nœuds.
  • Domaine (config/) :

    • tache gagne termineLe (date, « Terminée le ») et raisonAbandon (texte de 500 caractères au plus, « Raison de l’abandon »), avec les effets ci-dessus.
    • La vue « Terminées » regroupe et trie par tache.termineLe. Une tâche terminée avant cette version (sans date de fin) y reste si sa dernière modification date de moins de 30 jours, rangée à la fin dans le groupe sans date.

Conséquences

  • Les données anciennes ne sont pas reprises : aucune migration ne remplit termineLe, puisqu’une migration ne doit pas connaître le domaine. La vue « Terminées » garde updatedAt en secours pour retenir une tâche, pas pour la dater : un regroupement ne combine pas deux champs.
  • La date de fin est un jour et non un instant : c’est ce que montrent les vues, et c’est ce qu’on corrige à la main.
  • raisonAbandon est un champ texte libre de l’aspect : il est cherchable comme les autres (ADR 0013).
  • Un connecteur qui passera une tâche à « abandonne » (jalon 3, synchronisations) devra fournir la raison, ou la transition sera refusée comme pour une saisie. Une création reste libre.
  • Les effets ne déclenchent rien d’autre qu’une écriture de champs : pas de lien, pas de notification. Ce rôle reviendra aux règles du moteur (jalon 3), qui pourront s’appuyer sur ces mêmes changements d’état.