- détachement: le panneau Paramètres du projet devient détachable (mapping historique panel=context → projectSettings dans parseViewWindowParams, DETACHABLE/ViewPanelBody alignés, ProjectsView ne court-circuitant plus le refus); Retry d'échec de détachement préservé
- couleurs projet: projectTheme publie idea:project-theme-changed localement + BroadcastChannel inter-webviews (fenêtres OS détachées), garde de révision par projet dans ProjectsView/ViewWindow contre les lectures settings en course — plus de flash/retour au bleu par défaut
- conversation custom CLI: transcript_restore_expected ne considère plus l'absence de scrollback volatile comme une restauration échouée quand la session rouvre en SessionPlan::None (continuation vide valide); CustomAgentChatView n'affiche plus le faux « conversation liée n'a pas pu être restaurée »
- images: conservation des images de conversation après changement de projet puis retour
- menus Panneaux: retrait des entrées Projects et Project Context (panelOrder WebWorkspace, création/switch via tab-bar +, contexte projet fusionné dans Paramètres du projet)
- QA verte: tests frontend ciblés window/ViewWindow/ProjectSettingsPanel/ProjectsView.detach/projects/CustomAgentChatView/WebDesktopWorkspace + cargo test -p application lifecycle transcript_restore_expected; fichiers .ideai/* (tickets 308-315, index/counter, agents/test, chat-drafts, system-permissions, project-settings.json, attachments/) et scripts/ hors périmètre préservés en worktree; aucun push
#305: nouvel agrégat durable ScheduledProjectTask dans domain (scheduled_task.rs, ids, ports ScheduledProjectTaskStore), source de vérité <app_data_dir>/scheduled-project-tasks/<projectId>.json, schedules bornés V1 interval/daily/weekly avec fuseau, cycle À approuver/Active/Suspendue avec approbation obligatoire des tâches demandées par agent; use cases application/scheduled_task (list/create/update/delete/run_now/reconcile/fire) réutilisant BackgroundTask pour les runs avec distinction planifié/manuel; store infrastructure dédié + scheduler reconcile au boot sans rattrapage en rafale, cwd confiné au projet; 7 tools MCP idea_scheduled_task_* (list/read lecture seule, create/update/delete/run_now/set_enabled write-actions soumises aux permissions natives); commandes Tauri + routes HTTP aux contrats DTO identiques (camelCase nextRunAtMs/lastRunAtMs/lastTaskId/createdBy/updatedBy); frontend: gateways Tauri/HTTP/mock + types ports/domain, surface projet 'Tâches planifiées' (liste utilisateur+agent unifiée, création/édition avec récapitulatif avant activation, approbation/refus, exécuter maintenant, suspendre/reprendre, suppression), catalogage surface web (webSurfaceCatalog/viewPlacement/ViewPanelBody/WebWorkspace); docs/scheduled-project-tasks.md et sdk docs/services.md (047c38b: pas un service plugin V1, pas d'émulation via cron système).
#314: correction de 'Conversation liée — transcript indisponible' après reboot: restauration du transcript lié côté application (orchestrator service, lifecycle, session_limit, background sink, terminal registry) et côté frontend (adapters agent/http frames/stream/requestResponse, workStateNormalization, CustomAgentChatView avec état et action utile), tests dto_chat/agent_lifecycle/structured_registry_d1/background_completion_sink étendus.
QA verte rapportée par QA sur feature/305-scheduled-project-tasks-mcp, sans bloquant, pour #305 et #314.
Hors périmètre worktree: tickets non liés, attachments/, scripts/; aucun push.
- #310: horodatage de tous les messages et événements de conversation visibles — composant partagé EntryTimestamp, timestamps propagés dans le transcript chat, les annonces inter-agent et le viewer de conversation, normalisation côté domain/adapters (conversation_log, paginate).
- #311: stop d'agent robuste pour PTY non monté, sessions headless et CLI custom — lifecycle/orchestrator durcis, ponts chat app-tauri/web-server alignés, arrêt fiable sans session orpheline.
- #312: préférence globale d'interface terminal par défaut — nouveau store settings (domain/application/infrastructure + tests), DTO et commandes backend, adapter frontend userSettings, panneau TerminalSettings dans les paramètres globaux.
- #313: Paramètres du projet — contexte de projet éditable et couleur de thème par projet (ProjectSettingsPanel + projectTheme), teinte des onglets projet, persistance via DTO projet.
QA verte (GO QA sur feature/batch-310-311-312-313-chat-stop-prefs-theme) — vérifications Git: git diff --cached --check OK; staging borné à crates/ et frontend/ (74 fichiers, +2105/-531), commit unique de lot conforme à la convention des lots (précédents batch-151/158/159, batch-210/211/212) car les fichiers partagés (dto.rs, commands.rs, CustomAgentChatView) entrelacent les 4 tickets.
fichiers .ideai/* (agents/test.md, chat-drafts.json, system-permissions.json, tickets/counter.json, tickets/index.json, tickets/308-313/), attachments/ et scripts/ hors périmètre et préservés en worktree ; aucun push.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
cause: la politique de lancement Codex divergeait selon le chemin de composition. compile_codex_launch_policy ajoutait les roots extra (temp géré) AVANT de calculer le mode de sandbox, et le builder PTY (append_codex_pty_managed_temp_roots) n'appliquait QUE ces roots temp, ignorant project root et politique compilée ; sans permissions explicites, codex_permission_writable_roots renvoyait une liste vide, donc le premier lancement TUI partait sans --add-dir project root et rejetait les roots additionnels (« Error adding directories: Ignoring --add-dir … because the effective permissions do not allow additional writable roots ») ; un --add-dir <project root> préexistant dans spec.args n'était pas réconcilié, d'où des comportements différents entre first-run, stop/relaunch, CLI custom et délégation headless — invisible sur le projet IdeA lui-même du fait de ses permissions déjà configurées.
fix: codex_permission_writable_roots retourne par défaut le project root quand aucune permission explicite n'est configurée (jamais vide si root non vide) ; compile_codex_launch_policy n'ajoute les roots extra qu'en mode workspace-write et reste vide en read-only (deny explicite ne gagne jamais project root ni temp géré) ; nouveau apply_codex_pty_launch_policy unique source de vérité PTY Codex — réécrit la valeur --sandbox existante ou l'ajoute, draine le --add-dir <project root> préexistant puis pose les roots compilés dédupliqués, composition identique pour first-run, relaunch, custom CLI et headless ; unconfigured_policy_writes_project_and_deduplicated_managed_temp, explicit_write_deny_never_gains_project_or_managed_temp_root, partial_allow_exposes_only_its_computed_rw_grant + tests lifecycle de réécriture d'args ajoutés.
QA verte: cargo targeted tests OK, npm targeted/typecheck OK — reconfirmée par Git: git diff --check OK, cargo fmt --all -- --check OK, cargo test -p application --lib codex_permissions 3/3, cargo test -p application --lib lifecycle 48/48.
fichiers .ideai/* (agents/test.md, chat-drafts.json, system-permissions.json, tickets/counter.json, tickets/index.json, tickets/308/, tickets/309/), attachments/ et scripts/ hors périmètre et préservés en worktree ; aucun push.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
cause: l'utilisateur ne pouvait pas retirer un projet du registre IdeA (projects.json) ; la seule issue laissait l'entrée fantôme dans la liste des projets, sans capacité générique de retrait.
domain: port ProjectStore::remove_project(id) -> Option<Project> avec implémentation par défaut « not supported » pour ne pas casser les stores spécialisés.
infrastructure: FsProjectStore implémente remove_project — retire l'entrée du registre persisté et la renvoie, None si id inconnu, ordre relatif du registre préservé ; test store dédié.
application: use case RemoveProjectFromRegistry (remove.rs) — NotFound explicite si id absent, retourne le projet retiré + registre rafraîchi.
backend/app-tauri/web-server: DTO RemoveProjectRequestDto/RemoveProjectResponseDto (removed, projects, restore_state, was_open), adapter partagé remove_project_for_adapter réutilisé par la commande Tauri remove_project et la route HTTP — même contrat desktop/web ; remove_project_and_deactivate désactive le projet s'il était ouvert, purge l'entrée open-projects restore correspondante et renvoie was_open/restore_state ; test adapter vérifiant le contrat de bout en bout (entrée retirée, restore purgé, root intact).
frontend: DTO + ProjectGateway.removeProject (HTTP + mock), ProjectsView expose un menu d'actions projet (ProjectActionsMenu) avec entrée Retirer, RemoveProjectDialog — confirmation explicite précisant que le dossier disque n'est pas supprimé, invalidation/rechargement de la liste des projets après succès (useProjects), tests ProjectsView/projects/adapters/httpInvoker/services.
QA verte: cargo targeted tests/check OK (domain, application project_usecases, infrastructure project_store, backend, app-tauri, web-server), npm targeted/typecheck/build OK ; suite npm test complète rouge non bloquante hors #308 (échecs préexistants listProjectSettings/web). Vérifications Git: git diff --check OK, cargo fmt --all -- --check OK.
fichiers .ideai/tickets/308/, tickets/counter.json, tickets/index.json, attachments/ et scripts/ hors périmètre et préservés en worktree (sync store traité séparément) ; aucun push.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Recalcule la policy structured Codex lors des reprises custom/headless afin que le project root et le managed tmp soient transmis en writable_roots. Fail-closed si une session Codex structured est lancée sans policy explicite.
Suppression du backfill agent→template au open_project (cause racine de la non-persistance des permissions MCP/system des templates) et couverture de régression : create/read/update des default permissions + test dédié open_project sans backfill.
AgentTemplate étendu (defaultAgentName, systemPermissions, mcpToolPermissions),
CreateTemplate/UpdateTemplate/CreateAgentFromTemplate projetés vers l'agent créé,
DTO/MCP tools et persistance store complétés. Correctif modèle par défaut:
UpdateTemplate ne persistait pas defaultProfileId (profil stale à la création
d'agent). Backfill best-effort des templates existants depuis les agents du
projet au open_project, overrides explicites uniquement.
ensure_structured_session reçoit désormais le conversation_id déjà résolu et le
transmet au plan de spawn au lieu de retomber sur le fallback User-B. WorkState
et la session lancée pointent ainsi sur le même fil. Test de régression
ask_cold_structured_agent_requester_launches_on_resolved_pair_thread.
Les grants Landlock RO/RW empêchaient la custom CLI de démarrer côté web
téléphone avant d'atteindre le project root. Les providers structurés/headless
gèrent leur propre sandbox compatible CLI.
Codex ne pouvait pas accéder aux métadonnées Git sous sandbox workspace-write.
À l'inverse, la factory relayait jusqu'ici le project_root entier en --add-dir,
soit un grant large non défendable.
Change pour n'accorder en écriture (Codex --add-dir ET SandboxPlan OS) que le
.git réellement résolu :
- resolve_git_dir gère .git/ directory ET fichier worktree 'gitdir: <path>'
(absolu ou relatif au parent du marker), puis canonicalize.
- enrich_sandbox_plan_with_codex_git_roots ajoute/déduplique les roots en RW
sur le SandboxPlan existant (préserve l'autorité EffectivePermissions).
- factory: défaut writable_roots = Vec::new() en l'absence de policy explicite.
Invariants conservés :
- project_root n'est jamais injecté comme writable root.
- aucun élargissement au parent commun (canonicalize + BTreeSet).
- permissions MCP/globales inchangées (seul le plan runtime est enrichi).
Tests (crates/application) :
- standard .git/ dir -> root unique.
- projet non-Git -> aucun root.
- worktree (.git file gitdir:) -> résolution + canonicalization.
- symlink -> dédup, pas de grant parent.
- factory: défaut sans policy n'émet plus --add-dir project_root.
Réserve QA: launch_conventionfile_injects_assigned_skills_in_order
(agent_lifecycle) arbitré hors-scope/non bloquant pour #195.
Cancel transitif, idempotent et ownership-aware sur le graphe parent→enfants des background tasks:
- domain/background_task + ports: modèle parent/enfant (parent_task_id, rejet du self-parent), index children_by_parent, traversal des descendants en largeur.
- application/background + infrastructure/store: cascade cancel (annule les descendants running/open, laisse les terminaux), idempotente face aux double-cancel et ordres de terminaison non déterministes; respect de l'ownership (parent_task_link).
- orchestrator/service: stop agent → cascade cancel des tasks lancées par cet agent (relatesTo #196); cancel task parent → cascade de ses sous-tasks (dependsOn #199). Refactor unifiant le chemin de cancel, sans sémantique #196/#199 regreffée (tests hérités verts).
- frontend: projection parentTaskId (workstate normalization/domain/services).
Tests QA verts: domain background_task, application background_task_cancel (cascades_background_task_subtree, cascades_to_running_child, cascades_to_open_descendants_and_leaves_terminal_child, parent_task_link_roundtrips_and_rejects_self_parent), orchestrator_service, workstate_actions, workstate.test.tsx.
Transforme l'annulation d'une background task en contrat immédiat, observable et non-best-effort:
- domain/background_task: états Cancelling/Cancelled/CancelFailed et transitions idempotentes; un kill PTY qui échoue est surfacé en CancelFailed plutôt qu'en succès silencieux.
- infrastructure/runner + store: cancel effectif de la task ciblée, reconcile marque Cancelling même sans live handle, et départage CancelFailed des tâches pending au boot.
- application/background + workstate + orchestrator/wake: chemin de cancel initié depuis le workstate, projection synchronisée de l'état (pas d'attente d'un cycle ambigu).
- frontend: projection workstate de l'état cancelled/cancelling/cancelFailed (cancellingRow/cancelFailedRow/cancelButtons), normalization et services.
Tests QA verts: background_task_cancel, background_task_runner cancel_wins_even_if_output_is_still_available, background_task_store reconcile_marks_cancelling_without_live_handle_cancel_failed_and_pending, workstate.test.tsx.
Solidifie le cycle stop/restart d'un agent visible en cellule:
- terminal/registry: kill effectif de la TUI native et de la CLI custom, arrêt idempotent du live runtime (PTY + structuré), distinction nette « agent stoppé » vs « pas de session ».
- workstate + lifecycle: projection d'un état runtime explicite « stopped » (AgentRuntimeState: running/stopping/stopped/failedStart), réconcilié avec le live state; traitement des courses « agent not found ».
- relance froide via freshStart: repart de zéro sans réattacher une session morte et recharge la configuration courante de l'agent (y compris changement de modèle).
- contrat projeté côté adapters frontend (ports/domain/normalization/mock) pour la consommation UI future (#197/#198), sans surface UI ici.
Tests QA verts: workstate_actions, orchestrator_service stop_agent, agent_lifecycle (reopen/fresh_start), workstate stopped runtime state, vitest adapter stop_live_agent.
Une demande idea_ask_agent vers UX échouait avec « finished without a structured Final response » alors qu'Architect répondait. Deux causes corrigées:
- session/process: un tour CLI terminé en code non nul SANS stdout (ex. erreur 401/auth) renvoyait une sortie vide au lieu du stderr réel; l'orchestrateur concluait alors à un « no Final ». On remonte désormais le stderr effectif (drain sync + async + helper process_exit_message + test dédié).
- orchestrator/service: la branche « ask waiting on background » masquait le premier Final structuré; la réponse synchrone redevient le premier Final (suppression de active_rendezvous_has_background_tasks / mark_rendezvous_task_waiting).
Nettoyage runtime corollaire (#204): arrêt du live runtime stale (PTY + structuré) d'un agent recréé afin que la délégation route vers l'agent courant (LiveSessions, stop_live_agent_runtime_in_project, wiring backend via DeleteAgent::with_runtime_cleanup, lifecycle, workstate cascade).
Le Stop du CustomAgentChatView n'annulait que le tour courant sans libérer la
session ni toucher aux tâches enfants ou appels MCP en cours. Il appelle
désormais stop_live_agent sur l'agent vivant, qui:
- ajoute le port StopAgentExecutionCascade (best-effort, idempotent), implémenté
par OrchestratorService pour annuler les idea_run_in_background rattachés au
rendez-vous actif (via BackgroundTaskRunner.cancel);
- cascade l'arrêt aux wait-dépendances (stop_turn_watch + preempt) pour
interrompre les appels MCP et les attentes inter-agents;
- libère et supprime la session structurée.
UI: nouvel état "Arrêt en cours..." (bouton disabled + loading) pendant l'arrêt,
libération du sessionId, label expliquant la cascade demandée. La Tauri command
cancel_agent_chat est alignée sur stop_live_agent + stop_turn_watch agent et
dépendances.
Tests: SpyBackgroundRunner (cascade n'annule que les tâches rattachées),
workstate_actions cascade best-effort, CustomAgentChatView stop+cascade feedback
et fallback NOT_FOUND.
Le collage d'une image (ex. capture d'écran) échouait en environnement desktop : le presse-papier image n'est souvent pas exposé comme un File synchrone dans clipboardData.files, mais uniquement via l'Async Clipboard API. On ajoute donc un repli sur navigator.clipboard.read() (MIME image/*) quand l'événement de collage ne fournit aucun fichier. Le preventDefault() n'est émis que lorsqu'on a effectivement lu des fichiers issus de l'événement, afin de ne pas bloquer le collage texte quand le repli async ne remonte rien.
- refactor clipboardFiles(event) -> dataTransferFiles(DataTransfer) réutilisable
- dedupeKey robuste pour les images sans nom (mime + taille + contenu base64)
- ReplyChunk::UserPrompt porte désormais les labels d'attachments (DTO + accounting reply_chunk_bytes, skip_serializing_if Vec::is_empty)
- tests ciblés frontend (+219) et backend (chat_bridge/dto/attachments) verts
QA : tests ciblés verts. Réserve : environnement headless, pas de validation manuelle desktop — validation AppImage à confirmer en suivi.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Permet à un plugin de contribuer des commandes slash adossées à une callback,
transitant par le registry/contrat unifié (#162) :
- sdk/IdeaSDK: manifeste contributes.slashCommands (déclaratif) — pointer bumpé.
- domain: source Plugin étendue + effet PluginCallback (la commande ne
s'exécute pas directement : elle renvoie une référence à la callback du plugin).
- application: registration des commandes plugin dans le registry + exécution
renvoyant l'effet PluginCallback ; métadonnées UI suffisantes pour l'autocomplete.
- backend + app-tauri: DTO + commandes Tauri listant/invoquant les commandes plugin.
- frontend (contrat): types adapters/domain/ports/mock pour pluginCallback et
l'invocation avec arguments.
L'exécution effective de la callback côté runtime UI est livrée par #166.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>