Files
ObsiGate/docs/features/fusion-journal-evenements-220.md
T
Spark f434f30286
CI / lint (push) Successful in 2m53s
CI / security (push) Failing after 1m46s
CI / test (push) Successful in 4m57s
CI / build (push) Successful in 1m36s
CI / e2e (push) Successful in 17m40s
feat: émission d'événements vers le journal du service d'agents #220
2026-10-22 19:58:25 +00:00

5.0 KiB

#220 — Fusion : émission d'événements vers le journal du service d'agents

Contexte. Le Lot 2 du programme Fusion (carte fusion-trois-apps #18) dote le service d'agents d'un journal d'activité cross-apps : chaque application y émet ses mutations significatives au contrat ACC-0.2 — enveloppe {id, type, occurred_at, actor, domain, source_app, object{ref, label, url}} signée HMAC-SHA256 du corps brut (en-tête X-Draco-Signature, clé propre à chaque application), que le service expose dans la vue Journal de sa console (agent-commun 0.11.0). TikTak (0.59.0) et Kanbante (0.28.0) émettent déjà ; ObsiGate est la troisième source, après Flowdeck.

Principe directeur : référence, jamais contenu. L'enveloppe ne contient que la référence de la note (fichier:<vault>/<chemin>), son nom (libellé) et un lien profond (/#file=<vault>:<chemin>) — jamais son contenu.

Garde-fou central : jamais le watcher

ObsiGate surveille les vaults par un watcher de système de fichiers (backend/watcher.py) qui réindexe chaque changement disque — le vault principal compte 222 393 fichiers. Brancher l'émission sur le watcher inonderait le journal (et confondrait les gestes d'Obsidian, de la synchro de fichiers ou d'un éditeur externe avec des opérations faites dans ObsiGate).

L'émission n'a donc lieu que pour les opérations explicites via l'API web, à deux points d'accroche, et nulle part ailleurs :

Source Point d'accroche Acteur
Interface web / clients REST backend/routers/files_write.py user
Serveur MCP d'ObsiGate (outils) backend/tools/service.py, mode mcp agent:mcp
Assistant intégré (outils, mode in_app) backend/tools/service.py user

Conséquences assumées :

  • une note créée/modifiée hors API (Obsidian, éditeur externe, copie de fichiers) n'apparaît pas au journal — c'est voulu ;
  • les écritures internes qui passent par la couche mutations sans passer par ces points (planificateur, crawler, fusion de doublons, restauration de sauvegarde) n'émettent pas non plus ;
  • le test test_watcher_et_indexation_sans_evenement verrouille le garde-fou : écriture disque directe + gestionnaires du watcher + update_single_file de l'indexeur → zéro émission.

Catalogue d'événements émis

Type Déclencheur
obsigate.note.created POST /api/file/{vault} ; outil create_file
obsigate.note.edited PUT /api/file/{vault}/save (+ variantes xlsx/csv : cellules, structure, style) ; outils edit_file, append_to_file
obsigate.note.moved PATCH /api/file/{vault} (renommage), POST /api/move/{vault} (fichiers seulement) ; outils rename_file, move_path (fichiers seulement) — la référence pointe sur le nouveau chemin
obsigate.note.deleted DELETE /api/file/{vault} ; outil delete_file

Hors catalogue (non émis, volontairement) : dossiers (création, renommage, déplacement, suppression — le catalogue est note.*), téléversement par lots (batch-upload), duplication, restauration de sauvegarde, replace_in_files (multi-fichiers).

Module backend/agent_journal.py

  • Activation : AGENT_EVENEMENTS_CLE (clé propre à ObsiGate, jamais en dur). Sans clé, le module est inactif : aucun appel, aucun bruit. URL surchargée par AGENT_EVENEMENTS_URL (défaut https://agent.dracodev.net/api/v1/evenements).
  • Discipline « au mieux » (identique à TikTak/Kanbante) : envoi dans un fil d'arrière-plan démon via httpx, délai maximal 2 s, exceptions avalées avec une ligne de journal local — une panne du service d'agents ne casse ni ne ralentit jamais le métier (prouvé par test_recepteur_en_panne_ne_casse_rien).
  • Domaine : ObsiGate ne modélise pas encore le domaine d'un vault (la question Q-A du programme — les domaines sont-ils des vaults distincts ? — est toujours ouverte). La correspondance peut être déclarée par AGENT_EVENEMENTS_DOMAINES (JSON {"NomDuVault": "famille"}) ; repli pro, qui est aussi le défaut du journal du service.

Activation en production (gestes de Bruno)

  1. Générer une clé propre à ObsiGate (openssl rand -hex 32).
  2. Service d'agents : ajouter l'entrée obsigate à AGENT_EVENEMENTS_CLES (JSON des clés par application), recréer le conteneur.
  3. ObsiGate : poser AGENT_EVENEMENTS_CLE=<même valeur> dans son environnement et redéployer (Docker).

Tant que la clé n'est pas posée des deux côtés, ObsiGate n'émet rien — c'est le comportement voulu.

Tests

tests/test_agent_journal.py (7 tests) : enveloppe et signature revérifiées comme le fait le service ; cycle de vie complet en REST (créer → éditer → renommer → déplacer → supprimer, acteur user) ; chemin outils/MCP (create/edit/rename/delete, acteur agent:mcp) ; inactivité sans clé ; garde-fou watcher/indexation ; panne du récepteur sans effet métier ; correspondance de domaines.