--- 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é: - `/scheduled-project-tasks/.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 `/scheduled-project-tasks/.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.