Second Brain documentation

ADR 0041 – Actions rapides déclaratives et actions en lot

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

← Toutes les décisions

ADR 0041 – Actions rapides déclaratives et actions en lot

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

La vague 1 (validée par Aurélien le 2026-10-15) demande deux choses :

  • des actions rapides déclaratives qui généralisent le report rapide (ADR 0025) à tout aspect, par exemple « Payée aujourd’hui » sur une facture ou « Mettre en pause » sur un projet. Elles doivent apparaître en boutons sur la fiche, dans les vues et dans les lignes de la revue hebdomadaire (ADR 0037) ;
  • des actions groupées par sélection multiple dans les vues et l’inbox (statut selon le workflow, report, tags, déplacement, classement), avec « Annuler ».

Décision – actions rapides

Clé quickActions d’un aspect

quickActions:
  - { label: Payée aujourd'hui, icon: paid, when: "!facture.payee", set: { payee: true, datePaiement: "{{today}}" } }
  - { label: Mettre en pause, icon: pause_circle, when: "projet.statut == 'actif'", set: { statut: en-pause } }
  • Généralisation de postpone. C’est la clé postpone de l’ADR 0025, renommée, avec la même structure (label, icon, set à variables, unset) et les mêmes contrôles au chargement. Elle gagne une condition facultative when, écrite dans le langage des vues (ADR 0028) et limitée aux champs de l’aspect.
  • Compatibilité. L’ancien nom postpone reste lu (alias), mais on ne peut pas donner les deux clés à la fois. L’endpoint …/postpone/{index} reste, marqué obsolète, à côté du nouveau POST /api/nodes/{id}/aspects/{aspect}/actions/{index}.
  • Pourquoi pas la clé actions. La clé actions existante, réservée au jalon 3, décrit des actions inter-aspects (set: { facture.payee: … }) évaluées par le futur moteur de règles. Les actions rapides restent dans leur aspect : c’est ce qui permet de les valider entièrement au chargement. L’exemple marquer-payee de facture.yaml, jamais exécuté, est remplacé par l’action rapide équivalente.
  • Condition when appliquée des deux côtés.
    • à l’affichage : NodeView.quickActions donne pour chaque action des aspects portés si elle est disponible, et ViewRow.actions donne les actions disponibles sur une ligne (aspect:rang), pour les aspects de la requête de la vue. Les deux sont calculés en SQL par ViewSql, en une requête ;
    • à l’écriture : une action dont la condition est fausse est refusée (400). Le workflow (ADR 0023) s’applique comme pour toute écriture.
    • L’interface masque aussi une action que le workflow interdit depuis l’état affiché.
  • Où elles apparaissent : à droite de l’intertitre de l’aspect dans la fiche ; dans une colonne « Actions » de la table (seulement si une ligne en a) ; en fin de ligne dans la liste ; sur les lignes des étapes de la revue.
  • Exemples de config/ : la facture gagne datePaiement et « Payée aujourd’hui ». Le projet gagne « Mettre en pause » (projet actif) et « Reprendre » (projet en pause), en plus de « Marquer revu ». La tâche garde ses trois reports.

Décision – actions en lot

Serveur : POST /api/nodes/bulk et POST /api/nodes/bulk/{undoId}/undo

  • Opérations. PATCH (champs d’un aspect porté, typiquement l’état), QUICK_ACTION, ADD_TAGS, REMOVE_TAGS, MOVE (parent, ou racine) et INBOX (Classé ou remettre dans l’inbox). Un lot compte 200 nœuds au plus.
  • Nœud par nœud. Chaque nœud passe par la méthode habituelle de NodeService, dans sa propre transaction : validation, workflow, journal node_event. Un refus (transition interdite, condition fausse, nœud introuvable) n’arrête pas le lot. Une erreur technique sur un nœud non plus : elle devient un refus « Erreur inattendue », journalisé, et les nœuds déjà modifiés restent annulables. Les tags du lot sont normalisés une seule fois (« #Perso » retire perso), et un tag invalide refuse le lot entier (400). La réponse compte les nœuds modifiés et ceux déjà dans l’état demandé, et liste les refus avec leur message. Pourquoi pas tout ou rien : dans une revue, on veut fermer les 9 tâches qui peuvent l’être, et voir la dixième qui résiste.
  • Annuler. Avant chaque modification réussie, le serveur garde la partie touchée du nœud : données de l’aspect, tags saisis, parent ou présence dans l’inbox. undo la rétablit, nœud par nœud et dans l’ordre inverse.
    • Pour un aspect, le retour ne passe pas par le contrôle du workflow (restoreAspect), puisqu’il revient à un état qui a existé : sinon, annuler « abandonnée » échouerait. L’événement aspect.updated porte reason: bulk.undo.
    • Le jeton vit 30 minutes en mémoire, sert une fois et ne survit pas à un redémarrage : « Annuler » répond à une erreur immédiate, pas à un historique.
    • Une modification faite entre-temps sur la même partie est écrasée. C’est le prix d’un retour simple, et c’est rare dans ce délai. Un aspect retiré entre-temps n’est pas rajouté : son retour est refusé. Annuler « Classé » remet l’élément dans l’inbox à la date du retour, donc en tête.

Interface

  • Table des vues : une case par ligne et une case « tout » dans l’en-tête (sélection AG Grid, lignes de groupe exclues).
  • Inbox : une case par élément et « Tout sélectionner ».
  • Barre d’actions (BulkBar), affichée dès qu’un élément est coché :
    • « Statut » : les états du workflow des aspects de la vue ;
    • « Actions » : leurs actions rapides ;
    • « Tags » (ajouter ou retirer) ;
    • « Déplacer » : le dialogue de déplacement, en mode choix seul ;
    • dans l’inbox, « Classé » ;
    • « Désélectionner ».
  • Après un lot, une notification résume le résultat (« 3 modifiés · 1 refusé ») et propose « Annuler » pendant 10 secondes. Les refus sont listés, et la liste est relue ; dans l’inbox, seuls les éléments refusés restent cochés.
  • Le retour ne dépend pas de la barre (BulkFeedback, relevé en revue). La barre disparaît souvent avec sa sélection : lignes sorties de la vue, inbox relue. Le résumé et les refus sont donc affichés par la page (app-bulk-status), et « Annuler » relit la page par une fonction qu’elle a fournie.
  • Clavier dans la colonne « Actions » : Entrée sur la cellule donne le focus au premier bouton, Tab passe d’un bouton à l’autre, et un clic dans la cellule n’ouvre pas la ligne. Un double clic sur une action rapide n’en envoie qu’une, et l’action faite est annoncée.

Correction liée : prochaine action et date de début

À la demande du fil « Étape 0 », la prochaine action (ADR 0037) ignore maintenant une tâche qui ne commence que plus tard. Si l’aspect de from déclare deferUntil (ADR 0039), un candidat dont la date est après aujourd’hui n’est pas retenu, même s’il est forcé.

Reporté

  • Sélection multiple dans la liste et le kanban : la liste a ses actions rapides, mais pas de cases à cocher, et le kanban n’a pas encore de boutons sur ses cartes.
  • Annuler une action rapide isolée, hors lot : on refait l’action inverse, ou on modifie le champ.
  • Libellés des états d’une énumération (« à faire » plutôt que a-faire) : la valeur brute s’affiche, comme ailleurs.
  • La disponibilité affichée sur la fiche (et sa prochaine action) est évaluée dans le fuseau du serveur, faute de fuseau sur GET /api/nodes/{id}. Une condition sur today peut donc différer d’un jour autour de minuit pour un navigateur dans un autre fuseau ; l’écriture, elle, tranche dans le fuseau du navigateur.

Conséquences

  • Un geste fréquent sur un aspect se déclare dans son YAML, sans code ; il apparaît partout où l’aspect est montré.
  • Toute nouvelle écriture de nœud doit pouvoir servir une opération de lot, et dire ce qu’il faut garder pour l’annuler.

Amendement (ADR 0049) : « Annuler » ne rétablit plus que les champs de l’aspect écrits par l’opération ; les autres gardent leur valeur du moment. L’opération TRASH (mise à la corbeille) s’annule en sortant le nœud de la corbeille.