docs: add Graylog auto-resolve design spec
This commit is contained in:
parent
5dbaa3c717
commit
e4377f08c9
1 changed files with 402 additions and 0 deletions
402
docs/superpowers/specs/2026-04-17-graylog-auto-resolve-design.md
Normal file
402
docs/superpowers/specs/2026-04-17-graylog-auto-resolve-design.md
Normal file
|
|
@ -0,0 +1,402 @@
|
||||||
|
# Orchai - Design Graylog Auto-Resolve (V1)
|
||||||
|
|
||||||
|
Date: 2026-04-17
|
||||||
|
Statut: Validé en session de brainstorming
|
||||||
|
|
||||||
|
## 1. Objectif
|
||||||
|
|
||||||
|
Ajouter un module projet `Graylog` qui:
|
||||||
|
|
||||||
|
1. Interroge Graylog à intervalle régulier.
|
||||||
|
2. Détecte des sujets d'erreur récurrents à partir des logs récents.
|
||||||
|
3. Attribue un score déterministe à chaque sujet (`sévérité + fréquence + récence`).
|
||||||
|
4. Déclenche automatiquement le pipeline agent existant `analyst -> developer` pour les sujets au-dessus d'un seuil.
|
||||||
|
5. Crée une branche/worktree locale de correction via le flux actuel Orchai.
|
||||||
|
|
||||||
|
Le module doit rester isolé par projet avec des credentials Graylog propres à chaque projet.
|
||||||
|
|
||||||
|
## 2. Décisions validées
|
||||||
|
|
||||||
|
1. Exécution automatique au-dessus du seuil: oui.
|
||||||
|
2. Pipeline de traitement: réutilisation stricte du pipeline existant `analyst -> developer`.
|
||||||
|
3. Scoring: formule déterministe uniquement.
|
||||||
|
4. Définition d'un sujet: `source/service + message normalisé`.
|
||||||
|
5. Déduplication: stricte, une seule branche active par sujet.
|
||||||
|
6. Portée de la configuration Graylog: par projet.
|
||||||
|
7. Approche d'architecture: extension ciblée sans refonte complète du modèle Tuleap.
|
||||||
|
|
||||||
|
## 3. Architecture cible
|
||||||
|
|
||||||
|
## 3.1 Module projet
|
||||||
|
|
||||||
|
Nouveau module dans `project_modules`:
|
||||||
|
|
||||||
|
- `module_key`: `graylog_polling_auto_resolve`
|
||||||
|
- `name`: `Polling Graylog + auto-resolve`
|
||||||
|
- `description`: surveillance Graylog, scoring, déclenchement automatique du pipeline agent.
|
||||||
|
|
||||||
|
## 3.2 Composants backend
|
||||||
|
|
||||||
|
1. `graylog_client`
|
||||||
|
- Appelle l'API Graylog (requêtes de recherche, filtres, stream éventuel).
|
||||||
|
- Transforme les réponses en événements de logs exploitables.
|
||||||
|
2. `graylog_scoring`
|
||||||
|
- Normalise les messages.
|
||||||
|
- Regroupe par sujet.
|
||||||
|
- Calcule le score déterministe.
|
||||||
|
3. `graylog_poller`
|
||||||
|
- Boucle périodique (comme les services de fond existants).
|
||||||
|
- Charge la configuration de chaque projet.
|
||||||
|
- Applique dédup et seuil.
|
||||||
|
4. `graylog_to_queue_bridge`
|
||||||
|
- Insère un item de traitement dans la file existante pour réutiliser l'orchestrateur.
|
||||||
|
|
||||||
|
## 3.3 Réutilisation du pipeline actuel
|
||||||
|
|
||||||
|
Le traitement des sujets Graylog utilise:
|
||||||
|
|
||||||
|
1. `processed_tickets` comme file de traitement commune.
|
||||||
|
2. `services/orchestrator.rs` pour exécuter `analyst -> developer`.
|
||||||
|
3. `services/worktree_manager.rs` pour créer la branche/worktree locale.
|
||||||
|
|
||||||
|
Le poller Graylog détecte et pousse en file; il ne lance pas directement de correction.
|
||||||
|
|
||||||
|
## 4. Modèle de données
|
||||||
|
|
||||||
|
## 4.1 Nouvelles tables
|
||||||
|
|
||||||
|
1. `graylog_credentials` (unique par projet)
|
||||||
|
- `id TEXT PRIMARY KEY`
|
||||||
|
- `project_id TEXT NOT NULL UNIQUE REFERENCES projects(id) ON DELETE CASCADE`
|
||||||
|
- `base_url TEXT NOT NULL`
|
||||||
|
- `api_token_encrypted TEXT NOT NULL`
|
||||||
|
- `analyst_agent_id TEXT NOT NULL REFERENCES agents(id)`
|
||||||
|
- `developer_agent_id TEXT NOT NULL REFERENCES agents(id)`
|
||||||
|
- `stream_id TEXT` (optionnel)
|
||||||
|
- `query_filter TEXT NOT NULL DEFAULT ''`
|
||||||
|
- `polling_interval_minutes INTEGER NOT NULL DEFAULT 10`
|
||||||
|
- `lookback_minutes INTEGER NOT NULL DEFAULT 30`
|
||||||
|
- `score_threshold INTEGER NOT NULL DEFAULT 70`
|
||||||
|
- `created_at TEXT NOT NULL`
|
||||||
|
- `updated_at TEXT NOT NULL`
|
||||||
|
|
||||||
|
2. `graylog_subjects` (état courant d'un sujet)
|
||||||
|
- `id TEXT PRIMARY KEY`
|
||||||
|
- `project_id TEXT NOT NULL REFERENCES projects(id) ON DELETE CASCADE`
|
||||||
|
- `subject_key TEXT NOT NULL`
|
||||||
|
- `source TEXT NOT NULL`
|
||||||
|
- `normalized_message TEXT NOT NULL`
|
||||||
|
- `first_seen_at TEXT NOT NULL`
|
||||||
|
- `last_seen_at TEXT NOT NULL`
|
||||||
|
- `last_score INTEGER NOT NULL DEFAULT 0`
|
||||||
|
- `active_ticket_id TEXT REFERENCES processed_tickets(id)`
|
||||||
|
- `status TEXT NOT NULL DEFAULT 'idle'` (`idle|queued|processing|done|error|cancelled`)
|
||||||
|
- `created_at TEXT NOT NULL`
|
||||||
|
- `updated_at TEXT NOT NULL`
|
||||||
|
|
||||||
|
3. `graylog_detections` (historique des cycles)
|
||||||
|
- `id TEXT PRIMARY KEY`
|
||||||
|
- `subject_id TEXT NOT NULL REFERENCES graylog_subjects(id) ON DELETE CASCADE`
|
||||||
|
- `window_start TEXT NOT NULL`
|
||||||
|
- `window_end TEXT NOT NULL`
|
||||||
|
- `critical_count INTEGER NOT NULL DEFAULT 0`
|
||||||
|
- `error_count INTEGER NOT NULL DEFAULT 0`
|
||||||
|
- `warning_count INTEGER NOT NULL DEFAULT 0`
|
||||||
|
- `total_count INTEGER NOT NULL DEFAULT 0`
|
||||||
|
- `last_seen_at TEXT NOT NULL`
|
||||||
|
- `score INTEGER NOT NULL`
|
||||||
|
- `triggered INTEGER NOT NULL DEFAULT 0`
|
||||||
|
- `triggered_ticket_id TEXT REFERENCES processed_tickets(id)`
|
||||||
|
- `created_at TEXT NOT NULL`
|
||||||
|
|
||||||
|
## 4.2 Évolution de `processed_tickets`
|
||||||
|
|
||||||
|
Ajouts:
|
||||||
|
|
||||||
|
1. `project_id TEXT REFERENCES projects(id)` (renseigné pour tous les tickets)
|
||||||
|
2. `source TEXT NOT NULL DEFAULT 'tuleap'`
|
||||||
|
3. `source_ref TEXT` (référence externe, ici `graylog_subjects.id`)
|
||||||
|
|
||||||
|
Évolutions:
|
||||||
|
|
||||||
|
1. `tracker_id` devient nullable pour supporter les sources non-Tuleap.
|
||||||
|
2. Le code de lecture par projet ne dépend plus d'un `JOIN` obligatoire vers `watched_trackers`.
|
||||||
|
3. Les tickets Tuleap continuent de renseigner `tracker_id`.
|
||||||
|
4. Les tickets Graylog renseignent `project_id` et laissent `tracker_id` nul.
|
||||||
|
|
||||||
|
Rôle:
|
||||||
|
|
||||||
|
- Distinguer les items Tuleap et Graylog.
|
||||||
|
- Réutiliser l'orchestrateur sans dupliquer la mécanique de queue/worktree.
|
||||||
|
|
||||||
|
## 4.3 Index recommandés
|
||||||
|
|
||||||
|
1. `UNIQUE(project_id, subject_key)` sur `graylog_subjects`.
|
||||||
|
2. `INDEX(subject_id, created_at DESC)` sur `graylog_detections`.
|
||||||
|
3. `INDEX(source, source_ref)` sur `processed_tickets`.
|
||||||
|
4. `INDEX(project_id, detected_at DESC)` sur `processed_tickets`.
|
||||||
|
5. Index unique Tuleap conservé en version partielle:
|
||||||
|
`UNIQUE(tracker_id, artifact_id) WHERE tracker_id IS NOT NULL`.
|
||||||
|
|
||||||
|
## 4.4 Migration des données existantes
|
||||||
|
|
||||||
|
Ordre de migration recommandé:
|
||||||
|
|
||||||
|
1. Ajouter `project_id`, `source`, `source_ref` sur `processed_tickets`.
|
||||||
|
2. Backfill `project_id` pour les lignes existantes via `JOIN watched_trackers`.
|
||||||
|
3. Rendre `tracker_id` nullable.
|
||||||
|
4. Remplacer l'index unique historique `(tracker_id, artifact_id)` par l'index partiel Tuleap.
|
||||||
|
5. Ajouter les nouvelles tables Graylog.
|
||||||
|
|
||||||
|
## 5. Définition d'un sujet et normalisation
|
||||||
|
|
||||||
|
## 5.1 Clé de regroupement
|
||||||
|
|
||||||
|
`subject_key = source + '|' + normalized_message`
|
||||||
|
|
||||||
|
`source` correspond au champ service applicatif si présent, sinon au champ `source` Graylog.
|
||||||
|
|
||||||
|
## 5.2 Normalisation V1
|
||||||
|
|
||||||
|
Transformation déterministe:
|
||||||
|
|
||||||
|
1. Passage en minuscules.
|
||||||
|
2. Remplacement des UUID par `<uuid>`.
|
||||||
|
3. Remplacement des nombres longs/identifiants numériques par `<num>`.
|
||||||
|
4. Remplacement des timestamps ISO par `<ts>`.
|
||||||
|
5. Remplacement des adresses IP par `<ip>`.
|
||||||
|
6. Remplacement des hash hexadécimaux longs par `<hash>`.
|
||||||
|
7. Réduction des espaces multiples.
|
||||||
|
|
||||||
|
Objectif: regrouper des occurrences logiquement identiques malgré des valeurs dynamiques.
|
||||||
|
|
||||||
|
## 6. Scoring déterministe
|
||||||
|
|
||||||
|
Score total entre 0 et 100:
|
||||||
|
|
||||||
|
`score_total = severity_score + frequency_score + recency_score`
|
||||||
|
|
||||||
|
## 6.1 Sévérité (0..50)
|
||||||
|
|
||||||
|
1. Si au moins un `critical`: `50`
|
||||||
|
2. Sinon si au moins un `error`: `35`
|
||||||
|
3. Sinon si au moins un `warning`: `20`
|
||||||
|
4. Sinon: `0`
|
||||||
|
|
||||||
|
## 6.2 Fréquence (0..35)
|
||||||
|
|
||||||
|
Barème sur `total_count` dans la fenêtre du cycle:
|
||||||
|
|
||||||
|
1. `1` -> `5`
|
||||||
|
2. `2-3` -> `12`
|
||||||
|
3. `4-7` -> `22`
|
||||||
|
4. `8-15` -> `30`
|
||||||
|
5. `>=16` -> `35`
|
||||||
|
|
||||||
|
## 6.3 Récence (0..15)
|
||||||
|
|
||||||
|
Barème sur l'âge du dernier événement du sujet:
|
||||||
|
|
||||||
|
1. `<=2 min` -> `15`
|
||||||
|
2. `<=10 min` -> `12`
|
||||||
|
3. `<=30 min` -> `8`
|
||||||
|
4. `<=120 min` -> `4`
|
||||||
|
5. `>120 min` -> `0`
|
||||||
|
|
||||||
|
## 6.4 Seuil
|
||||||
|
|
||||||
|
`score_threshold` est porté par projet dans `graylog_credentials`.
|
||||||
|
|
||||||
|
Valeur par défaut V1: `70`.
|
||||||
|
|
||||||
|
## 7. Flux d'exécution
|
||||||
|
|
||||||
|
## 7.1 Cycle planifié
|
||||||
|
|
||||||
|
1. Le scheduler Graylog se déclenche toutes les 60 secondes.
|
||||||
|
2. Pour chaque projet:
|
||||||
|
- vérifier module `graylog_polling_auto_resolve` activé;
|
||||||
|
- charger credentials + config;
|
||||||
|
- vérifier si `polling_interval_minutes` est atteint;
|
||||||
|
- interroger Graylog sur la fenêtre `lookback_minutes`.
|
||||||
|
3. Regrouper les logs en sujets.
|
||||||
|
4. Calculer score par sujet.
|
||||||
|
5. Persister détections et état des sujets.
|
||||||
|
6. Décider du déclenchement.
|
||||||
|
|
||||||
|
## 7.2 Règle de déclenchement
|
||||||
|
|
||||||
|
1. Si `score < threshold`:
|
||||||
|
- `triggered = 0` dans `graylog_detections`.
|
||||||
|
- aucun item queue créé.
|
||||||
|
2. Si `score >= threshold`:
|
||||||
|
- si sujet sans traitement actif: créer un item `processed_tickets` de source `graylog`.
|
||||||
|
- sinon: ne rien créer (dédup stricte), garder la trace en détection.
|
||||||
|
|
||||||
|
## 7.3 Dédup stricte
|
||||||
|
|
||||||
|
Un sujet est considéré actif si:
|
||||||
|
|
||||||
|
1. `graylog_subjects.active_ticket_id` non nul, et
|
||||||
|
2. le ticket lié est dans `Pending|Analyzing|Developing`.
|
||||||
|
|
||||||
|
Conséquence:
|
||||||
|
|
||||||
|
- une seule branche active par sujet.
|
||||||
|
- pas de duplication de branche tant que le ticket actif n'est pas terminé.
|
||||||
|
|
||||||
|
## 7.4 Nommage de branche
|
||||||
|
|
||||||
|
Format recommandé:
|
||||||
|
|
||||||
|
`orchai/graylog/<subject_hash>`
|
||||||
|
|
||||||
|
`subject_hash` est dérivé de `subject_key` pour conserver un nom stable et compact.
|
||||||
|
|
||||||
|
## 8. Intégration orchestrateur/worktree
|
||||||
|
|
||||||
|
## 8.1 Représentation queue Graylog
|
||||||
|
|
||||||
|
Lors du trigger:
|
||||||
|
|
||||||
|
1. créer un `processed_tickets` avec:
|
||||||
|
- `project_id = <project_id>`
|
||||||
|
- `tracker_id = NULL`
|
||||||
|
- `source = 'graylog'`
|
||||||
|
- `source_ref = graylog_subjects.id`
|
||||||
|
- `artifact_id`: identifiant synthétique négatif (dérivé de hash de sujet, avec fallback anti-collision) pour compatibilité UI.
|
||||||
|
- `artifact_title`: résumé court (`[Graylog] <source> - <message normalisé>`).
|
||||||
|
- `artifact_data`: JSON détaillant métriques et exemples de logs.
|
||||||
|
2. mettre `graylog_subjects.status = 'queued'` et `active_ticket_id = <ticket_id>`.
|
||||||
|
|
||||||
|
## 8.2 Transition de statut sujet
|
||||||
|
|
||||||
|
Synchroniser `graylog_subjects.status` avec le ticket lié:
|
||||||
|
|
||||||
|
1. `Pending` -> `queued`
|
||||||
|
2. `Analyzing` -> `processing`
|
||||||
|
3. `Developing` -> `processing`
|
||||||
|
4. `Done` -> `done` puis `idle` au cycle suivant si pas de nouveau dépassement
|
||||||
|
5. `Error` -> `error`
|
||||||
|
6. `Cancelled` -> `cancelled`
|
||||||
|
|
||||||
|
## 8.3 Sélection des agents Graylog
|
||||||
|
|
||||||
|
Pour un ticket `source='graylog'`, l'orchestrateur charge les agents depuis `graylog_credentials` du projet:
|
||||||
|
|
||||||
|
1. `analyst_agent_id` pour l'étape d'analyse.
|
||||||
|
2. `developer_agent_id` pour l'étape de correction.
|
||||||
|
|
||||||
|
Comportement en cas de configuration invalide:
|
||||||
|
|
||||||
|
1. le ticket passe en erreur explicite;
|
||||||
|
2. l'événement d'erreur est émis;
|
||||||
|
3. aucun worktree n'est créé.
|
||||||
|
|
||||||
|
## 9. Contrats Tauri (backend)
|
||||||
|
|
||||||
|
## 9.1 Credentials/config
|
||||||
|
|
||||||
|
1. `set_graylog_credentials(project_id, base_url, api_token, analyst_agent_id, developer_agent_id, stream_id, query_filter, polling_interval_minutes, lookback_minutes, score_threshold)`
|
||||||
|
2. `get_graylog_credentials(project_id)`
|
||||||
|
3. `test_graylog_connection(project_id)`
|
||||||
|
4. `delete_graylog_credentials(project_id)`
|
||||||
|
5. Les commandes de configuration incluent `analyst_agent_id` et `developer_agent_id`.
|
||||||
|
|
||||||
|
## 9.2 Pilotage et visibilité
|
||||||
|
|
||||||
|
1. `manual_graylog_poll(project_id)`
|
||||||
|
2. `list_graylog_subjects(project_id)`
|
||||||
|
3. `list_graylog_detections(project_id, subject_id?)`
|
||||||
|
|
||||||
|
## 10. Événements frontend
|
||||||
|
|
||||||
|
Événements Tauri ajoutés:
|
||||||
|
|
||||||
|
1. `graylog-polling-started`
|
||||||
|
2. `graylog-subject-triggered`
|
||||||
|
3. `graylog-polling-finished`
|
||||||
|
4. `graylog-polling-error`
|
||||||
|
|
||||||
|
Ces événements sont complémentaires des événements existants du pipeline ticket.
|
||||||
|
|
||||||
|
## 11. UI
|
||||||
|
|
||||||
|
## 11.1 Modules projet
|
||||||
|
|
||||||
|
Dans `ProjectModules`, afficher le module Graylog avec activation/désactivation.
|
||||||
|
|
||||||
|
## 11.2 Vue Graylog projet
|
||||||
|
|
||||||
|
Nouvelle page projet dédiée:
|
||||||
|
|
||||||
|
1. Formulaire de configuration (`URL`, `token`, `stream`, `query`, intervalle, lookback, seuil).
|
||||||
|
2. Bouton `Tester la connexion`.
|
||||||
|
3. Liste des sujets:
|
||||||
|
- source
|
||||||
|
- message normalisé
|
||||||
|
- score actuel
|
||||||
|
- dernière occurrence
|
||||||
|
- statut
|
||||||
|
- ticket/branche liés s'ils existent
|
||||||
|
4. Historique des détections par sujet.
|
||||||
|
|
||||||
|
## 11.3 Dashboard
|
||||||
|
|
||||||
|
Le dashboard projet affiche les événements Graylog récents au même titre que les événements de polling et d'orchestration actuels.
|
||||||
|
|
||||||
|
## 12. Gestion d'erreurs et robustesse
|
||||||
|
|
||||||
|
1. Erreur API Graylog:
|
||||||
|
- journaliser côté backend,
|
||||||
|
- émettre `graylog-polling-error`,
|
||||||
|
- reprendre au cycle suivant sans bloquer le reste.
|
||||||
|
2. Credentials absents/invalides:
|
||||||
|
- projet ignoré pour le cycle,
|
||||||
|
- statut explicite côté UI.
|
||||||
|
3. Aucune erreur Graylog ne doit arrêter:
|
||||||
|
- le poller Tuleap,
|
||||||
|
- l'orchestrateur,
|
||||||
|
- le task runner.
|
||||||
|
|
||||||
|
## 13. Tests
|
||||||
|
|
||||||
|
## 13.1 Unit tests
|
||||||
|
|
||||||
|
1. Normalisation de message.
|
||||||
|
2. Calcul de score (cas limites inclus).
|
||||||
|
3. Dédup stricte (pas de second ticket si sujet actif).
|
||||||
|
|
||||||
|
## 13.2 Intégration DB
|
||||||
|
|
||||||
|
1. Migration vers le nouveau schéma.
|
||||||
|
2. Création d'un ticket `source='graylog'`.
|
||||||
|
3. Cohérence de `active_ticket_id` et des transitions de statut sujet.
|
||||||
|
|
||||||
|
## 13.3 Services
|
||||||
|
|
||||||
|
1. Poll sous seuil -> pas de trigger.
|
||||||
|
2. Poll au-dessus seuil -> trigger unique.
|
||||||
|
3. Poll répété au-dessus seuil avec sujet actif -> pas de nouveau trigger.
|
||||||
|
|
||||||
|
## 13.4 UI
|
||||||
|
|
||||||
|
1. Toggle module Graylog.
|
||||||
|
2. Sauvegarde config + test connexion.
|
||||||
|
3. Affichage des sujets et statut lié au ticket.
|
||||||
|
|
||||||
|
## 14. Critères d'acceptation
|
||||||
|
|
||||||
|
1. Un sujet Graylog au-dessus du seuil crée automatiquement un traitement complet `analyst -> developer`.
|
||||||
|
2. Les sujets sont regroupés par `source + message normalisé`.
|
||||||
|
3. Tant qu'un sujet est actif, aucune branche supplémentaire n'est créée pour ce sujet.
|
||||||
|
4. Les décisions (trigger/dédup) sont auditables via l'historique des détections.
|
||||||
|
5. Désactiver le module Graylog coupe les nouveaux déclenchements sans perdre l'historique.
|
||||||
|
|
||||||
|
## 15. Plan de rollout
|
||||||
|
|
||||||
|
1. Livrer backend (migrations, services, commandes).
|
||||||
|
2. Livrer UI (module + écran Graylog + événements).
|
||||||
|
3. Activer sur un projet pilote.
|
||||||
|
4. Ajuster seuil et barèmes de scoring si nécessaire.
|
||||||
|
5. Généraliser projet par projet.
|
||||||
Loading…
Reference in a new issue