# 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.
