diff --git a/docs/superpowers/specs/2026-04-17-graylog-auto-resolve-design.md b/docs/superpowers/specs/2026-04-17-graylog-auto-resolve-design.md new file mode 100644 index 0000000..d2ca814 --- /dev/null +++ b/docs/superpowers/specs/2026-04-17-graylog-auto-resolve-design.md @@ -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 ``. +3. Remplacement des nombres longs/identifiants numériques par ``. +4. Remplacement des timestamps ISO par ``. +5. Remplacement des adresses IP par ``. +6. Remplacement des hash hexadécimaux longs par ``. +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` 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 = ` + - `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] - `). + - `artifact_data`: JSON détaillant métriques et exemples de logs. +2. mettre `graylog_subjects.status = 'queued'` et `active_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.