247 lines
16 KiB
Markdown
247 lines
16 KiB
Markdown
---
|
||
issueRef: "#305"
|
||
version: 7
|
||
updatedBy: {"kind":"agent","agent_id":"a6c6ea12-bfc6-4bdc-8031-324102dfa34d"}
|
||
updatedAt: 1789115934356
|
||
---
|
||
# Carnet #305 - Tâches planifiées projet avec outil MCP et menu utilisateur
|
||
|
||
## Demande
|
||
- Date: 2026-09-10.
|
||
- Ajouter des tâches planifiées liées au projet, gérées par IdeA plutôt que par le cron Linux.
|
||
- Les agents doivent disposer d'un tool MCP pour créer des tâches récurrentes/projet.
|
||
- L'utilisateur doit disposer d'un menu par projet pour créer, supprimer et modifier ces tasks.
|
||
- Motivation: un projet a grossi build après build; l'orchestrateur a créé un cron Linux pour nettoyer régulièrement le dossier. Objectif: permettre cette automatisation dans IdeA, scoped au projet et visible/gérable.
|
||
- Après livraison et QA: rebuild AppImage demandé.
|
||
|
||
## Contraintes initiales
|
||
- Les tâches sont liées au projet, pas au système global Linux.
|
||
- L'utilisateur doit pouvoir voir/modifier/supprimer ce que les agents créent.
|
||
- Le tool MCP doit éviter que les agents contournent IdeA avec `crontab` système.
|
||
- Sécurité: commandes planifiées potentiellement destructrices; besoin de permissions/sandbox claires.
|
||
- Réutiliser le modèle background tasks pour les exécutions si pertinent, mais distinguer tâche planifiée et exécution ponctuelle.
|
||
|
||
## Cadrage UX
|
||
Surface:
|
||
- Ajouter l'entrée `Tâches planifiées` dans le menu du projet.
|
||
- Surface unique pour les tâches créées par l'utilisateur et celles demandées par un agent.
|
||
- Ligne: état, nom, fréquence, prochaine exécution, résultat dernière exécution, origine (`Créée par vous` ou `Créée par {agent}`), menu `Modifier`, `Exécuter maintenant`, `Suspendre/Reprendre`, `Supprimer`.
|
||
- États visibles: `À approuver`, `Active`, `Suspendue`, `En cours`, `Échec`, `Projet inaccessible`.
|
||
- État vide: `Aucune tâche planifiée pour ce projet` puis `Créez une tâche pour automatiser une commande récurrente sans utiliser le cron du système.`
|
||
|
||
Création/édition:
|
||
- Action principale: `Nouvelle tâche planifiée`.
|
||
- Champs: `Nom`, `Commande`, `Dossier de travail` relatif au projet par défaut, `Planification` (presets `Toutes les heures`, `Chaque jour`, `Chaque semaine`, option `Personnalisée`), `Fuseau horaire`, `Limite de durée`, `Permissions et sandbox`, `Activée`.
|
||
- Avant activation: récapitulatif commande exacte, dossier, fréquence, prochaine exécution, permissions accordées.
|
||
- Modification de commande/périmètre/planification/permissions repasse par validation.
|
||
- Tâche créée par agent: état `À approuver`, ne s'exécute jamais silencieusement. Fiche: `Demandée par {agent}`, date et motif, commande/fréquence proposées, permissions nécessaires, actions `Approuver et activer`, `Modifier avant d’activer`, `Refuser`.
|
||
- Le tool MCP doit retourner cet état à l'agent, sans prétendre que la tâche est active.
|
||
|
||
Sécurité UX:
|
||
- Périmètre projet et profil sandbox toujours visibles.
|
||
- Accès hors projet, réseau ou commandes potentiellement destructrices signalés explicitement.
|
||
- Alerte: `Cette tâche pourra modifier ou supprimer des fichiers automatiquement.`
|
||
- Activation initiale d'une tâche demandée par agent exige confirmation utilisateur.
|
||
- Toute augmentation de permissions remet la tâche en état `À approuver`.
|
||
- `Exécuter maintenant` utilise exactement les permissions planifiées.
|
||
- Suppression active: confirmation `Supprimer définitivement cette tâche planifiée ?`.
|
||
- Si exécution en cours: choix séparés `Supprimer après l’exécution` et `Arrêter puis supprimer`; ne jamais interrompre implicitement.
|
||
|
||
Exécutions:
|
||
- Fiche: dernière/prochaine exécution, déclenchement `Planifié` ou `Manuel`, début, durée, code sortie, statut, stdout/stderr, permissions réellement utilisées, lien vers la tâche de fond correspondante.
|
||
- Succès silencieux.
|
||
- Échec: indicateur dans menu projet + notification discrète `Voir les détails`, sans avalanche de notifications répétées.
|
||
- IdeA doit indiquer que l'application doit fonctionner pour déclencher les tâches.
|
||
- Occurrences manquées pendant arrêt: pas de rattrapage en rafale; seule la prochaine occurrence future est programmée.
|
||
- Projet inaccessible: exécution `Ignorée — projet inaccessible`, sans lancer ailleurs.
|
||
|
||
## Cadrage Architect
|
||
Décision:
|
||
- Backend + frontend + docs.
|
||
- `BackgroundTask` est réutilisé uniquement pour les runs.
|
||
- Une tâche planifiée est un nouvel agrégat durable distinct: intention récurrente; chaque déclenchement crée une `BackgroundTask`.
|
||
|
||
Source de vérité:
|
||
- `<app_data_dir>/scheduled-project-tasks/<projectId>.json`.
|
||
- Ne pas utiliser `cron`, `systemd timer`, ni fichier auto-exécuté dans le repo.
|
||
- Machine-local et IdeA-owned pour éviter qu'un clone embarque une tâche destructrice.
|
||
|
||
Modèle domaine cible:
|
||
```ts
|
||
ScheduledProjectTask {
|
||
id: ScheduledTaskId
|
||
projectId: ProjectId
|
||
name: string
|
||
description?: string
|
||
enabled: boolean
|
||
command: { command: string; args: string[]; cwd?: string }
|
||
schedule: Interval | Daily | Weekly
|
||
timezone: string
|
||
nextRunAtMs: number | null
|
||
lastRunAtMs?: number
|
||
lastTaskId?: TaskId
|
||
createdBy: Human | Agent { agentId }
|
||
updatedBy: Human | Agent { agentId }
|
||
createdAtMs: number
|
||
updatedAtMs: number
|
||
auditRevision: number
|
||
}
|
||
```
|
||
|
||
V1 schedule borné:
|
||
- `interval { everyMinutes }`, minimum configurable, défaut min 5 ou 15 minutes.
|
||
- `daily { time: "HH:mm" }`.
|
||
- `weekly { days: [...], time: "HH:mm" }`.
|
||
- Pas de cron expression libre en V1.
|
||
|
||
Séparation schedule/run:
|
||
- `ScheduledProjectTask` = définition visible, éditable, activable/désactivable.
|
||
- `ScheduledTaskRun` optionnel = historique léger (`scheduleId`, `taskId`, `plannedAtMs`, `startedAtMs`, `status`).
|
||
- `BackgroundTask` = exécution concrète, sortie attachable, cancel/retry existants.
|
||
- Préférence architect: champ additif `origin: Scheduled { scheduledTaskId, plannedAtMs } | Manual` sur les runs/background tasks, plutôt que mélanger la nature et la provenance.
|
||
|
||
Ports/use cases:
|
||
- `ScheduledProjectTaskStore`: `list(projectId)`, `get(projectId, scheduleId)`, `save(task)`, `delete(projectId, scheduleId)`, `list_due(nowMs)`.
|
||
- `ScheduledProjectTaskAuditStore` ou audit via event store existant.
|
||
- `ProjectTaskScheduler`: `reconcile_project(projectId)`, `arm(scheduleId, nextRunAtMs)`, `cancel(scheduleId)`.
|
||
- Use cases: `ListScheduledProjectTasks`, `CreateScheduledProjectTask`, `UpdateScheduledProjectTask`, `DeleteScheduledProjectTask`, `RunScheduledProjectTaskNow`, `ReconcileScheduledProjectTasks`, `FireScheduledProjectTask`.
|
||
- `FireScheduledProjectTask`: calcule prochain run, persiste avant spawn, puis appelle `SpawnBackgroundCommand` avec `wakePolicy = RecordOnly` par défaut.
|
||
|
||
Scheduler/boot:
|
||
- Réutiliser `TokioScheduler` one-shot comme mécanisme de réveil, jamais comme source de vérité.
|
||
- Au boot: charger projets connus; pour chaque projet ouvert/restauré, `reconcile_project(projectId)`.
|
||
- V1: ne pas lancer de tâches pour projets fermés.
|
||
- Si `nextRunAtMs <= now`: politique `skipMissedAndScheduleNext`, pas de rafale.
|
||
- Si une run était `Running` avant crash: laisser le reconcile `BackgroundTask` existant marquer failed/lost handle.
|
||
|
||
Sécurité:
|
||
- `cwd` confiné au project root.
|
||
- Pas d'`env` libre V1, ou seulement références à variables existantes; jamais de secrets persistés.
|
||
- Commande/args visibles intégralement avant activation.
|
||
- Audit create/update/delete/enable/disable/runNow: actor, projectId, scheduleId, diff résumé, timestamp.
|
||
- Tools MCP création/modification/suppression = `WriteAction`, soumis aux permissions MCP natives.
|
||
- #305 fournit l'alternative visible au cron système; limiter les permissions shell/sandbox reste nécessaire pour empêcher un agent large de lancer `crontab` directement.
|
||
|
||
MCP tools à ajouter:
|
||
- `idea_scheduled_task_list` read-only
|
||
- `idea_scheduled_task_read` read-only
|
||
- `idea_scheduled_task_create` write-action
|
||
- `idea_scheduled_task_update` write-action
|
||
- `idea_scheduled_task_delete` write-action
|
||
- `idea_scheduled_task_run_now` write-action
|
||
- `idea_scheduled_task_set_enabled` write-action
|
||
- Project scope vient du serveur MCP du projet courant; l'agent ne choisit pas un autre projectId.
|
||
|
||
DTO/API Tauri + HTTP:
|
||
- `list_scheduled_project_tasks(projectId)`
|
||
- `create_scheduled_project_task(request)`
|
||
- `update_scheduled_project_task(request)`
|
||
- `delete_scheduled_project_task(projectId, scheduledTaskId)`
|
||
- `run_scheduled_project_task_now(projectId, scheduledTaskId)`
|
||
- `set_scheduled_project_task_enabled(projectId, scheduledTaskId, enabled)`
|
||
- DTO camelCase: `nextRunAtMs`, `lastRunAtMs`, `lastTaskId`, `createdBy`, `updatedBy`.
|
||
|
||
Frontend:
|
||
- Nouvelle surface projet/menu `Tâches planifiées`.
|
||
- `ScheduledTaskGateway` dans ports/adapters Tauri/HTTP/mock.
|
||
- Panneau liste + create/edit/delete/enable/run-now.
|
||
- Lien vers `BackgroundTask` du dernier run si `lastTaskId` existe.
|
||
- Ne pas fusionner avec WorkState: WorkState montre les runs; panneau planifié montre les définitions.
|
||
|
||
Docs:
|
||
- Mettre à jour la doc MCP/agent-facing pour `idea_scheduled_task_*`.
|
||
- `sdk/IdeaSDK/docs/services.md` si exposé aux plugins, sinon préciser que ce n'est pas un service plugin V1.
|
||
- `sdk/IdeaSDK/docs/manifest.md` seulement si contributions plugin touchent les schedules, déconseillé V1.
|
||
- Mentionner explicitement: ne pas utiliser cron système pour une maintenance projet IdeA.
|
||
|
||
Fichiers probables:
|
||
- Backend: `crates/domain/src/scheduled_task.rs`, `crates/domain/src/ports.rs`, `crates/application/src/scheduled_task/mod.rs`, `crates/application/src/background/mod.rs`, `crates/infrastructure/src/store/scheduled_task.rs`, `crates/infrastructure/src/scheduler/mod.rs`, `crates/backend/src/lib.rs`, `crates/app-tauri/src/commands.rs`, `crates/backend/src/dto.rs`, `crates/web-server/src/lib.rs`, `crates/infrastructure/src/orchestrator/mcp/tools.rs`.
|
||
- Frontend: `frontend/src/features/scheduled-tasks/*`, `frontend/src/ports/index.ts`, `frontend/src/adapters/scheduledTask.ts`, `frontend/src/adapters/http/requestResponseGateways.ts`, `frontend/src/adapters/mock/index.ts`, `frontend/src/features/projects/ProjectsView.tsx`, `frontend/src/features/web/webSurfaceCatalog.ts`.
|
||
|
||
## Critères QA
|
||
- Une tâche utilisateur et une tâche créée via MCP apparaissent dans la même liste.
|
||
- L'auteur, commande, fréquence, prochaine exécution et permissions sont identifiables.
|
||
- Tâche demandée par agent ne s'exécute pas avant approbation.
|
||
- Approuver/refuser/modifier/suspendre/reprendre/supprimer produisent les états attendus.
|
||
- Modification sensible redemande une approbation.
|
||
- Tâche suspendue ne s'exécute pas.
|
||
- Exécution manuelle et planifiée sont distinguables/auditables.
|
||
- Logs, durée, code de sortie et erreurs consultables.
|
||
- Projet inaccessible ne bloque pas IdeA et ne déplace jamais l'exécution ailleurs.
|
||
- Aucun rattrapage multiple après arrêt prolongé.
|
||
- Suppression pendant exécution ne l'interrompt pas sans choix explicite.
|
||
- Succès silencieux; échecs visibles sans notifications répétitives.
|
||
- Horaires respectent le fuseau configuré.
|
||
- Créer via MCP puis vérifier UI; créer/modifier/supprimer via UI puis vérifier MCP list/read.
|
||
- Échéance atteinte: `BackgroundTask` créée, visible WorkState, sortie attachable.
|
||
- Redémarrage avant échéance: reconcile rearme.
|
||
- Redémarrage après échéance manquée: pas de rafale, prochaine échéance recalculée, audit missed/skipped.
|
||
- Projet fermé: aucune exécution silencieuse.
|
||
- `cwd` hors projet rejeté.
|
||
- Tool MCP write refusé sans permission native.
|
||
- Delete/disable annule le timer armé.
|
||
- Deux reconciles concurrents ne créent pas deux runs pour la même échéance.
|
||
|
||
## Blocage — 2026-09-10
|
||
- Git a préparé `feature/305-scheduled-project-tasks-mcp`, mais a recommandé de finaliser #304 avant de démarrer #305.
|
||
- #304 est implémenté et QA vert, mais non committé/mergé: trois délégations Git pour le commit/merge #304 ont expiré à 600s malgré relance/rattachement.
|
||
- Le worktree courant porte encore les deux fichiers frontend #304 modifiés. Main ne doit pas démarrer #305 ni changer de branche tant que cette topologie n'est pas résolue, pour éviter de mélanger les changements.
|
||
- Prochaine reprise: débloquer Git ou faire exécuter par l'agent Git le commit/merge #304, puis checkout/rebase/reset de `feature/305-scheduled-project-tasks-mcp` par Git et lancer DevBackend #305.
|
||
|
||
## Validation QA — 2026-09-11
|
||
|
||
Verdict: VERT pour le périmètre #305 sur `feature/305-scheduled-project-tasks-mcp`.
|
||
|
||
Diff inspecté: nouvel agrégat durable `ScheduledProjectTask`, store machine-local `<app_data_dir>/scheduled-project-tasks/<projectId>.json`, service application de création/update/enable/delete/run/fire/reconcile, origine `BackgroundTaskOrigin::Scheduled`, tools MCP `idea_scheduled_task_*`, commandes Tauri/HTTP, surface frontend `Tâches planifiées`, adapters Tauri/HTTP/mock et doc `docs/scheduled-project-tasks.md`.
|
||
|
||
Preuves de commandes réelles:
|
||
|
||
```text
|
||
CARGO_HOME=/home/anthony/.local/share/app.idea.ide/managed-tmp/1d97033d-7b81-4f12-9b27-fe5a3842c5cc/data/cargo-home cargo test -p domain -- --nocapture
|
||
=> test result: ok. 311 passed; 0 failed; intégrations domain 11+14+2+12+11+49+6+72+16+21+16+5 passed; doc-tests 0.
|
||
|
||
CARGO_HOME=/home/anthony/.local/share/app.idea.ide/managed-tmp/1d97033d-7b81-4f12-9b27-fe5a3842c5cc/data/cargo-home cargo test -p application scheduled_task -- --nocapture
|
||
=> test result: ok. 3 passed; 0 failed; 185 filtered out.
|
||
|
||
CARGO_HOME=/home/anthony/.local/share/app.idea.ide/managed-tmp/1d97033d-7b81-4f12-9b27-fe5a3842c5cc/data/cargo-home cargo test -p infrastructure scheduled_task -- --nocapture
|
||
=> test result: ok. 1 passed; 0 failed; 380 filtered out.
|
||
|
||
CARGO_HOME=/home/anthony/.local/share/app.idea.ide/managed-tmp/1d97033d-7b81-4f12-9b27-fe5a3842c5cc/data/cargo-home cargo test -p infrastructure scheduled_create -- --nocapture
|
||
=> test result: ok. 1 passed; 0 failed; 380 filtered out.
|
||
|
||
CARGO_HOME=/home/anthony/.local/share/app.idea.ide/managed-tmp/1d97033d-7b81-4f12-9b27-fe5a3842c5cc/data/cargo-home cargo test -p infrastructure scheduled_read -- --nocapture
|
||
=> test result: ok. 1 passed; 0 failed; 380 filtered out.
|
||
|
||
CARGO_HOME=/home/anthony/.local/share/app.idea.ide/managed-tmp/1d97033d-7b81-4f12-9b27-fe5a3842c5cc/data/cargo-home cargo test -p backend scheduled_task -- --nocapture
|
||
=> test result: ok. 1 passed; 0 failed; 103 filtered out.
|
||
|
||
CARGO_HOME=/home/anthony/.local/share/app.idea.ide/managed-tmp/1d97033d-7b81-4f12-9b27-fe5a3842c5cc/data/cargo-home cargo test -p web-server scheduled_task -- --nocapture
|
||
=> test result: ok. 1 passed; 0 failed; 116 filtered out.
|
||
|
||
npm --prefix frontend test -- --run src/features/scheduled-tasks/scheduledTasks.test.tsx src/adapters/scheduledTask.test.ts src/adapters/http/scheduledTaskGateway.test.ts src/features/agents/CustomAgentChatView.test.tsx src/adapters/agent.test.ts src/adapters/http/agentGateway.test.ts
|
||
=> Test Files 6 passed (6); Tests 107 passed (107).
|
||
|
||
npm --prefix frontend run typecheck
|
||
=> tsc --noEmit passed.
|
||
|
||
npm --prefix frontend run build
|
||
=> tsc --noEmit && vite build passed; Vite emitted only the existing chunk-size warning.
|
||
```
|
||
|
||
Risques résiduels non bloquants:
|
||
- Pas de test live AppImage/desktop scheduler réel dans cette passe QA; validation limitée aux tests automatisés Rust/TS et inspection du câblage.
|
||
- Les critères UX exhaustifs comme notifications discrètes et parcours complet créer UI -> lire MCP / créer MCP -> lire UI sont couverts par contrat/adapters/tests unitaires ciblés, pas par un e2e navigateur/Tauri complet.
|
||
|
||
## Clôture Git/AppImage — 2026-09-11
|
||
- Feature commit: `7007b2c` sur `feature/305-scheduled-project-tasks-mcp`.
|
||
- Sync tickets: `686cf31`.
|
||
- Merge local develop: `600aae17`.
|
||
- Rebuild AppImage depuis `develop @ 600aae17` réussi.
|
||
- Artefact: `target/release/bundle/appimage/IdeA_0.3.0_amd64.AppImage`.
|
||
- Taille: `120560120` octets.
|
||
- SHA-256: `d7de4519736eee6b923ba5b9a6e966cea08760173bd85a66ae6422a60a95e0d1`.
|
||
- Commit dédié rebuild: `1e2cb5e`.
|
||
- Tag annoté: `v0.48.0`.
|
||
- Aucun push effectué.
|
||
- Restant manuel: remplacer l'AppImage installée puis relancer IdeA pour valider live le binaire v0.48.0.
|