ADR 0005 – Champ référence : `format: node-ref` stocké dans les données d'aspect
Accepté (2026-10-14)
ADR 0005 – Champ référence : format: node-ref stocké dans les données d’aspect
Statut : Accepté (2026-10-14)
Contexte
Le cahier (jalon 1, « Champ référence ») demande un champ d’aspect qui pointe vers un nœud filtré par type ou par aspect. Les relations typées (node_link, ADR 0001) existent déjà, avec des contraintes source/target. Il faut fixer trois choses difficiles à changer une fois que l’utilisateur a écrit ses fichiers et saisi des données : l’écriture YAML du champ, l’emplacement de la valeur, et le comportement quand la cible change ensuite (corbeille, aspect retiré).
Décision
-
Format : un champ référence est une propriété de premier niveau du JSON Schema de l’aspect, de type chaîne, portant
format: node-refet une contrainte facultativex-target:projet: type: string format: node-ref title: Projet x-target: { aspects: [projet] } # facultatif ; types: [...] possible aussix-targeta la même sémantique quesource/targetd’un lien : le nœud cible porte tous les aspects listés et, sitypesest présent, a l’un de ces types. Un seul record Java (NodeFilter) et un seul contrôle (LinkConstraints) servent aux deux. -
Stockage : la valeur est l’UUID du nœud cible, en chaîne, dans le JSONB
node_aspect.data({"projet": "6f1c…"}). Aucune lignenode_linkn’est créée. La modification passe paraspect.added/aspect.updated(avant/après), comme tout champ. -
Contrôles au chargement :
x-targetn’est accepté qu’avecformat: node-ref;node-refn’est accepté qu’au premier niveau et surtype: string(jalon 1 : pas de tableau) ; les noms dex-targetdoivent correspondre à des fichiers déclarés dansconfig/aspects/etconfig/types/(contrôle des fautes de frappe, sans invalidation en cascade). -
Validation à l’écriture : la syntaxe UUID est vérifiée par le JSON Schema (format
node-refenregistré dansJsonSchemas). Ensuite, pour les seules références nouvelles ou modifiées : le nœud existe, n’est pas en corbeille, n’est pas le nœud lui-même et satisfaitx-target. -
Tolérance à la lecture : une référence inchangée n’est pas recontrôlée. Une cible passée ensuite en corbeille, retirée de son aspect ou purgée n’empêche ni la lecture ni la modification des autres champs. Le titre de la cible est résolu côté serveur dans
NodeView.references, avec un indicateurdeleted.
Alternatives écartées
- Créer et maintenir un
node_linkmiroir pour chaque référence : deux sources de vérité à synchroniser (un retrait dans le panneau Liens désynchroniserait le champ), un type de lien implicite à inventer, des événements doublés. Les rétroliens peuvent être obtenus sans copie par une requêtedata @> '{"champ": "uuid"}', couverte par l’index GINjsonb_path_opsexistant. - Remplacer le champ par un lien typé (le formulaire écrirait un
node_link) : la valeur sortirait des données d’aspect. Elle ne serait plus visible dans les expressions (tache.projet), ni dans les colonnes de vues, ni dans l’export frontmatter. - Mot-clé
targetsans préfixe, ou$ref-like propriétaire :x-signale une extension non standard, que les outils JSON Schema ignorent proprement. - Recontrôler toutes les références à chaque écriture : une cible mise en corbeille bloquerait toute modification du nœud source.
- Vérifier les noms de
x-targetcontre les définitions valides : invaliderait en cascade un aspect quand un autre fichier est cassé. La contrainte porte sur des noms stockés (node.type,node_aspect.aspect) et reste évaluable même si la définition visée est absente.
Conséquences
- Les références mono-valuées sont des attributs de l’aspect. Les relations multiples, ou celles qui ont leur propre cycle de vie, restent des liens typés. Le skill
definitions-formatdocumente cette distinction. - Les rétroliens issus des champs référence, les colonnes de vues (jalon 2) et l’export pourront lire la valeur dans le JSONB sans migration.
- Un tableau de références (
type: array, items: { format: node-ref },x-targetà côté duformatdansitems) pourra être ajouté plus tard sans changer l’écriture actuelle. - Rien n’empêche une référence de devenir pendante. L’interface doit afficher les cas « en corbeille » et « introuvable ».
- Changer un champ existant en
node-refrend non conformes les anciennes valeurs non UUID. Elles restent lisibles, mais toute réécriture de l’aspect sera refusée tant qu’elles ne sont pas corrigées (même règle que pour tout changement de schéma, migrations en V2).
Amendement (ADR 0033) : un champ cité par le
derivedFromd’un type de lien ne forme plus son propre groupe de rétroliens (« Tâche · Projet ») ; ses sources rejoignent le groupe du lien (« Éléments du projet »).