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) <noreply@anthropic.com>
This commit is contained in:
commit
79c1f790eb
1 changed files with 460 additions and 0 deletions
460
docs/superpowers/specs/2026-04-13-orchai-design.md
Normal file
460
docs/superpowers/specs/2026-04-13-orchai-design.md
Normal file
|
|
@ -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<String> -- 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<FilterGroup>
|
||||
}
|
||||
|
||||
AgentConfig {
|
||||
analyst_command: String -- ex: "claude", "codex"
|
||||
analyst_args: Vec<String>
|
||||
developer_command: String
|
||||
developer_args: Vec<String>
|
||||
}
|
||||
|
||||
FilterGroup { -- groupes combines en ET
|
||||
conditions: Vec<Filter> -- conditions dans un groupe combinees en OU
|
||||
}
|
||||
|
||||
Filter {
|
||||
field: String -- ex: "status", "assigned_to", "priority"
|
||||
operator: FilterOp -- Equals, NotEquals, In, NotIn
|
||||
value: Vec<String> -- 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<String>
|
||||
developer_report: Option<String>
|
||||
worktree_path: Option<String>
|
||||
branch_name: Option<String>
|
||||
detected_at: DateTime
|
||||
processed_at: Option<DateTime>
|
||||
}
|
||||
|
||||
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<DateTime>
|
||||
merged_into: Option<String> -- 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<UUID> -- 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'))
|
||||
);
|
||||
```
|
||||
Loading…
Reference in a new issue