99 lines
5.0 KiB
Markdown
99 lines
5.0 KiB
Markdown
# #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.
|