From 79c1f790ebc9b9f18bdc10ae3122fcc674af01b6 Mon Sep 17 00:00:00 2001 From: thibaud-leclere Date: Mon, 13 Apr 2026 09:04:30 +0200 Subject: [PATCH] Add Orchai design spec Desktop app (Tauri + React) that monitors Tuleap trackers and dispatches AI agents to analyze and fix tickets automatically. Co-Authored-By: Claude Opus 4.6 (1M context) --- .../specs/2026-04-13-orchai-design.md | 460 ++++++++++++++++++ 1 file changed, 460 insertions(+) create mode 100644 docs/superpowers/specs/2026-04-13-orchai-design.md diff --git a/docs/superpowers/specs/2026-04-13-orchai-design.md b/docs/superpowers/specs/2026-04-13-orchai-design.md new file mode 100644 index 0000000..9533845 --- /dev/null +++ b/docs/superpowers/specs/2026-04-13-orchai-design.md @@ -0,0 +1,460 @@ +# Orchai -- Design Spec + +App desktop d'equipe qui surveille des trackers Tuleap et lance des agents IA pour analyser et corriger les tickets automatiquement. + +## Decisions cles + +- **Type** : App desktop (Tauri 2 + React) +- **Stack** : Rust backend (Tauri), React + TypeScript frontend, SQLite embarque +- **Utilisateurs** : equipe de devs, pas de distribution large +- **Zero setup** : tout embarque, pas de serveur externe + +## Architecture globale + +``` ++--------------------------------------------------+ +| React Frontend | +| +----------+ +----------+ +-------------------+ | +| | Projects | | Tickets | | Agent Results | | +| | Manager | | Monitor | | (report + diff) | | +| +----------+ +----------+ +-------------------+ | ++--------------------------------------------------+ +| Tauri IPC (commands) | ++--------------------------------------------------+ +| Rust Backend | +| +-----------+ +-----------+ +----------------+ | +| | Project | | Tuleap | | Agent | | +| | Manager | | Poller | | Orchestrator | | +| +-----------+ +-----------+ +----------------+ | +| +-----------+ +-----------+ | +| | Worktree | | Notifier | | +| | Manager | | | | +| +-----------+ +-----------+ | +| +--------------------------------------------+ | +| | SQLite (rusqlite) | | +| +--------------------------------------------+ | ++--------------------------------------------------+ +``` + +5 modules backend, 1 SPA React frontend. Communication via Tauri IPC commands et events. + +--- + +## Module 1 : Project Manager + +Gere les projets et leurs trackers surveilles. + +### Modele de donnees + +``` +Project { + id: UUID + name: String + path: String -- chemin local du repo + cloned_from: Option -- URL d'origine si clone par l'app + base_branch: String -- ex: "stable", "main" + created_at: DateTime +} + +WatchedTracker { + id: UUID + project_id: UUID -- FK -> Project + tracker_id: i32 -- ID du tracker Tuleap + tracker_label: String -- nom affiche + polling_interval: i32 -- en minutes, defaut 10 + agent_config: AgentConfig + filters: Vec +} + +AgentConfig { + analyst_command: String -- ex: "claude", "codex" + analyst_args: Vec + developer_command: String + developer_args: Vec +} + +FilterGroup { -- groupes combines en ET + conditions: Vec -- conditions dans un groupe combinees en OU +} + +Filter { + field: String -- ex: "status", "assigned_to", "priority" + operator: FilterOp -- Equals, NotEquals, In, NotIn + value: Vec -- ex: ["Nouveau", "A traiter"] +} +``` + +Exemple : `(Statut Nouveau OU Statut A traiter) ET Assigne a Team Maintenance` +- FilterGroup 1 : `[status IN ("Nouveau", "A traiter")]` +- FilterGroup 2 : `[assigned_to IN ("Team Maintenance")]` + +### Commandes Tauri + +- `create_project(name, path_or_url, base_branch)` -- cree le projet, clone si URL +- `update_project(id, ...)` / `delete_project(id)` +- `list_projects()` / `get_project(id)` +- `add_tracker(project_id, tracker_id, filters, agent_config)` +- `update_tracker(...)` / `remove_tracker(id)` + +--- + +## Module 2 : Tuleap Poller + +Interroge l'API Tuleap a intervalle regulier, applique les filtres, detecte les nouveaux tickets. + +### Fonctionnement + +1. Un scheduler tourne en arriere-plan (`tokio::interval` par tracker surveille) +2. A chaque tick, appel API Tuleap `GET /api/trackers/{id}/artifacts` avec les credentials +3. Les resultats sont filtres localement selon les `FilterGroup` configures (l'API Tuleap ne supporte pas nativement les combinaisons ET/OU complexes) +4. Chaque ticket est compare a la table `processed_tickets` -- si absent, c'est un nouveau ticket +5. Les nouveaux tickets sont inseres en base avec le statut `Pending` et ajoutes a la file d'attente + +### Modele de donnees + +``` +ProcessedTicket { + id: UUID + tracker_id: UUID -- FK -> WatchedTracker + artifact_id: i32 -- ID du ticket Tuleap + artifact_title: String + artifact_data: String -- JSON brut du ticket + status: TicketStatus -- Pending, Analyzing, Developing, Done, Error + analyst_report: Option + developer_report: Option + worktree_path: Option + branch_name: Option + detected_at: DateTime + processed_at: Option +} + +TuleapCredentials { + id: UUID + tuleap_url: String + username: String + password: String -- chiffre au repos +} +``` + +### Points importants + +- Credentials chiffres en SQLite (cle derivee via `keyring` ou secret local) +- Le poller emet un evenement Tauri `new-tickets-detected` vers le frontend +- Si l'API Tuleap est injoignable, log de l'erreur et retry au prochain tick + +### Commandes Tauri + +- `set_tuleap_credentials(url, username, password)` +- `test_tuleap_connection()` +- `get_tracker_fields(tracker_id)` -- recupere les champs/valeurs pour les filtres UI +- `list_processed_tickets(project_id, filters)` +- `manual_poll(tracker_id)` + +--- + +## Module 3 : Agent Orchestrator + +Traite les tickets de la file d'attente sequentiellement via un pipeline a 2 agents. + +### Pipeline + +1. L'orchestrateur consomme la file (FIFO), un ticket a la fois +2. **Etape 1 -- Analyste** : lance la commande CLI configuree avec un prompt structure contenant les infos du ticket et du contexte code +3. Le rapport markdown de l'analyste est stocke en base +4. **Etape 2 -- Developpeur** : lance la commande CLI avec le rapport de l'analyste + acces au worktree +5. Le rapport + diff sont collectes +6. Le ticket passe en `Done`, le Notifier est appele + +### Invocation CLI + +```rust +Command::new("claude") + .arg("--print") + .arg("--prompt") + .arg(analyst_prompt) + .current_dir(&project_path) + .stdout(Stdio::piped()) + .stderr(Stdio::piped()) + .spawn() +``` + +### Prompts + +**Analyste :** +``` +Tu es un analyste technique. Voici un ticket Tuleap a analyser. + +## Ticket +- ID: {artifact_id} +- Titre: {title} +- Description: {description} +- Priorite: {priority} +- Champs additionnels: {fields} + +## Contexte +- Projet: {project_name} +- Repo: {project_path} + +## Ta mission +1. Analyse le ticket et identifie les fichiers/fonctions concernes +2. Explique techniquement le probleme +3. Evalue si une correction de code est necessaire +4. Produis un rapport structure en markdown +``` + +**Developpeur** : recoit en plus le rapport de l'analyste et travaille dans le worktree. + +### Gestion des erreurs + +- Timeout configurable par agent (defaut : 10 minutes) +- Si un agent plante ou timeout, le ticket passe en `Error` avec le message d'erreur +- L'utilisateur peut relancer manuellement un ticket en erreur + +### Evenements Tauri + +- `ticket-processing-started { ticket_id, step: "analyst"|"developer" }` +- `ticket-processing-progress { ticket_id, output_chunk }` -- streaming stdout +- `ticket-processing-done { ticket_id }` +- `ticket-processing-error { ticket_id, error }` + +### Commandes Tauri + +- `get_queue_status()` -- tickets en attente + ticket en cours +- `retry_ticket(ticket_id)` +- `cancel_ticket(ticket_id)` +- `get_ticket_result(ticket_id)` -- rapport + diff + +--- + +## Module 4 : Worktree Manager + +Cree et gere les git worktrees temporaires pour les agents developpeurs. + +### Cycle de vie + +``` +Ticket detecte -> Analyse -> Fix necessaire + -> Worktree cree (branche locale orchai/{artifact_id}) basee sur {base_branch} + -> Agent developpeur travaille dedans + -> Resultat pret, worktree en attente de review + -> User review le diff dans l'app + -> User choisit "Appliquer le fix dans ma branche" -> selectionne sa branche locale + -> L'app cherry-pick les commits du worktree dans la branche de l'user + -> App propose suppression du worktree + -> User confirme -> worktree + branche orchai/ supprimes +``` + +**Le code dans le worktree n'est jamais push.** La branche `orchai/{artifact_id}` est purement locale. C'est l'utilisateur qui push sa propre branche apres avoir applique le fix. + +### Stockage + +``` +/chemin/du/projet/ -- repo principal +/chemin/du/projet/.orchai/ -- dossier gere par l'app + worktrees/ + orchai-1234/ -- worktree pour le ticket #1234 + orchai-5678/ +``` + +### Operations git + +```bash +# Creation +git worktree add .orchai/worktrees/orchai-{artifact_id} -b orchai/{artifact_id} {base_branch} + +# Application du fix dans la branche de l'user +git checkout {user_branch} +git cherry-pick {commits_from_orchai_branch} + +# Nettoyage +git worktree remove .orchai/worktrees/orchai-{artifact_id} +git branch -d orchai/{artifact_id} +``` + +### Modele de donnees + +``` +Worktree { + id: UUID + ticket_id: UUID -- FK -> ProcessedTicket + path: String -- chemin absolu du worktree + branch_name: String -- orchai/{artifact_id} + status: WorktreeStatus -- Active, Merged, Deleted + created_at: DateTime + merged_at: Option + merged_into: Option -- branche cible +} +``` + +### Commandes Tauri + +- `create_worktree(ticket_id)` -- appele automatiquement par l'orchestrateur +- `list_worktrees(project_id)` +- `apply_fix_to_branch(worktree_id, target_branch)` -- cherry-pick dans la branche de l'user +- `delete_worktree(worktree_id)` +- `get_worktree_diff(worktree_id)` -- diff par rapport a la branche de base +- `list_local_branches(project_id)` -- pour le selecteur de branche dans l'UI + +### Conflits de merge + +- Si le cherry-pick echoue (conflit), l'app notifie l'utilisateur avec les fichiers en conflit +- Le cherry-pick est annule (`git cherry-pick --abort`), le worktree reste en place +- L'utilisateur peut resoudre manuellement ou demander a un agent de tenter la resolution + +--- + +## Module 5 : Notifier + +Informe l'utilisateur quand un ticket a ete traite. + +### Notification systeme (OS) + +- `notify-rust` (Linux) / API Tauri native +- Declenchee quand un ticket passe en `Done` ou `Error` +- Contenu : titre du ticket + resume court +- Clic sur la notification -> ouvre l'app sur le detail du ticket + +### Notification in-app + +- Centre de notifications (icone avec badge compteur) +- Liste chronologique : nouveau ticket detecte, analyse terminee, fix propose, erreur +- Chaque notification cliquable -> navigue vers le ticket +- Marquage lu/non-lu + +### Modele de donnees + +``` +Notification { + id: UUID + project_id: UUID + ticket_id: Option -- FK -> ProcessedTicket + type: NotificationType -- NewTicket, AnalysisDone, FixReady, Error + title: String + message: String + read: bool + created_at: DateTime +} +``` + +### Commandes Tauri + +- `list_notifications(project_id, unread_only)` +- `mark_notification_read(id)` / `mark_all_read(project_id)` + +### Evenement Tauri + +- `new-notification { notification }` -- le frontend met a jour le badge en temps reel + +--- + +## Frontend React + +### Stack + +- React + TypeScript +- Tailwind CSS +- `@tauri-apps/api` pour les IPC commands et events +- `react-diff-viewer` pour l'affichage des diffs +- `react-markdown` pour le rendu des rapports + +### Navigation + +Sidebar avec la liste des projets + header avec le centre de notifications. + +### Vues + +**1. Dashboard projet** +- Resume : nom, chemin, branche de base, nombre de trackers +- Liste des trackers surveilles avec statut (actif, derniere verification, tickets traites) +- File d'attente en cours (ticket en traitement + en attente) +- Acces rapide aux derniers resultats + +**2. Configuration tracker** +- Selection du tracker Tuleap (liste via API) +- Constructeur de filtres visuel : ajout de conditions, groupement ET/OU +- Configuration des agents (analyste / developpeur) : commande CLI + arguments +- Intervalle de polling + +**3. Liste des tickets traites** +- Tableau : ID ticket, titre, statut, date +- Filtrage par statut +- Clic -> detail + +**4. Detail ticket** +- Infos du ticket Tuleap (titre, description, priorite, assignation) +- Rapport analyste (markdown rendu) +- Rapport developpeur (markdown rendu) +- Vue diff du fix (style GitHub, cote a cote) +- Actions : "Appliquer le fix dans ma branche" (selecteur de branche), "Relancer", "Supprimer le worktree" + +**5. Parametres** +- Credentials Tuleap (URL, login, password) + bouton "Tester la connexion" +- Gestion des projets (ajouter, modifier, supprimer) + +--- + +## Schema SQLite + +```sql +CREATE TABLE projects ( + id TEXT PRIMARY KEY, + name TEXT NOT NULL, + path TEXT NOT NULL, + cloned_from TEXT, + base_branch TEXT NOT NULL DEFAULT 'main', + created_at TEXT NOT NULL DEFAULT (datetime('now')) +); + +CREATE TABLE tuleap_credentials ( + id TEXT PRIMARY KEY, + tuleap_url TEXT NOT NULL, + username TEXT NOT NULL, + password_encrypted TEXT NOT NULL +); + +CREATE TABLE watched_trackers ( + id TEXT PRIMARY KEY, + project_id TEXT NOT NULL REFERENCES projects(id) ON DELETE CASCADE, + tracker_id INTEGER NOT NULL, + tracker_label TEXT NOT NULL, + polling_interval INTEGER NOT NULL DEFAULT 10, + agent_config_json TEXT NOT NULL, + filters_json TEXT NOT NULL, + created_at TEXT NOT NULL DEFAULT (datetime('now')) +); + +CREATE TABLE processed_tickets ( + id TEXT PRIMARY KEY, + tracker_id TEXT NOT NULL REFERENCES watched_trackers(id) ON DELETE CASCADE, + artifact_id INTEGER NOT NULL, + artifact_title TEXT NOT NULL, + artifact_data TEXT NOT NULL, + status TEXT NOT NULL DEFAULT 'Pending', + analyst_report TEXT, + developer_report TEXT, + detected_at TEXT NOT NULL DEFAULT (datetime('now')), + processed_at TEXT +); + +CREATE TABLE worktrees ( + id TEXT PRIMARY KEY, + ticket_id TEXT NOT NULL REFERENCES processed_tickets(id), + path TEXT NOT NULL, + branch_name TEXT NOT NULL, + status TEXT NOT NULL DEFAULT 'Active', + created_at TEXT NOT NULL DEFAULT (datetime('now')), + merged_at TEXT, + merged_into TEXT +); + +CREATE TABLE notifications ( + id TEXT PRIMARY KEY, + project_id TEXT NOT NULL REFERENCES projects(id) ON DELETE CASCADE, + ticket_id TEXT REFERENCES processed_tickets(id), + type TEXT NOT NULL, + title TEXT NOT NULL, + message TEXT NOT NULL, + read INTEGER NOT NULL DEFAULT 0, + created_at TEXT NOT NULL DEFAULT (datetime('now')) +); +```