Cause: après un cancel manuel de tour, le ticket médié busy/FIFO n'était pas
retiré (l'agent restait Busy, bouton Stop persistant) et un terminal tardif du
tour annulé pouvait bloquer le tour suivant (Final non affiché).
Backend: StructuredSessions lie chaque génération de tour à son ticket médié
(bind_turn_accounting) et request_turn_cancel restitue l'identité (agent,
ticket) exacte observée avant annulation; CancelAgentChat.with_turn_accounting
retire le ticket exact (cancel_head + mark_idle) seulement s'il est toujours
le ticket courant — jamais un successeur déjà remplacé;
StructuredHumanTurnGuard expose accounting_identity et agent_send (app-tauri)
lie la comptabilité au démarrage du tour humain.
Frontend: CustomAgentChatView réconcilie l'interaction active depuis le
work-state (agent idle sans délégation -> interaction terminée, fallback
« Tour terminé sans réponse finale. ») au lieu de laisser un agentBusyChanged
sans tour-id clore un tour plus récent; les terminaux tardifs (final/error) du
tour read-model-settled remplacent le fallback; tour suivant isolé des
callbacks tardifs de la génération précédente.
Tests: cancel_agent_chat (retrait du ticket exact avant le prompt suivant,
nettoyage tardif sans idle sur ticket successeur), CustomAgentChatView
(isolation tour suivant, réconciliation work-state + remplacement fallback par
Final tardif).
Réserve non bloquante: alignement fixture skills préexistant porté par le
commit test dédié 83659d9a.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Cause: la logique de suivi du bas de fil était dupliquée et ad-hoc dans chaque surface (scrollRef, followsLatestRef, showLatestButton dans CustomAgentChatView), et dérivait dans certains cas — le fil ne suivait plus les nouveaux messages.
Fix: nouveau hook partagé shared/lib/useThreadScrollController (resté collé au bas quand l'utilisateur est déjà en bas, position préservée pendant la consultation de l'historique, notice latest sinon) câblé dans CustomAgentChatView, ConversationViewer et TicketAssistantPanel.
QA verte: npm run typecheck exit 0; tests ciblés useThreadScrollController, CustomAgentChatView, ConversationViewer/useConversationThread et tickets verts; npm test 1400/1400 verts (137 fichiers); git diff --check.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Cause: the monochrome-first treatment from #351 flattened the emoji
artwork through an alpha mask (transparent fill + zero-offset
text-shadow), dropping internal details (the ghost lost its tongue and
eyes), and the watermark box did not occupy the full space available
above the chat composer.
Fix: new .project-cell-illustration-area container (container-type:
size) wrapping the scroll zone so the absolute watermark spans the
whole region between the agent toolbar and the composer; glyph keeps
the OS full-colour emoji artwork as source and is neutralised with a
non-destructive CSS filter (grayscale(1) contrast(0.92)) so internal
tones and details stay visible in black and white; glyph sized to
min(100cqw - 8px, 100cqh - 12px); text-variation selector U+FE0E
stripping removed (full emoji preserved) and project-theme tint
plumbing (accessibleProjectAccent/onProjectThemeChanged) dropped as
superseded by grayscale; empty label and overlay z-layering untouched.
QA verte reconfirmée par Git: npm test -- CustomAgentChatView.test.tsx
projectIcons.test.tsx 96/96, npm run typecheck OK, npm run build OK,
npm test 1396/1396, git diff --check OK.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Custom CLI composer stays active during busy turns: submitting while the
agent works enqueues the message instead of sending it. Local FIFO queue
scoped per cell (projectId+agentId+layoutId+nodeId), entries carry id,
text, attachments snapshot and createdAtMs; single in-flight
transmission, entry removed at send time, auto-drain only on final
chunk, queue preserved on error/Stop with 'Relancer l'agent' action on
stopped cells. 'En attente · N' panel anchored above the composer:
order, 2-line preview, per-entry edit/delete with transient undo, clear
all with confirmation; main button switches Send / 'Mettre en attente'
with live announcement; layout contract updated (CLI visible while
busy, textarea enabled, Stop available). Chat navigation: round up-arrow
under the agent bar jumps to previous Final, round down-arrow above the
composer appears when not at bottom to jump to latest message,
auto-scroll suspended while reading history, prefers-reduced-motion and
44px a11y targets respected.
Ports unchanged (AgentGateway send/cancel/reattach, restartCellAgentRuntime).
QA verte reconfirmée par Git: targeted suites 115/115
(CustomAgentChatView/LayoutGrid/LayoutGrid.chat), git diff --check;
QA: npm run typecheck OK, npm run build OK.
Réserves documentées: queue renderer-local (non persistée après restart
complet), validation visuelle cellules très étroites restante.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Cause: the empty-state watermark filled only 92% of a centered box and
kept residual palette colors from host emoji fonts, non-compliant with
the monochrome-first project icon rule (#351 user capture).
Fix: illustration spans the cell with 6px/4px insets and an unclipped
glyph at min(88cqw, 88cqh); emoji painted as monochrome silhouettes via
transparent fill + zero-offset text-shadow tint
(--project-illustration-tint) derived from accessibleProjectAccent when
a project color exists, neutral muted fallback otherwise; empty label
anchored bottom-center and hidden first on constrained containers
(@container 280px width / 240px height); overlay priorities, composer
and z-layering untouched.
QA verte reconfirmée par Git: npm test -- CustomAgentChatView.test.tsx
80/80, git diff --check; QA: ProjectSettingsPanel 12/12, typecheck,
build OK (warning Vite chunk >500 kB préexistant).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Tabs (shared/ui): cible de drop matérialisée par écartement before/after pendant le drag/reorder, reset via rAF sans reliquat, clavier et annonces préservés.
- projectIcons: icônes projet en noir et blanc par défaut, colorisées avec la couleur du projet, occupant la cellule et visibles derrière les messages rendus légèrement transparents.
- Permissions plugin headless: PluginMcpServerSpec transporte l'allowlist exacte authorized_tools regroupée par (pluginId, serverId) depuis les PluginToolRef; projection Codex enabled_tools + default_tools_approval_mode="approve" — une permission déjà accordée au niveau projet/template ne déclenche pas de seconde approbation, un tool non assigné reste hors surface appelable; réserves orchestrator/mcp server+tools alignées, dto backend + dto_agents app-tauri.
- Skills: ref stable plugin:<pluginId>:<skillId> exposée dans le contexte agent, idea_skill_read(ref=...) canonique via read_assigned_by_ref, erreur structurée ambiguousSkillName avec candidates; SkillsPanel dédoublonné (android-build-install); agents-dev/L12-skills.md à jour.
- Docs: ARCHITECTURE.md section 22.3 (projection des autorisations agent vers les runtimes plugin).
- QA verte #350 reconfirmée avant commit.
#346 — capacités plugin SCM: le manifeste permissions.workspaceExec
(contrat SDK 8ff0f47) est appliqué côté hôte — domain PluginWorkspaceExec
Rule/Effect + audit sink, port WorkspaceToolRunner, adapter
LocalWorkspaceToolRunner sandboxé (concurrence 4, timeout, bornes
stdout/stderr, résolution bare executable sans shell), service
application + DTO backend + commandes app-tauri, gateway frontend
execTool exposée au runtime plugin — fin des lectures ad hoc du store
interne .git par les plugins.
#347 — onglets projet réordonnables par glisser-déposer (Tabs DnD,
onReorder, persistance de l'ordre via useProjects).
#348 — stop d'agent CLI custom: le message Final vert réapparaît au tour
suivant (override terminal stop armé correctement, reset de
l'interaction, refresh de la délégation requester).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Reprend à zéro la liste des icônes projet: catalogue d'emojis Unicode rendus
par la police système, sans embarquer d'assets graphiques propriétaires.
- domain: variante `emoji` ajoutée à ProjectIconSet (serde kebab-case)
- ports TS: union ProjectIconSet alignée avec "emoji"
- frontend: catalogue réécrit en 7 groupes (smileys, nature, food,
activities, travel, objects, symbols), registres légaux et métadonnées
supprimés, assets SVG projet retirés (angular.svg, NOTICE.md)
- watermark CLI custom: rendu emoji direct avec libellé de repli dérivé du
nom de dossier du cwd
- i18n FR/EN des groupes du picker et tests mis à jour (domain settings,
adapter projet, picker, panneau settings, chat custom)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Lot en un seul commit: les deux features partagent les mêmes contrats
(domain/settings.rs, DTO, commandes Tauri/HTTP, web-server) et les mêmes
surfaces UI — la séparation par fichiers était impossible sans chirurgie
de hunks risquant des états intermédiaires non compilables (convention
lot des batches #310-#313 et #315).
#325 — icône configurable par projet, watermark CLI custom:
- domaine: ProjectIconSet (devicon, simple-icons, lucide, idea),
ProjectIconRef validé (nom ASCII 1..=64, -_. internes), champ icon
optionnel sur ProjectSettings; sérialisation camelCase et try_from
strict
- application: UpdateProjectSettings.execute accepte un patch
theme_color OU icon (absent = inchangé, null = défaut produit), tests
in-dependants avec store mémoire
- backend/app-tauri/web-server: ProjectIconRefDto, UpdateProjectSettingsRequestDto
avec icon optionnel (désérialisation additive), commandes
update_project_settings alignées Tauri/HTTP, tests DTO et HTTP
(défaut null, set, clear, nom invalide rejeté)
- frontend: catalogue projectIcons (recherche EN), ProjectIconPicker en
grille multi-lignes dans Paramètres projet, affichage de l'icône dans
ProjectTabs et watermark grand format à faible opacité en fond de CLI
custom; ports ProjectSettings.icon, Partial<ProjectSettings>,
adapters tauri/mock/http et tests alignés
#326 — localisation FR/EN et traduction des textes IdeA:
- domaine: IdeaLanguage (fr | en), champ language optionnel sur
IdeaUserSettings (absence = défaut produit, série additive validée)
- backend/web-server: UserSettingsDto.language, update_user_settings
accepte language, tests HTTP alignés; fixtures structured_launch_d3
mises à jour
- frontend: i18n/ (catalogue FR/EN, I18nProvider, scan anti-régression
des sources visibles), GeneralSettings avec sélecteur de langue dans
les paramètres, setupI18n dans vitest; migration de l'ensemble des
textes hardcodés vers le catalogue sur toutes les surfaces (agents,
layout, projets, tickets, sprints, web, plugins, permissions, git,
templates, skills, mémoire, model servers, embedder, first-run,
devices, scheduled tasks, workstate, dialogs), FR conservé par défaut;
recherche d'icônes #325 en anglais conformément au ticket
QA verte sur feature/batch-325-326-project-icons-localisation. Aucun push.
Symptôme: l'historique du composer CLI custom (flèche haut) n'était
écrit dans localStorage qu'via un effet sur l'état, sans garantie au
moment de l'envoi — une fermeture brutale/crash perdait la dernière
entrée.
- writeComposerHistory retourne désormais le succès de l'écriture; à
chaque envoi, l'entrée est ajoutée et écrite AVANT l'invocation de
l'agent (source de vérité par ref + état, réinit de l'index de
navigation et du brouillon d'historique)
- l'effet d'écriture global est supprimé: l'historique n'est plus
réécrit en boucle, seulement aux envois (et lectures au changement de
scope projet/layout/node)
- si l'écriture échoue, l'envoi part quand même et un avertissement
discret « Historique des prompts non sauvegardé. » (role=status)
s'affiche au-dessus du composer
- tests: persistance vérifiée au moment de l'appel sendAgentChat,
envoi conservé + avertissement affiché quand localStorage échoue
QA verte sur fix/batch-322-323-session-restore-prompt-history. Aucun push.
- 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>
Backend : StructuredHumanTurnGuard pour partager la comptabilité busy/FIFO entre chat direct humain (agent_send, web chat.send) et asks inter-agents structurés, avec liveness mark_alive et retrait idempotent du tour. Web-server : tap d'événements sur chat.send pour le scrollback temps réel. Frontend : scoping des clés de progression par tour pour éviter la déduplication/ghosting des progress et outils entre appels.
L'erreur « La réponse du modèle n'a pas pu être finalisée. » indique désormais
l'adapter en cause (Session Codex / Claude / OpenCode / HTTP) pour lever
l'ambiguïté constatée sur GameTime entre session Codex et Claude.
- Attachments: projection canonique ChatPromptAttachmentDto (référence [file n]/[image n] par compteur de type, ordre d'import) insérée dans le prompt modèle et transportée par ReplyChunk::UserPrompt (attachmentRefs) ; rendu compact côté CustomAgentChatView.
- Resize: la zone chat (LeafView/LayoutGrid) est redimensionnable, taille actuelle conservée par défaut, préférence locale persistée.
- Tests: commands (projection des refs), dto_chat, chat_bridge, CustomAgentChatView, LayoutGrid.chat.
- Codex: les items agent_message sont désormais tapés en live comme progress (Message/Delta) dès item.completed, au lieu d'être bufferisés dans pending_final jusqu'à l'événement tool/MCP suivant.
- Corrélation stable progressId (codex:item:<id>) ajoutée sur ReplyProgress, propagée jusqu'au DTO (ReplyProgressDto.progressId) et au frontend ; le ProgressSettler et la vue procèdent par upsert running -> done au lieu d'empiler des lignes dupliquées.
- Tests: chat_bridge (settler par progressId), dto_chat, session codex, CustomAgentChatView (upsert par progressId).
Projette l'état d'interaction agent→projet sur la LED via des signaux explicites de la CLI custom:
- projectCliInteractionActivity: modèle d'activité d'interaction (InteractionStarted/Finished/Stopped, interactionId/seq, version).
- useProjectActivity + ProjectTabs: projection live « interaction en cours » → LED, retour immédiat à l'état attendu au stop ou à la fin réelle de l'échange (pas d'état collant).
- CustomAgentChatView: émet les signaux d'interaction CLI custom (début d'envoi, fin de tour, stop) consommés par l'activité projet — sans dériver la LED du texte streamé ou d'heuristiques.
Tout côté frontend: aucun signal runtime manquant (les signaux proviennent de la CLI custom).
Tests QA verts: projectActivity.test.ts, ProjectTabs.test.tsx (+ useProjectActivity.test.tsx, CustomAgentChatView.test.tsx).
Les progress « started » d'une même entité s'empilaient en plusieurs turns et
n'étaient dédupliqués qu'a posteriori par scan linéaire du tableau de turns,
d'où des doublons visibles et un affichage retardé (re-scan par chunk).
appendProgress opérait directement sur turns[] (chaque « started » y était poussé
puis retrouvé/remplacé par recherche séquentielle). Désormais:
- ChatViewState (turns + entityProgressMap): la Map indexée par « entité\0bucket »
(started/completed) est l'unique source de vérité pour les progress en cours;
- les progress « started » ne deviennent des turns qu'au flush
(completeChatViewProgress sur final/error), supprimant la duplication et
rendant l'affichage immédiat;
- « completed » merge depuis l'entrée running de la Map puis la retire, au lieu
de scanner le tableau;
- turnsIndicateBusy s'appuie sur la taille de la Map (état vivant) en plus du
scan des turns.
Tests: déduplication des progress par entité (started répétés → une seule entrée,
flush au final), fusion « completed » depuis la Map, état busy reflété par la
Map, et rendu non dupliqué sur l'enchaînement started/completed/final.
Corrige la perte de session à la navigation: le démontage du CustomAgentChatView
appelait close_agent_session (destructeur) au lieu de simplement détacher le
transport. Désormais:
- nouvelle commande Tauri detach_agent_chat (+ ChatBridge::detach) qui libère le
canal sans tuer la session ni le scrollback retenu; distinct de
close_agent_session qui reste le close destructeur explicite;
- le frontend appelle detachAgentChat à l'unmount puis restore sa vue depuis un
cache de session (turns + chunkCount) à la réattache;
- message « Reprise effectuée: N événements rattrapés… » quand la réattache
rejoue plus de chunks que le cache local n'en avait conservé.
Enrichit le rendu tools/MCP/progress:
- ProgressSettler (chat bridge) émet un progress completed synthétique quand un
spinner reste ouvert (nouvelle entité progress, chunk text/final/error): fini
les spinners fantômes;
- les adapters claude/codex/opencode/openai_compat extraient un aperçu du
payload (arguments/input/command/message/params) attaché au progress;
- bounded_reply_progress_preview (domain) garde les 4 premières lignes non
vides, normalisation CR/LF;
- previewText (frontend) multiligne whitespace-pre-wrap, source/nativeEvent
affichés sous le titre; suppression du flag settled et du Spinner inline
devenus redondants (l'état est porté par le stage + le settler).
Tests: detach_agent_chat gateway (Tauri + mock), ProgressSettler, bornage
reply_progress, item_preview/tool_preview par adapter, réattache avec cache et
notice de rattrapage.
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.